File: resolve_context.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (351 lines) | stat: -rw-r--r-- 14,762 bytes parent folder | download | duplicates (6)
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_DNS_RESOLVE_CONTEXT_H_
#define NET_DNS_RESOLVE_CONTEXT_H_

#include <memory>
#include <string>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/memory/safe_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/sample_vector.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/time/default_clock.h"
#include "base/time/default_tick_clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "net/base/isolation_info.h"
#include "net/base/net_export.h"
#include "net/base/network_handle.h"
#include "net/dns/dns_config.h"
#include "net/dns/public/secure_dns_mode.h"

namespace net {

class ClassicDnsServerIterator;
class DnsSession;
class DnsServerIterator;
class DohDnsServerIterator;
class HostCache;
class HostResolverCache;
class URLRequestContext;

// Represents various states of the DoH auto-upgrade process.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused. Update the corresponding enums.xml
// entry when making changes here.
enum class DohServerAutoupgradeStatus {
  kSuccessWithNoPriorFailures = 0,
  kSuccessWithSomePriorFailures = 1,
  kFailureWithSomePriorSuccesses = 2,
  kFailureWithNoPriorSuccesses = 3,

  kMaxValue = kFailureWithNoPriorSuccesses
};

// Per-URLRequestContext data used by HostResolver. Expected to be owned by the
// ContextHostResolver, and all usage/references are expected to be cleaned up
// or cancelled before the URLRequestContext goes out of service.
class NET_EXPORT_PRIVATE ResolveContext : public base::CheckedObserver {
 public:
  // Number of failures allowed before a DoH server is designated 'unavailable'.
  // In AUTOMATIC mode, non-probe DoH queries should not be sent to DoH servers
  // that have reached this limit.
  //
  // This limit is different from the failure limit that governs insecure async
  // resolver bypass in multiple ways: NXDOMAIN responses are never counted as
  // failures, and the outcome of fallback queries is not taken into account.
  static const int kAutomaticModeFailureLimit = 10;

  // The amount of time to wait after `StartDohAutoupgradeSuccessTimer()` is
  // called before `EmitDohAutoupgradeSuccessMetrics()` will be called to
  // possibly record the state of the DoH auto-upgrade process.
  static constexpr base::TimeDelta kDohAutoupgradeSuccessMetricTimeout =
      base::Minutes(1);

  class DohStatusObserver : public base::CheckedObserver {
   public:
    // Notification indicating that the current session for which DoH servers
    // are being tracked has changed.
    virtual void OnSessionChanged() = 0;

    // Notification indicating that a DoH server has been marked unavailable,
    // but is ready for usage such as availability probes.
    //
    // |network_change| true if the invalidation was triggered by a network
    // connection change.
    virtual void OnDohServerUnavailable(bool network_change) = 0;

   protected:
    DohStatusObserver() = default;
    ~DohStatusObserver() override = default;
  };

  ResolveContext(URLRequestContext* url_request_context,
                 bool enable_caching,
                 const base::Clock& clock = *base::DefaultClock::GetInstance(),
                 const base::TickClock& tick_clock =
                     *base::DefaultTickClock::GetInstance());

  ResolveContext(const ResolveContext&) = delete;
  ResolveContext& operator=(const ResolveContext&) = delete;

  ~ResolveContext() override;

  // Returns an iterator for DoH DNS servers.
  std::unique_ptr<DnsServerIterator> GetDohIterator(const DnsConfig& config,
                                                    const SecureDnsMode& mode,
                                                    const DnsSession* session);

  // Returns an iterator for classic DNS servers.
  std::unique_ptr<DnsServerIterator> GetClassicDnsIterator(
      const DnsConfig& config,
      const DnsSession* session);

  // Returns whether |doh_server_index| is eligible for use in AUTOMATIC mode,
  // that is that consecutive failures are less than kAutomaticModeFailureLimit
  // and the server has had at least one successful query or probe. Always
  // |false| if |session| is not the current session.
  bool GetDohServerAvailability(size_t doh_server_index,
                                const DnsSession* session) const;

  // Returns the number of DoH servers available for use in AUTOMATIC mode (see
  // GetDohServerAvailability()). Always 0 if |session| is not the current
  // session.
  size_t NumAvailableDohServers(const DnsSession* session) const;

