1 // Copyright 2016 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 #include "components/cronet/stale_host_resolver.h"
6
7 #include <memory>
8 #include <optional>
9 #include <set>
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include "base/check_op.h"
15 #include "base/functional/bind.h"
16 #include "base/functional/callback_helpers.h"
17 #include "base/notreached.h"
18 #include "base/timer/timer.h"
19 #include "base/values.h"
20 #include "net/base/host_port_pair.h"
21 #include "net/base/net_errors.h"
22 #include "net/base/network_anonymization_key.h"
23 #include "net/dns/context_host_resolver.h"
24 #include "net/dns/dns_util.h"
25 #include "net/dns/host_resolver.h"
26 #include "net/dns/public/host_resolver_results.h"
27 #include "net/dns/public/host_resolver_source.h"
28 #include "net/dns/public/resolve_error_info.h"
29 #include "net/log/net_log_with_source.h"
30 #include "url/scheme_host_port.h"
31
32 namespace cronet {
33
34 // A request made by the StaleHostResolver. May return fresh cached data,
35 // network data, or stale cached data.
36 class StaleHostResolver::RequestImpl
37 : public net::HostResolver::ResolveHostRequest {
38 public:
39 // StaleOptions will be read directly from |resolver|.
40 RequestImpl(base::WeakPtr<StaleHostResolver> resolver,
41 const net::HostPortPair& host,
42 const net::NetworkAnonymizationKey& network_anonymization_key,
43 const net::NetLogWithSource& net_log,
44 const ResolveHostParameters& input_parameters,
45 const base::TickClock* tick_clock);
46 ~RequestImpl() override = default;
47
48 // net::HostResolver::ResolveHostRequest implementation:
49 int Start(net::CompletionOnceCallback result_callback) override;
50 const net::AddressList* GetAddressResults() const override;
51 const net::HostResolverEndpointResults* GetEndpointResults() const override;
52 const std::vector<std::string>* GetTextResults() const override;
53 const std::vector<net::HostPortPair>* GetHostnameResults() const override;
54 const std::set<std::string>* GetDnsAliasResults() const override;
55 net::ResolveErrorInfo GetResolveErrorInfo() const override;
56 const std::optional<net::HostCache::EntryStaleness>& GetStaleInfo()
57 const override;
58 void ChangeRequestPriority(net::RequestPriority priority) override;
59
60 // Called on completion of an asynchronous (network) inner request. Expected
61 // to be called by StaleHostResolver::OnNetworkRequestComplete().
62 void OnNetworkRequestComplete(int error);
63
64 private:
have_network_request() const65 bool have_network_request() const { return network_request_ != nullptr; }
have_cache_data() const66 bool have_cache_data() const {
67 return cache_error_ != net::ERR_DNS_CACHE_MISS;
68 }
have_returned() const69 bool have_returned() const { return result_callback_.is_null(); }
70
71 // Determines if |cache_error_| and |cache_request_| represents a usable entry
72 // per the requirements of |resolver_->options_|.
73 bool CacheDataIsUsable() const;
74
75 // Callback for |stale_timer_| that returns stale results.
76 void OnStaleDelayElapsed();
77
78 base::WeakPtr<StaleHostResolver> resolver_;
79
80 const net::HostPortPair host_;
81 const net::NetworkAnonymizationKey network_anonymization_key_;
82 const net::NetLogWithSource net_log_;
83 const ResolveHostParameters input_parameters_;
84
85 // The callback passed into |Start()| to be called when the request returns.
86 net::CompletionOnceCallback result_callback_;
87
88 // The error from the stale cache entry, if there was one.
89 // If not, net::ERR_DNS_CACHE_MISS.
90 int cache_error_;
91 // Inner local-only/stale-allowed request.
92 std::unique_ptr<ResolveHostRequest> cache_request_;
93 // A timer that fires when the |Request| should return stale results, if the
94 // underlying network request has not finished yet.
95 base::OneShotTimer stale_timer_;
96
97 // An inner request for network results. Only set if |cache_request_| gave a
98 // stale or unusable result, and unset if the stale result is to be used as
99 // the overall result.
100 std::unique_ptr<ResolveHostRequest> network_request_;
101
102 base::WeakPtrFactory<RequestImpl> weak_ptr_factory_{this};
103 };
104
RequestImpl(base::WeakPtr<StaleHostResolver> resolver,const net::HostPortPair & host,const net::NetworkAnonymizationKey & network_anonymization_key,const net::NetLogWithSource & net_log,const ResolveHostParameters & input_parameters,const base::TickClock * tick_clock)105 StaleHostResolver::RequestImpl::RequestImpl(
106 base::WeakPtr<StaleHostResolver> resolver,
107 const net::HostPortPair& host,
108 const net::NetworkAnonymizationKey& network_anonymization_key,
109 const net::NetLogWithSource& net_log,
110 const ResolveHostParameters& input_parameters,
111 const base::TickClock* tick_clock)
112 : resolver_(std::move(resolver)),
113 host_(host),
114 network_anonymization_key_(network_anonymization_key),
115 net_log_(net_log),
116 input_parameters_(input_parameters),
117 cache_error_(net::ERR_DNS_CACHE_MISS),
118 stale_timer_(tick_clock) {
119 DCHECK(resolver_);
120 }
121
Start(net::CompletionOnceCallback result_callback)122 int StaleHostResolver::RequestImpl::Start(
123 net::CompletionOnceCallback result_callback) {
124 DCHECK(resolver_);
125 DCHECK(!result_callback.is_null());
126
127 net::HostResolver::ResolveHostParameters cache_parameters = input_parameters_;
128 cache_parameters.cache_usage =
129 net::HostResolver::ResolveHostParameters::CacheUsage::STALE_ALLOWED;
130 cache_parameters.source = net::HostResolverSource::LOCAL_ONLY;
131 cache_request_ = resolver_->inner_resolver_->CreateRequest(
132 host_, network_anonymization_key_, net_log_, cache_parameters);
133 int error =
134 cache_request_->Start(base::BindOnce([](int error) { NOTREACHED(); }));
135 DCHECK_NE(net::ERR_IO_PENDING, error);
136 cache_error_ = cache_request_->GetResolveErrorInfo().error;
137 DCHECK_NE(net::ERR_IO_PENDING, cache_error_);
138 // If it's a fresh cache hit (or literal), return it synchronously.
139 if (cache_error_ != net::ERR_DNS_CACHE_MISS &&
140 (!cache_request_->GetStaleInfo() ||
141 !cache_request_->GetStaleInfo().value().is_stale())) {
142 return cache_error_;
143 }
144
145 if (cache_error_ != net::ERR_DNS_CACHE_MISS &&
146 input_parameters_.cache_usage ==
147 net::HostResolver::ResolveHostParameters::CacheUsage::STALE_ALLOWED) {
148 return cache_error_;
149 }
150
151 result_callback_ = std::move(result_callback);
152
153 if (CacheDataIsUsable()) {
154 // |stale_timer_| is deleted when the Request is deleted, so it's safe to
155 // use Unretained here.
156 stale_timer_.Start(
157 FROM_HERE, resolver_->options_.delay,
158 base::BindOnce(&StaleHostResolver::RequestImpl::OnStaleDelayElapsed,
159 base::Unretained(this)));
160 } else {
161 cache_error_ = net::ERR_DNS_CACHE_MISS;
162 cache_request_.reset();
163 }
164
165 // Don't check the cache again.
166 net::HostResolver::ResolveHostParameters no_cache_parameters =
167 input_parameters_;
168 no_cache_parameters.cache_usage =
169 net::HostResolver::ResolveHostParameters::CacheUsage::DISALLOWED;
170 network_request_ = resolver_->inner_resolver_->CreateRequest(
171 host_, network_anonymization_key_, net_log_, no_cache_parameters);
172 int network_rv = network_request_->Start(
173 base::BindOnce(&StaleHostResolver::OnNetworkRequestComplete, resolver_,
174 network_request_.get(), weak_ptr_factory_.GetWeakPtr()));
175
176 // Network resolver has returned synchronously (for example by resolving from
177 // /etc/hosts).
178 if (network_rv != net::ERR_IO_PENDING) {
179 stale_timer_.Stop();
180 }
181 return network_rv;
182 }
183
GetAddressResults() const184 const net::AddressList* StaleHostResolver::RequestImpl::GetAddressResults()
185 const {
186 if (network_request_)
187 return network_request_->GetAddressResults();
188
189 DCHECK(cache_request_);
190 return cache_request_->GetAddressResults();
191 }
192
193 const net::HostResolverEndpointResults*
GetEndpointResults() const194 StaleHostResolver::RequestImpl::GetEndpointResults() const {
195 if (network_request_)
196 return network_request_->GetEndpointResults();
197
198 DCHECK(cache_request_);
199 return cache_request_->GetEndpointResults();
200 }
201
GetTextResults() const202 const std::vector<std::string>* StaleHostResolver::RequestImpl::GetTextResults()
203 const {
204 if (network_request_)
205 return network_request_->GetTextResults();
206
207 DCHECK(cache_request_);
208 return cache_request_->GetTextResults();
209 }
210
211 const std::vector<net::HostPortPair>*
GetHostnameResults() const212 StaleHostResolver::RequestImpl::GetHostnameResults() const {
213 if (network_request_)
214 return network_request_->GetHostnameResults();
215
216 DCHECK(cache_request_);
217 return cache_request_->GetHostnameResults();
218 }
219
220 const std::set<std::string>*
GetDnsAliasResults() const221 StaleHostResolver::RequestImpl::GetDnsAliasResults() const {
222 if (network_request_)
223 return network_request_->GetDnsAliasResults();
224
225 DCHECK(cache_request_);
226 return cache_request_->GetDnsAliasResults();
227 }
228
GetResolveErrorInfo() const229 net::ResolveErrorInfo StaleHostResolver::RequestImpl::GetResolveErrorInfo()
230 const {
231 if (network_request_)
232 return network_request_->GetResolveErrorInfo();
233 DCHECK(cache_request_);
234 return cache_request_->GetResolveErrorInfo();
235 }
236
237 const std::optional<net::HostCache::EntryStaleness>&
GetStaleInfo() const238 StaleHostResolver::RequestImpl::GetStaleInfo() const {
239 if (network_request_)
240 return network_request_->GetStaleInfo();
241
242 DCHECK(cache_request_);
243 return cache_request_->GetStaleInfo();
244 }
245
ChangeRequestPriority(net::RequestPriority priority)246 void StaleHostResolver::RequestImpl::ChangeRequestPriority(
247 net::RequestPriority priority) {
248 if (network_request_) {
249 network_request_->ChangeRequestPriority(priority);
250 } else {
251 DCHECK(cache_request_);
252 cache_request_->ChangeRequestPriority(priority);
253 }
254 }
255
OnNetworkRequestComplete(int error)256 void StaleHostResolver::RequestImpl::OnNetworkRequestComplete(int error) {
257 DCHECK(resolver_);
258 DCHECK(have_network_request());
259 DCHECK(!have_returned());
260
261 bool return_stale_data_instead_of_network_name_not_resolved =
262 resolver_->options_.use_stale_on_name_not_resolved &&
263 error == net::ERR_NAME_NOT_RESOLVED && have_cache_data();
264
265 stale_timer_.Stop();
266
267 if (return_stale_data_instead_of_network_name_not_resolved) {
268 network_request_.reset();
269 std::move(result_callback_).Run(cache_error_);
270 } else {
271 cache_request_.reset();
272 std::move(result_callback_).Run(error);
273 }
274 }
275
CacheDataIsUsable() const276 bool StaleHostResolver::RequestImpl::CacheDataIsUsable() const {
277 DCHECK(resolver_);
278 DCHECK(cache_request_);
279
280 if (cache_error_ != net::OK)
281 return false;
282
283 DCHECK(cache_request_->GetStaleInfo());
284 const net::HostCache::EntryStaleness& staleness =
285 cache_request_->GetStaleInfo().value();
286
287 if (resolver_->options_.max_expired_time != base::TimeDelta() &&
288 staleness.expired_by > resolver_->options_.max_expired_time) {
289 return false;
290 }
291 if (resolver_->options_.max_stale_uses > 0 &&
292 staleness.stale_hits > resolver_->options_.max_stale_uses) {
293 return false;
294 }
295 if (!resolver_->options_.allow_other_network &&
296 staleness.network_changes > 0) {
297 return false;
298 }
299 return true;
300 }
301
OnStaleDelayElapsed()302 void StaleHostResolver::RequestImpl::OnStaleDelayElapsed() {
303 DCHECK(!have_returned());
304 DCHECK(have_cache_data());
305 DCHECK(have_network_request());
306
307 // If resolver is destroyed after starting a request, the request is
308 // considered cancelled and callbacks must not be invoked. Logging the
309 // cancellation will happen on destruction of |this|.
310 if (!resolver_) {
311 network_request_.reset();
312 return;
313 }
314 DCHECK(CacheDataIsUsable());
315
316 // Detach |network_request_| to allow it to complete and backfill the cache
317 // even if |this| is destroyed.
318 resolver_->DetachRequest(std::move(network_request_));
319
320 std::move(result_callback_).Run(cache_error_);
321 }
322
StaleOptions()323 StaleHostResolver::StaleOptions::StaleOptions()
324 : allow_other_network(false),
325 max_stale_uses(0),
326 use_stale_on_name_not_resolved(false) {}
327
StaleHostResolver(std::unique_ptr<net::ContextHostResolver> inner_resolver,const StaleOptions & stale_options)328 StaleHostResolver::StaleHostResolver(
329 std::unique_ptr<net::ContextHostResolver> inner_resolver,
330 const StaleOptions& stale_options)
331 : inner_resolver_(std::move(inner_resolver)), options_(stale_options) {
332 DCHECK_LE(0, stale_options.max_expired_time.InMicroseconds());
333 DCHECK_LE(0, stale_options.max_stale_uses);
334 }
335
~StaleHostResolver()336 StaleHostResolver::~StaleHostResolver() {}
337
OnShutdown()338 void StaleHostResolver::OnShutdown() {
339 inner_resolver_->OnShutdown();
340 }
341
342 std::unique_ptr<net::HostResolver::ResolveHostRequest>
CreateRequest(url::SchemeHostPort host,net::NetworkAnonymizationKey network_anonymization_key,net::NetLogWithSource net_log,std::optional<ResolveHostParameters> optional_parameters)343 StaleHostResolver::CreateRequest(
344 url::SchemeHostPort host,
345 net::NetworkAnonymizationKey network_anonymization_key,
346 net::NetLogWithSource net_log,
347 std::optional<ResolveHostParameters> optional_parameters) {
348 // TODO(crbug.com/40181080): Propagate scheme.
349 return CreateRequest(net::HostPortPair::FromSchemeHostPort(host),
350 network_anonymization_key, net_log, optional_parameters);
351 }
352
353 std::unique_ptr<net::HostResolver::ResolveHostRequest>
CreateRequest(const net::HostPortPair & host,const net::NetworkAnonymizationKey & network_anonymization_key,const net::NetLogWithSource & net_log,const std::optional<ResolveHostParameters> & optional_parameters)354 StaleHostResolver::CreateRequest(
355 const net::HostPortPair& host,
356 const net::NetworkAnonymizationKey& network_anonymization_key,
357 const net::NetLogWithSource& net_log,
358 const std::optional<ResolveHostParameters>& optional_parameters) {
359 DCHECK(tick_clock_);
360 return std::make_unique<RequestImpl>(
361 weak_ptr_factory_.GetWeakPtr(), host, network_anonymization_key, net_log,
362 optional_parameters.value_or(ResolveHostParameters()), tick_clock_);
363 }
364
365 std::unique_ptr<net::HostResolver::ServiceEndpointRequest>
CreateServiceEndpointRequest(Host host,net::NetworkAnonymizationKey network_anonymization_key,net::NetLogWithSource net_log,ResolveHostParameters parameters)366 StaleHostResolver::CreateServiceEndpointRequest(
367 Host host,
368 net::NetworkAnonymizationKey network_anonymization_key,
369 net::NetLogWithSource net_log,
370 ResolveHostParameters parameters) {
371 // TODO(crbug.com/335119455): Figure out a plan to support the
372 // ServiceEndpointRequest API.
373 NOTIMPLEMENTED();
374 return nullptr;
375 }
376
GetHostCache()377 net::HostCache* StaleHostResolver::GetHostCache() {
378 return inner_resolver_->GetHostCache();
379 }
380
GetDnsConfigAsValue() const381 base::Value::Dict StaleHostResolver::GetDnsConfigAsValue() const {
382 return inner_resolver_->GetDnsConfigAsValue();
383 }
384
SetRequestContext(net::URLRequestContext * request_context)385 void StaleHostResolver::SetRequestContext(
386 net::URLRequestContext* request_context) {
387 inner_resolver_->SetRequestContext(request_context);
388 }
389
OnNetworkRequestComplete(ResolveHostRequest * network_request,base::WeakPtr<RequestImpl> stale_request,int error)390 void StaleHostResolver::OnNetworkRequestComplete(
391 ResolveHostRequest* network_request,
392 base::WeakPtr<RequestImpl> stale_request,
393 int error) {
394 if (detached_requests_.erase(network_request))
395 return;
396
397 // If not a detached request, there should still be an owning RequestImpl.
398 // Otherwise the request should have been cancelled and this method never
399 // called.
400 DCHECK(stale_request);
401
402 stale_request->OnNetworkRequestComplete(error);
403 }
404
DetachRequest(std::unique_ptr<ResolveHostRequest> request)405 void StaleHostResolver::DetachRequest(
406 std::unique_ptr<ResolveHostRequest> request) {
407 DCHECK_EQ(0u, detached_requests_.count(request.get()));
408 detached_requests_[request.get()] = std::move(request);
409 }
410
SetTickClockForTesting(const base::TickClock * tick_clock)411 void StaleHostResolver::SetTickClockForTesting(
412 const base::TickClock* tick_clock) {
413 tick_clock_ = tick_clock;
414 inner_resolver_->SetTickClockForTesting(tick_clock);
415 }
416
417 } // namespace cronet
418