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