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