  // Record failure to get a response from the server (e.g. SERVFAIL, connection
  // failures, or that the server failed to respond before the fallback period
  // elapsed. If |is_doh_server| and the number of failures has surpassed a
  // threshold, sets the DoH probe state to unavailable. Noop if |session| is
  // not the current session. Should only be called with with server failure
  // |rv|s, not e.g. OK, ERR_NAME_NOT_RESOLVED (which at the transaction level
  // is expected to be nxdomain), or ERR_IO_PENDING.
  void RecordServerFailure(size_t server_index,
                           bool is_doh_server,
                           int rv,
                           const DnsSession* session);

  // Record that server responded successfully. Noop if |session| is not the
  // current session.
  void RecordServerSuccess(size_t server_index,
                           bool is_doh_server,
                           const DnsSession* session);

  // Record how long it took to receive a response from the server. Noop if
  // |session| is not the current session.
  void RecordRtt(size_t server_index,
                 bool is_doh_server,
                 base::TimeDelta rtt,
                 int rv,
                 const DnsSession* session);

  // Return the period the next query should run before fallback to next
  // attempt. (Not actually a "timeout" because queries are not typically
  // cancelled as additional attempts are made.) |attempt| counts from 0 and is
  // used for exponential backoff.
  base::TimeDelta NextClassicFallbackPeriod(size_t classic_server_index,
                                            int attempt,
                                            const DnsSession* session);

  // Return the period the next DoH query should run before fallback to next
  // attempt.
  base::TimeDelta NextDohFallbackPeriod(size_t doh_server_index,
                                        const DnsSession* session);

  // Return a timeout for an insecure transaction (from Transaction::Start()).
  // Expected that the transaction will skip waiting for this timeout if it is
  // using fast timeouts, and also expected that transactions will always wait
  // for all attempts to run for at least their fallback period before dying
  // with timeout.
  base::TimeDelta ClassicTransactionTimeout(const DnsSession* session);

  // Return a timeout for a secure transaction (from Transaction::Start()).
  // Expected that the transaction will skip waiting for this timeout if it is
  // using fast timeouts, and also expected that transactions will always wait
  // for all attempts to run for at least their fallback period before dying
  // with timeout.
  base::TimeDelta SecureTransactionTimeout(SecureDnsMode secure_dns_mode,
                                           const DnsSession* session);

  void RegisterDohStatusObserver(DohStatusObserver* observer);
  void UnregisterDohStatusObserver(const DohStatusObserver* observer);

  URLRequestContext* url_request_context() { return url_request_context_; }
  const URLRequestContext* url_request_context() const {
    return url_request_context_;
  }
  void set_url_request_context(URLRequestContext* url_request_context) {
    DCHECK(!url_request_context_);
    DCHECK(url_request_context);
    url_request_context_ = url_request_context;
  }

  HostCache* host_cache() { return host_cache_.get(); }
  HostResolverCache* host_resolver_cache() {
    return host_resolver_cache_.get();
  }

  // Invalidate or clear saved per-context cached data that is not expected to
  // stay valid between connections or sessions (eg the HostCache and DNS server
  // stats). |new_session|, if non-null, will be the new "current" session for
  // which per-session data will be kept.
  void InvalidateCachesAndPerSessionData(const DnsSession* new_session,
                                         bool network_change);

  const DnsSession* current_session_for_testing() const {
    return current_session_.get();
  }

  void StartDohAutoupgradeSuccessTimer(const DnsSession* session);

  bool doh_autoupgrade_metrics_timer_is_running_for_testing() {
    return doh_autoupgrade_success_metric_timer_.IsRunning();
  }

  // Returns IsolationInfo that should be used for DoH requests. Using a single
  // transient IsolationInfo ensures that DNS requests aren't pooled with normal
  // web requests, but still allows them to be pooled with each other, to allow
  // reusing connections to the DoH server across different third party
  // contexts. One downside of a transient IsolationInfo is that it means
  // metadata about the DoH server itself will not be cached across restarts
  // (alternative service info if it supports QUIC, for instance).
  const IsolationInfo& isolation_info() const { return isolation_info_; }

  // Network to perform the DNS lookups for. When equal to
  // handles::kInvalidNetworkHandle the decision of which one to target is left
  // to the resolver. Virtual for testing.
  virtual handles::NetworkHandle GetTargetNetwork() const;

  base::SafeRef<ResolveContext> AsSafeRef() {
    return weak_ptr_factory_.GetSafeRef();
  }

  base::WeakPtr<ResolveContext> GetWeakPtr() {
    return weak_ptr_factory_.GetWeakPtr();
  }

