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