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