 private:
  friend DohDnsServerIterator;
  friend ClassicDnsServerIterator;
  // Runtime statistics of DNS server.
  struct ServerStats {
    explicit ServerStats(std::unique_ptr<base::SampleVector> rtt_histogram);

    ServerStats(ServerStats&&);

    ~ServerStats();

    // Count of consecutive failures after last success.
    int last_failure_count = 0;

    // True if any success has ever been recorded for this server for the
    // current connection.
    bool current_connection_success = false;

    // Last time when server returned failure or exceeded fallback period. Reset
    // each time that a server returned success.
    base::TimeTicks last_failure;
    // Last time when server returned success.
    base::TimeTicks last_success;
    // Whether the server has ever returned failure. Used for per-provider
    // health metrics.
    bool has_failed_previously = false;

    // A histogram of observed RTT .
    std::unique_ptr<base::SampleVector> rtt_histogram;
  };

  // Return the (potentially rotating) index of the first configured server (to
  // be passed to [Doh]ServerIndexToUse()). Always returns 0 if |session| is not
  // the current session.
  size_t FirstServerIndex(bool doh_server, const DnsSession* session);

  bool IsCurrentSession(const DnsSession* session) const;

  // Returns the ServerStats for the designated server. Returns nullptr if no
  // ServerStats found.
  ServerStats* GetServerStats(size_t server_index, bool is_doh_server);

  // Return the fallback period for the next query.
  base::TimeDelta NextFallbackPeriodHelper(const ServerStats* server_stats,
                                           int attempt);

  template <typename Iterator>
  base::TimeDelta TransactionTimeoutHelper(Iterator server_stats_begin,
                                           Iterator server_stats_end);

  // Record the time to perform a query.
  void RecordRttForUma(size_t server_index,
                       bool is_doh_server,
                       base::TimeDelta rtt,
                       int rv,
                       base::TimeDelta base_fallback_period,
                       const DnsSession* session);
  std::string GetQueryTypeForUma(size_t server_index,
                                 bool is_doh_server,
                                 const DnsSession* session);
  std::string GetDohProviderIdForUma(size_t server_index,
                                     bool is_doh_server,
                                     const DnsSession* session);
  bool GetProviderUseExtraLogging(size_t server_index,
                                  bool is_doh_server,
                                  const DnsSession* session);

  void NotifyDohStatusObserversOfSessionChanged();
  void NotifyDohStatusObserversOfUnavailable(bool network_change);

  static bool ServerStatsToDohAvailability(const ServerStats& stats);

  // Emit histograms indicating the current state of all configured DoH
  // providers (for use in determining whether DoH auto-upgrade was successful).
  void EmitDohAutoupgradeSuccessMetrics();

  raw_ptr<URLRequestContext> url_request_context_;

  std::unique_ptr<HostCache> host_cache_;
  std::unique_ptr<HostResolverCache> host_resolver_cache_;

  // Current maximum server fallback period. Updated on connection change.
  base::TimeDelta max_fallback_period_;

  // All DohStatusObservers only hold a WeakPtr<ResolveContext>, so there's no
  // need for check_empty to be true.
  base::ObserverList<DohStatusObserver,
                     false /* check_empty */,
                     false /* allow_reentrancy */>
      doh_status_observers_;

  // Per-session data is only stored and valid for the latest session. Before
  // accessing, should check that |current_session_| is valid and matches a
  // passed in DnsSession.
  //
  // Using a WeakPtr, so even if a new session has the same pointer as an old
  // invalidated session, it can be recognized as a different session.
  //
  // TODO(crbug.com/40106440): Make const DnsSession once server stats have been
  // moved and no longer need to be read from DnsSession for availability logic.
  base::WeakPtr<const DnsSession> current_session_;
  // Current index into |config_.nameservers| to begin resolution with.
  int classic_server_index_ = 0;
  base::TimeDelta initial_fallback_period_;
  // Track runtime statistics of each classic (insecure) DNS server.
  std::vector<ServerStats> classic_server_stats_;
  // Track runtime statistics of each DoH server.
  std::vector<ServerStats> doh_server_stats_;

  const IsolationInfo isolation_info_;

  base::OneShotTimer doh_autoupgrade_success_metric_timer_;

  base::WeakPtrFactory<ResolveContext> weak_ptr_factory_{this};
};

}  // namespace net

#endif  // NET_DNS_RESOLVE_CONTEXT_H_