1 // Copyright 2012 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 "net/dns/host_resolver.h"
6
7 #include <optional>
8 #include <set>
9 #include <string>
10 #include <string_view>
11 #include <utility>
12 #include <vector>
13
14 #include "base/check.h"
15 #include "base/functional/bind.h"
16 #include "base/no_destructor.h"
17 #include "base/notreached.h"
18 #include "base/ranges/algorithm.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/time/time_delta_from_string.h"
21 #include "base/values.h"
22 #include "net/base/address_list.h"
23 #include "net/base/features.h"
24 #include "net/base/host_port_pair.h"
25 #include "net/base/net_errors.h"
26 #include "net/base/network_change_notifier.h"
27 #include "net/dns/context_host_resolver.h"
28 #include "net/dns/dns_client.h"
29 #include "net/dns/dns_util.h"
30 #include "net/dns/host_cache.h"
31 #include "net/dns/host_resolver_manager.h"
32 #include "net/dns/mapped_host_resolver.h"
33 #include "net/dns/public/host_resolver_results.h"
34 #include "net/dns/resolve_context.h"
35 #include "third_party/abseil-cpp/absl/types/variant.h"
36 #include "url/scheme_host_port.h"
37
38 #if BUILDFLAG(IS_ANDROID)
39 #include "base/android/build_info.h"
40 #include "net/android/network_library.h"
41 #endif // BUILDFLAG(IS_ANDROID)
42
43 namespace net {
44
45 namespace {
46
47 // The experiment settings of features::kUseDnsHttpsSvcb. See the comments in
48 // net/base/features.h for more details.
49 const char kUseDnsHttpsSvcbEnable[] = "enable";
50 const char kUseDnsHttpsSvcbInsecureExtraTimeMax[] = "insecure_extra_time_max";
51 const char kUseDnsHttpsSvcbInsecureExtraTimePercent[] =
52 "insecure_extra_time_percent";
53 const char kUseDnsHttpsSvcbInsecureExtraTimeMin[] = "insecure_extra_time_min";
54 const char kUseDnsHttpsSvcbSecureExtraTimeMax[] = "secure_extra_time_max";
55 const char kUseDnsHttpsSvcbSecureExtraTimePercent[] =
56 "secure_extra_time_percent";
57 const char kUseDnsHttpsSvcbSecureExtraTimeMin[] = "secure_extra_time_min";
58
59 class FailingRequestImpl : public HostResolver::ResolveHostRequest,
60 public HostResolver::ProbeRequest {
61 public:
FailingRequestImpl(int error)62 explicit FailingRequestImpl(int error) : error_(error) {}
63
64 FailingRequestImpl(const FailingRequestImpl&) = delete;
65 FailingRequestImpl& operator=(const FailingRequestImpl&) = delete;
66
67 ~FailingRequestImpl() override = default;
68
Start(CompletionOnceCallback callback)69 int Start(CompletionOnceCallback callback) override { return error_; }
Start()70 int Start() override { return error_; }
71
GetAddressResults() const72 AddressList* GetAddressResults() const override { return nullptr; }
73
GetEndpointResults() const74 std::vector<HostResolverEndpointResult>* GetEndpointResults() const override {
75 return nullptr;
76 }
77
GetTextResults() const78 const std::vector<std::string>* GetTextResults() const override {
79 return nullptr;
80 }
81
GetHostnameResults() const82 const std::vector<HostPortPair>* GetHostnameResults() const override {
83 return nullptr;
84 }
85
GetDnsAliasResults() const86 const std::set<std::string>* GetDnsAliasResults() const override {
87 return nullptr;
88 }
89
GetResolveErrorInfo() const90 ResolveErrorInfo GetResolveErrorInfo() const override {
91 return ResolveErrorInfo(error_);
92 }
93
GetStaleInfo() const94 const std::optional<HostCache::EntryStaleness>& GetStaleInfo()
95 const override {
96 static const std::optional<HostCache::EntryStaleness> nullopt_result;
97 return nullopt_result;
98 }
99
100 private:
101 const int error_;
102 };
103
EndpointResultIsNonProtocol(const HostResolverEndpointResult & result)104 bool EndpointResultIsNonProtocol(const HostResolverEndpointResult& result) {
105 return result.metadata.supported_protocol_alpns.empty();
106 }
107
GetTimeDeltaFromDictString(const base::Value::Dict & args,std::string_view key,base::TimeDelta * out)108 void GetTimeDeltaFromDictString(const base::Value::Dict& args,
109 std::string_view key,
110 base::TimeDelta* out) {
111 const std::string* value_string = args.FindString(key);
112 if (!value_string)
113 return;
114 *out = base::TimeDeltaFromString(*value_string).value_or(*out);
115 }
116
117 } // namespace
118
Host(absl::variant<url::SchemeHostPort,HostPortPair> host)119 HostResolver::Host::Host(absl::variant<url::SchemeHostPort, HostPortPair> host)
120 : host_(std::move(host)) {
121 #if DCHECK_IS_ON()
122 if (absl::holds_alternative<url::SchemeHostPort>(host_)) {
123 DCHECK(absl::get<url::SchemeHostPort>(host_).IsValid());
124 } else {
125 DCHECK(absl::holds_alternative<HostPortPair>(host_));
126 DCHECK(!absl::get<HostPortPair>(host_).IsEmpty());
127 }
128 #endif // DCHECK_IS_ON()
129 }
130
131 HostResolver::Host::~Host() = default;
132
133 HostResolver::Host::Host(const Host&) = default;
134
135 HostResolver::Host& HostResolver::Host::operator=(const Host&) = default;
136
137 HostResolver::Host::Host(Host&&) = default;
138
139 HostResolver::Host& HostResolver::Host::operator=(Host&&) = default;
140
HasScheme() const141 bool HostResolver::Host::HasScheme() const {
142 return absl::holds_alternative<url::SchemeHostPort>(host_);
143 }
144
GetScheme() const145 const std::string& HostResolver::Host::GetScheme() const {
146 DCHECK(absl::holds_alternative<url::SchemeHostPort>(host_));
147 return absl::get<url::SchemeHostPort>(host_).scheme();
148 }
149
GetHostname() const150 std::string HostResolver::Host::GetHostname() const {
151 if (absl::holds_alternative<url::SchemeHostPort>(host_)) {
152 return absl::get<url::SchemeHostPort>(host_).host();
153 } else {
154 DCHECK(absl::holds_alternative<HostPortPair>(host_));
155 return absl::get<HostPortPair>(host_).HostForURL();
156 }
157 }
158
GetHostnameWithoutBrackets() const159 std::string_view HostResolver::Host::GetHostnameWithoutBrackets() const {
160 if (absl::holds_alternative<url::SchemeHostPort>(host_)) {
161 std::string_view hostname = absl::get<url::SchemeHostPort>(host_).host();
162 if (hostname.size() > 2 && hostname.front() == '[' &&
163 hostname.back() == ']') {
164 return hostname.substr(1, hostname.size() - 2);
165 } else {
166 return hostname;
167 }
168 } else {
169 DCHECK(absl::holds_alternative<HostPortPair>(host_));
170 return absl::get<HostPortPair>(host_).host();
171 }
172 }
173
GetPort() const174 uint16_t HostResolver::Host::GetPort() const {
175 if (absl::holds_alternative<url::SchemeHostPort>(host_)) {
176 return absl::get<url::SchemeHostPort>(host_).port();
177 } else {
178 DCHECK(absl::holds_alternative<HostPortPair>(host_));
179 return absl::get<HostPortPair>(host_).port();
180 }
181 }
182
ToString() const183 std::string HostResolver::Host::ToString() const {
184 if (absl::holds_alternative<url::SchemeHostPort>(host_)) {
185 return absl::get<url::SchemeHostPort>(host_).Serialize();
186 } else {
187 DCHECK(absl::holds_alternative<HostPortPair>(host_));
188 return absl::get<HostPortPair>(host_).ToString();
189 }
190 }
191
AsSchemeHostPort() const192 const url::SchemeHostPort& HostResolver::Host::AsSchemeHostPort() const {
193 const url::SchemeHostPort* scheme_host_port =
194 absl::get_if<url::SchemeHostPort>(&host_);
195 DCHECK(scheme_host_port);
196 return *scheme_host_port;
197 }
198
199 HostResolver::HttpsSvcbOptions::HttpsSvcbOptions() = default;
200
201 HostResolver::HttpsSvcbOptions::HttpsSvcbOptions(
202 const HttpsSvcbOptions& other) = default;
203 HostResolver::HttpsSvcbOptions::HttpsSvcbOptions(HttpsSvcbOptions&& other) =
204 default;
205
206 HostResolver::HttpsSvcbOptions::~HttpsSvcbOptions() = default;
207
208 // static
FromDict(const base::Value::Dict & dict)209 HostResolver::HttpsSvcbOptions HostResolver::HttpsSvcbOptions::FromDict(
210 const base::Value::Dict& dict) {
211 net::HostResolver::HttpsSvcbOptions options;
212 options.enable =
213 dict.FindBool(kUseDnsHttpsSvcbEnable).value_or(options.enable);
214 GetTimeDeltaFromDictString(dict, kUseDnsHttpsSvcbInsecureExtraTimeMax,
215 &options.insecure_extra_time_max);
216
217 options.insecure_extra_time_percent =
218 dict.FindInt(kUseDnsHttpsSvcbInsecureExtraTimePercent)
219 .value_or(options.insecure_extra_time_percent);
220 GetTimeDeltaFromDictString(dict, kUseDnsHttpsSvcbInsecureExtraTimeMin,
221 &options.insecure_extra_time_min);
222
223 GetTimeDeltaFromDictString(dict, kUseDnsHttpsSvcbSecureExtraTimeMax,
224 &options.secure_extra_time_max);
225
226 options.secure_extra_time_percent =
227 dict.FindInt(kUseDnsHttpsSvcbSecureExtraTimePercent)
228 .value_or(options.secure_extra_time_percent);
229 GetTimeDeltaFromDictString(dict, kUseDnsHttpsSvcbSecureExtraTimeMin,
230 &options.secure_extra_time_min);
231
232 return options;
233 }
234
235 // static
FromFeatures()236 HostResolver::HttpsSvcbOptions HostResolver::HttpsSvcbOptions::FromFeatures() {
237 net::HostResolver::HttpsSvcbOptions options;
238 options.enable = base::FeatureList::IsEnabled(features::kUseDnsHttpsSvcb);
239 options.insecure_extra_time_max =
240 features::kUseDnsHttpsSvcbInsecureExtraTimeMax.Get();
241 options.insecure_extra_time_percent =
242 features::kUseDnsHttpsSvcbInsecureExtraTimePercent.Get();
243 options.insecure_extra_time_min =
244 features::kUseDnsHttpsSvcbInsecureExtraTimeMin.Get();
245 options.secure_extra_time_max =
246 features::kUseDnsHttpsSvcbSecureExtraTimeMax.Get();
247 options.secure_extra_time_percent =
248 features::kUseDnsHttpsSvcbSecureExtraTimePercent.Get();
249 options.secure_extra_time_min =
250 features::kUseDnsHttpsSvcbSecureExtraTimeMin.Get();
251 return options;
252 }
253
254 HostResolver::ManagerOptions::ManagerOptions() = default;
255
256 HostResolver::ManagerOptions::ManagerOptions(const ManagerOptions& other) =
257 default;
258 HostResolver::ManagerOptions::ManagerOptions(ManagerOptions&& other) = default;
259
260 HostResolver::ManagerOptions::~ManagerOptions() = default;
261
262 const std::vector<bool>*
GetExperimentalResultsForTesting() const263 HostResolver::ResolveHostRequest::GetExperimentalResultsForTesting() const {
264 NOTREACHED();
265 return nullptr;
266 }
267
CreateResolver(HostResolverManager * manager,std::string_view host_mapping_rules,bool enable_caching)268 std::unique_ptr<HostResolver> HostResolver::Factory::CreateResolver(
269 HostResolverManager* manager,
270 std::string_view host_mapping_rules,
271 bool enable_caching) {
272 return HostResolver::CreateResolver(manager, host_mapping_rules,
273 enable_caching);
274 }
275
CreateStandaloneResolver(NetLog * net_log,const ManagerOptions & options,std::string_view host_mapping_rules,bool enable_caching)276 std::unique_ptr<HostResolver> HostResolver::Factory::CreateStandaloneResolver(
277 NetLog* net_log,
278 const ManagerOptions& options,
279 std::string_view host_mapping_rules,
280 bool enable_caching) {
281 return HostResolver::CreateStandaloneResolver(
282 net_log, options, host_mapping_rules, enable_caching);
283 }
284
285 HostResolver::ResolveHostParameters::ResolveHostParameters() = default;
286
287 HostResolver::ResolveHostParameters::ResolveHostParameters(
288 const ResolveHostParameters& other) = default;
289
290 HostResolver::~HostResolver() = default;
291
292 std::unique_ptr<HostResolver::ProbeRequest>
CreateDohProbeRequest()293 HostResolver::CreateDohProbeRequest() {
294 // Should be overridden in any HostResolver implementation where this method
295 // may be called.
296 NOTREACHED();
297 return nullptr;
298 }
299
CreateMdnsListener(const HostPortPair & host,DnsQueryType query_type)300 std::unique_ptr<HostResolver::MdnsListener> HostResolver::CreateMdnsListener(
301 const HostPortPair& host,
302 DnsQueryType query_type) {
303 // Should be overridden in any HostResolver implementation where this method
304 // may be called.
305 NOTREACHED();
306 return nullptr;
307 }
308
GetHostCache()309 HostCache* HostResolver::GetHostCache() {
310 return nullptr;
311 }
312
GetDnsConfigAsValue() const313 base::Value::Dict HostResolver::GetDnsConfigAsValue() const {
314 return base::Value::Dict();
315 }
316
SetRequestContext(URLRequestContext * request_context)317 void HostResolver::SetRequestContext(URLRequestContext* request_context) {
318 // Should be overridden in any HostResolver implementation where this method
319 // may be called.
320 NOTREACHED();
321 }
322
GetManagerForTesting()323 HostResolverManager* HostResolver::GetManagerForTesting() {
324 // Should be overridden in any HostResolver implementation where this method
325 // may be called.
326 NOTREACHED();
327 return nullptr;
328 }
329
GetContextForTesting() const330 const URLRequestContext* HostResolver::GetContextForTesting() const {
331 // Should be overridden in any HostResolver implementation where this method
332 // may be called.
333 NOTREACHED();
334 return nullptr;
335 }
336
GetTargetNetworkForTesting() const337 handles::NetworkHandle HostResolver::GetTargetNetworkForTesting() const {
338 return handles::kInvalidNetworkHandle;
339 }
340
341 // static
CreateResolver(HostResolverManager * manager,std::string_view host_mapping_rules,bool enable_caching)342 std::unique_ptr<HostResolver> HostResolver::CreateResolver(
343 HostResolverManager* manager,
344 std::string_view host_mapping_rules,
345 bool enable_caching) {
346 DCHECK(manager);
347
348 auto resolve_context = std::make_unique<ResolveContext>(
349 nullptr /* url_request_context */, enable_caching);
350
351 auto resolver = std::make_unique<ContextHostResolver>(
352 manager, std::move(resolve_context));
353
354 if (host_mapping_rules.empty())
355 return resolver;
356 auto remapped_resolver =
357 std::make_unique<MappedHostResolver>(std::move(resolver));
358 remapped_resolver->SetRulesFromString(host_mapping_rules);
359 return remapped_resolver;
360 }
361
362 // static
CreateStandaloneResolver(NetLog * net_log,std::optional<ManagerOptions> options,std::string_view host_mapping_rules,bool enable_caching)363 std::unique_ptr<HostResolver> HostResolver::CreateStandaloneResolver(
364 NetLog* net_log,
365 std::optional<ManagerOptions> options,
366 std::string_view host_mapping_rules,
367 bool enable_caching) {
368 std::unique_ptr<ContextHostResolver> resolver =
369 CreateStandaloneContextResolver(net_log, std::move(options),
370 enable_caching);
371
372 if (host_mapping_rules.empty())
373 return resolver;
374 auto remapped_resolver =
375 std::make_unique<MappedHostResolver>(std::move(resolver));
376 remapped_resolver->SetRulesFromString(host_mapping_rules);
377 return remapped_resolver;
378 }
379
380 // static
381 std::unique_ptr<ContextHostResolver>
CreateStandaloneContextResolver(NetLog * net_log,std::optional<ManagerOptions> options,bool enable_caching)382 HostResolver::CreateStandaloneContextResolver(
383 NetLog* net_log,
384 std::optional<ManagerOptions> options,
385 bool enable_caching) {
386 auto resolve_context = std::make_unique<ResolveContext>(
387 nullptr /* url_request_context */, enable_caching);
388
389 return std::make_unique<ContextHostResolver>(
390 std::make_unique<HostResolverManager>(
391 std::move(options).value_or(ManagerOptions()),
392 NetworkChangeNotifier::GetSystemDnsConfigNotifier(), net_log),
393 std::move(resolve_context));
394 }
395
396 // static
397 std::unique_ptr<HostResolver>
CreateStandaloneNetworkBoundResolver(NetLog * net_log,handles::NetworkHandle target_network,std::optional<ManagerOptions> options,std::string_view host_mapping_rules,bool enable_caching)398 HostResolver::CreateStandaloneNetworkBoundResolver(
399 NetLog* net_log,
400 handles::NetworkHandle target_network,
401 std::optional<ManagerOptions> options,
402 std::string_view host_mapping_rules,
403 bool enable_caching) {
404 #if BUILDFLAG(IS_ANDROID)
405 // Note that the logic below uses Android APIs that don't work on a sandboxed
406 // process: This is not problematic because this function is used only by
407 // Cronet which doesn't enable sandboxing.
408
409 auto resolve_context = std::make_unique<ResolveContext>(
410 nullptr /*url_request_context */, enable_caching);
411 auto manager_options = std::move(options).value_or(ManagerOptions());
412 // Support the use of the built-in resolver when possible.
413 bool is_builtin_resolver_supported =
414 manager_options.insecure_dns_client_enabled &&
415 base::android::BuildInfo::GetInstance()->sdk_int() >=
416 base::android::SDK_VERSION_P;
417 if (is_builtin_resolver_supported) {
418 // Pre-existing DnsConfigOverrides is currently ignored, consider extending
419 // if a use case arises.
420 DCHECK(manager_options.dns_config_overrides == DnsConfigOverrides());
421
422 std::vector<IPEndPoint> dns_servers;
423 bool dns_over_tls_active;
424 std::string dns_over_tls_hostname;
425 std::vector<std::string> search_suffixes;
426 if (android::GetDnsServersForNetwork(&dns_servers, &dns_over_tls_active,
427 &dns_over_tls_hostname,
428 &search_suffixes, target_network)) {
429 DnsConfigOverrides dns_config_overrides =
430 DnsConfigOverrides::CreateOverridingEverythingWithDefaults();
431 dns_config_overrides.nameservers = dns_servers;
432 // Android APIs don't specify whether to use DoT or DoH. So, leave the
433 // decision to `DnsConfig::allow_dns_over_https_upgrade` default value.
434 dns_config_overrides.dns_over_tls_active = dns_over_tls_active;
435 dns_config_overrides.dns_over_tls_hostname = dns_over_tls_hostname;
436 dns_config_overrides.search = search_suffixes;
437
438 manager_options.dns_config_overrides = dns_config_overrides;
439 // Regardless of DoH vs DoT, the important contract to respect is not to
440 // perform insecure DNS lookups if `dns_over_tls_active` == true.
441 manager_options.additional_types_via_insecure_dns_enabled =
442 !dns_over_tls_active;
443 } else {
444 // Disable when android::GetDnsServersForNetwork fails.
445 is_builtin_resolver_supported = false;
446 }
447 }
448
449 manager_options.insecure_dns_client_enabled = is_builtin_resolver_supported;
450 return std::make_unique<ContextHostResolver>(
451 HostResolverManager::CreateNetworkBoundHostResolverManager(
452 manager_options, target_network, net_log),
453 std::move(resolve_context));
454 #else // !BUILDFLAG(IS_ANDROID)
455 NOTIMPLEMENTED();
456 return nullptr;
457 #endif // BUILDFLAG(IS_ANDROID)
458 }
459
460 // static
DnsQueryTypeSetToAddressFamily(DnsQueryTypeSet dns_query_types)461 AddressFamily HostResolver::DnsQueryTypeSetToAddressFamily(
462 DnsQueryTypeSet dns_query_types) {
463 DCHECK(HasAddressType(dns_query_types));
464 // If the set of query types contains A and AAAA, defer the choice of address
465 // family. Otherwise, pick the corresponding address family.
466 if (dns_query_types.HasAll({DnsQueryType::A, DnsQueryType::AAAA}))
467 return ADDRESS_FAMILY_UNSPECIFIED;
468 if (dns_query_types.Has(DnsQueryType::AAAA))
469 return ADDRESS_FAMILY_IPV6;
470 DCHECK(dns_query_types.Has(DnsQueryType::A));
471 return ADDRESS_FAMILY_IPV4;
472 }
473
474 // static
ParametersToHostResolverFlags(const ResolveHostParameters & parameters)475 HostResolverFlags HostResolver::ParametersToHostResolverFlags(
476 const ResolveHostParameters& parameters) {
477 HostResolverFlags flags = 0;
478 if (parameters.include_canonical_name)
479 flags |= HOST_RESOLVER_CANONNAME;
480 if (parameters.loopback_only)
481 flags |= HOST_RESOLVER_LOOPBACK_ONLY;
482 if (parameters.avoid_multicast_resolution)
483 flags |= HOST_RESOLVER_AVOID_MULTICAST;
484 return flags;
485 }
486
487 // static
SquashErrorCode(int error)488 int HostResolver::SquashErrorCode(int error) {
489 // TODO(crbug.com/1043281): Consider squashing ERR_INTERNET_DISCONNECTED.
490 if (error == OK || error == ERR_IO_PENDING ||
491 error == ERR_INTERNET_DISCONNECTED || error == ERR_NAME_NOT_RESOLVED ||
492 error == ERR_DNS_NAME_HTTPS_ONLY) {
493 return error;
494 } else {
495 return ERR_NAME_NOT_RESOLVED;
496 }
497 }
498
499 // static
EndpointResultToAddressList(base::span<const HostResolverEndpointResult> endpoints,const std::set<std::string> & aliases)500 AddressList HostResolver::EndpointResultToAddressList(
501 base::span<const HostResolverEndpointResult> endpoints,
502 const std::set<std::string>& aliases) {
503 AddressList list;
504
505 auto non_protocol_endpoint =
506 base::ranges::find_if(endpoints, &EndpointResultIsNonProtocol);
507 if (non_protocol_endpoint == endpoints.end())
508 return list;
509
510 list.endpoints() = non_protocol_endpoint->ip_endpoints;
511
512 std::vector<std::string> aliases_vector(aliases.begin(), aliases.end());
513 list.SetDnsAliases(std::move(aliases_vector));
514
515 return list;
516 }
517
518 // static
AllProtocolEndpointsHaveEch(base::span<const HostResolverEndpointResult> endpoints)519 bool HostResolver::AllProtocolEndpointsHaveEch(
520 base::span<const HostResolverEndpointResult> endpoints) {
521 bool has_svcb = false;
522 for (const auto& endpoint : endpoints) {
523 if (!endpoint.metadata.supported_protocol_alpns.empty()) {
524 has_svcb = true;
525 if (endpoint.metadata.ech_config_list.empty()) {
526 return false; // There is a non-ECH SVCB/HTTPS route.
527 }
528 }
529 }
530 // Either there were no SVCB/HTTPS records (should be SVCB-optional), or there
531 // were and all supported ECH (should be SVCB-reliant).
532 return has_svcb;
533 }
534
535 // static
MayUseNAT64ForIPv4Literal(HostResolverFlags flags,HostResolverSource source,const IPAddress & ip_address)536 bool HostResolver::MayUseNAT64ForIPv4Literal(HostResolverFlags flags,
537 HostResolverSource source,
538 const IPAddress& ip_address) {
539 return !(flags & HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6) &&
540 ip_address.IsValid() && ip_address.IsIPv4() &&
541 base::FeatureList::IsEnabled(features::kUseNAT64ForIPv4Literal) &&
542 (source != HostResolverSource::LOCAL_ONLY);
543 }
544
545 HostResolver::HostResolver() = default;
546
547 // static
548 std::unique_ptr<HostResolver::ResolveHostRequest>
CreateFailingRequest(int error)549 HostResolver::CreateFailingRequest(int error) {
550 return std::make_unique<FailingRequestImpl>(error);
551 }
552
553 // static
554 std::unique_ptr<HostResolver::ProbeRequest>
CreateFailingProbeRequest(int error)555 HostResolver::CreateFailingProbeRequest(int error) {
556 return std::make_unique<FailingRequestImpl>(error);
557 }
558
559 } // namespace net
560