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