1 // Copyright 2022 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_internal_result.h"
6
7 #include <map>
8 #include <memory>
9 #include <ostream>
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include "base/check_op.h"
15 #include "base/json/values_util.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/strings/string_piece.h"
18 #include "base/time/time.h"
19 #include "base/values.h"
20 #include "net/base/connection_endpoint_metadata.h"
21 #include "net/base/host_port_pair.h"
22 #include "net/base/ip_endpoint.h"
23 #include "net/base/net_errors.h"
24 #include "net/dns/https_record_rdata.h"
25 #include "net/dns/public/dns_query_type.h"
26 #include "third_party/abseil-cpp/absl/types/optional.h"
27 #include "url/url_canon.h"
28 #include "url/url_canon_stdstring.h"
29
30 namespace net {
31
32 namespace {
33
34 // base::Value keys
35 constexpr base::StringPiece kValueDomainNameKey = "domain_name";
36 constexpr base::StringPiece kValueQueryTypeKey = "query_type";
37 constexpr base::StringPiece kValueTypeKey = "type";
38 constexpr base::StringPiece kValueSourceKey = "source";
39 constexpr base::StringPiece kValueTimedExpirationKey = "timed_expiration";
40 constexpr base::StringPiece kValueEndpointsKey = "endpoints";
41 constexpr base::StringPiece kValueStringsKey = "strings";
42 constexpr base::StringPiece kValueHostsKey = "hosts";
43 constexpr base::StringPiece kValueMetadatasKey = "metadatas";
44 constexpr base::StringPiece kValueMetadataWeightKey = "metadata_weight";
45 constexpr base::StringPiece kValueMetadataValueKey = "metadata_value";
46 constexpr base::StringPiece kValueErrorKey = "error";
47 constexpr base::StringPiece kValueAliasTargetKey = "alias_target";
48
49 // Returns `domain_name` as-is if it could not be canonicalized.
MaybeCanonicalizeName(std::string domain_name)50 std::string MaybeCanonicalizeName(std::string domain_name) {
51 std::string canonicalized;
52 url::StdStringCanonOutput output(&canonicalized);
53 url::CanonHostInfo host_info;
54
55 url::CanonicalizeHostVerbose(domain_name.data(),
56 url::Component(0, domain_name.size()), &output,
57 &host_info);
58
59 if (host_info.family == url::CanonHostInfo::Family::NEUTRAL) {
60 output.Complete();
61 return canonicalized;
62 } else {
63 return domain_name;
64 }
65 }
66
EndpointMetadataPairToValue(const std::pair<HttpsRecordPriority,ConnectionEndpointMetadata> & pair)67 base::Value EndpointMetadataPairToValue(
68 const std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>& pair) {
69 base::Value::Dict dictionary;
70 dictionary.Set(kValueMetadataWeightKey, pair.first);
71 dictionary.Set(kValueMetadataValueKey, pair.second.ToValue());
72 return base::Value(std::move(dictionary));
73 }
74
75 absl::optional<std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>>
EndpointMetadataPairFromValue(const base::Value & value)76 EndpointMetadataPairFromValue(const base::Value& value) {
77 const base::Value::Dict* dict = value.GetIfDict();
78 if (!dict)
79 return absl::nullopt;
80
81 absl::optional<int> weight = dict->FindInt(kValueMetadataWeightKey);
82 if (!weight || !base::IsValueInRangeForNumericType<HttpsRecordPriority>(
83 weight.value())) {
84 return absl::nullopt;
85 }
86
87 const base::Value* metadata_value = dict->Find(kValueMetadataValueKey);
88 if (!metadata_value)
89 return absl::nullopt;
90 absl::optional<ConnectionEndpointMetadata> metadata =
91 ConnectionEndpointMetadata::FromValue(*metadata_value);
92 if (!metadata)
93 return absl::nullopt;
94
95 return std::make_pair(base::checked_cast<HttpsRecordPriority>(weight.value()),
96 std::move(metadata).value());
97 }
98
QueryTypeFromValue(const base::Value & value)99 absl::optional<DnsQueryType> QueryTypeFromValue(const base::Value& value) {
100 const std::string* query_type_string = value.GetIfString();
101 if (!query_type_string)
102 return absl::nullopt;
103 const auto* query_type_it =
104 base::ranges::find(kDnsQueryTypes, *query_type_string,
105 &decltype(kDnsQueryTypes)::value_type::second);
106 if (query_type_it == kDnsQueryTypes.end())
107 return absl::nullopt;
108
109 return query_type_it->first;
110 }
111
TypeToValue(HostResolverInternalResult::Type type)112 base::Value TypeToValue(HostResolverInternalResult::Type type) {
113 switch (type) {
114 case HostResolverInternalResult::Type::kData:
115 return base::Value("data");
116 case HostResolverInternalResult::Type::kMetadata:
117 return base::Value("metadata");
118 case HostResolverInternalResult::Type::kError:
119 return base::Value("error");
120 case HostResolverInternalResult::Type::kAlias:
121 return base::Value("alias");
122 }
123 }
124
TypeFromValue(const base::Value & value)125 absl::optional<HostResolverInternalResult::Type> TypeFromValue(
126 const base::Value& value) {
127 const std::string* string = value.GetIfString();
128 if (!string)
129 return absl::nullopt;
130
131 if (*string == "data") {
132 return HostResolverInternalResult::Type::kData;
133 } else if (*string == "metadata") {
134 return HostResolverInternalResult::Type::kMetadata;
135 } else if (*string == "error") {
136 return HostResolverInternalResult::Type::kError;
137 } else if (*string == "alias") {
138 return HostResolverInternalResult::Type::kAlias;
139 } else {
140 return absl::nullopt;
141 }
142 }
143
SourceToValue(HostResolverInternalResult::Source source)144 base::Value SourceToValue(HostResolverInternalResult::Source source) {
145 switch (source) {
146 case HostResolverInternalResult::Source::kDns:
147 return base::Value("dns");
148 case HostResolverInternalResult::Source::kHosts:
149 return base::Value("hosts");
150 case HostResolverInternalResult::Source::kUnknown:
151 return base::Value("unknown");
152 }
153 }
154
SourceFromValue(const base::Value & value)155 absl::optional<HostResolverInternalResult::Source> SourceFromValue(
156 const base::Value& value) {
157 const std::string* string = value.GetIfString();
158 if (!string)
159 return absl::nullopt;
160
161 if (*string == "dns") {
162 return HostResolverInternalResult::Source::kDns;
163 } else if (*string == "hosts") {
164 return HostResolverInternalResult::Source::kHosts;
165 } else if (*string == "unknown") {
166 return HostResolverInternalResult::Source::kUnknown;
167 } else {
168 return absl::nullopt;
169 }
170 }
171
172 } // namespace
173
174 // static
175 std::unique_ptr<HostResolverInternalResult>
FromValue(const base::Value & value)176 HostResolverInternalResult::FromValue(const base::Value& value) {
177 const base::Value::Dict* dict = value.GetIfDict();
178 if (!dict)
179 return nullptr;
180
181 const base::Value* type_value = dict->Find(kValueTypeKey);
182 if (!type_value)
183 return nullptr;
184 absl::optional<Type> type = TypeFromValue(*type_value);
185 if (!type.has_value())
186 return nullptr;
187
188 switch (type.value()) {
189 case Type::kData:
190 return HostResolverInternalDataResult::FromValue(value);
191 case Type::kMetadata:
192 return HostResolverInternalMetadataResult::FromValue(value);
193 case Type::kError:
194 return HostResolverInternalErrorResult::FromValue(value);
195 case Type::kAlias:
196 return HostResolverInternalAliasResult::FromValue(value);
197 }
198 }
199
AsData() const200 const HostResolverInternalDataResult& HostResolverInternalResult::AsData()
201 const {
202 CHECK_EQ(type_, Type::kData);
203 return *static_cast<const HostResolverInternalDataResult*>(this);
204 }
205
206 const HostResolverInternalMetadataResult&
AsMetadata() const207 HostResolverInternalResult::AsMetadata() const {
208 CHECK_EQ(type_, Type::kMetadata);
209 return *static_cast<const HostResolverInternalMetadataResult*>(this);
210 }
211
AsError() const212 const HostResolverInternalErrorResult& HostResolverInternalResult::AsError()
213 const {
214 CHECK_EQ(type_, Type::kError);
215 return *static_cast<const HostResolverInternalErrorResult*>(this);
216 }
217
AsAlias() const218 const HostResolverInternalAliasResult& HostResolverInternalResult::AsAlias()
219 const {
220 CHECK_EQ(type_, Type::kAlias);
221 return *static_cast<const HostResolverInternalAliasResult*>(this);
222 }
223
HostResolverInternalResult(std::string domain_name,DnsQueryType query_type,absl::optional<base::TimeTicks> expiration,absl::optional<base::Time> timed_expiration,Type type,Source source)224 HostResolverInternalResult::HostResolverInternalResult(
225 std::string domain_name,
226 DnsQueryType query_type,
227 absl::optional<base::TimeTicks> expiration,
228 absl::optional<base::Time> timed_expiration,
229 Type type,
230 Source source)
231 : domain_name_(MaybeCanonicalizeName(std::move(domain_name))),
232 query_type_(query_type),
233 type_(type),
234 source_(source),
235 expiration_(expiration),
236 timed_expiration_(timed_expiration) {
237 DCHECK(!domain_name_.empty());
238 // If `expiration` has a value, `timed_expiration` must too.
239 DCHECK(!expiration_.has_value() || timed_expiration.has_value());
240 }
241
HostResolverInternalResult(const base::Value::Dict & dict)242 HostResolverInternalResult::HostResolverInternalResult(
243 const base::Value::Dict& dict)
244 : domain_name_(*dict.FindString(kValueDomainNameKey)),
245 query_type_(QueryTypeFromValue(*dict.Find(kValueQueryTypeKey)).value()),
246 type_(TypeFromValue(*dict.Find(kValueTypeKey)).value()),
247 source_(SourceFromValue(*dict.Find(kValueSourceKey)).value()),
248 timed_expiration_(
249 dict.contains(kValueTimedExpirationKey)
250 ? base::ValueToTime(*dict.Find(kValueTimedExpirationKey))
251 : absl::optional<base::Time>()) {}
252
253 // static
ValidateValueBaseDict(const base::Value::Dict & dict,bool require_timed_expiration)254 bool HostResolverInternalResult::ValidateValueBaseDict(
255 const base::Value::Dict& dict,
256 bool require_timed_expiration) {
257 const std::string* domain_name = dict.FindString(kValueDomainNameKey);
258 if (!domain_name)
259 return false;
260
261 const std::string* query_type_string = dict.FindString(kValueQueryTypeKey);
262 if (!query_type_string)
263 return false;
264 const auto* query_type_it =
265 base::ranges::find(kDnsQueryTypes, *query_type_string,
266 &decltype(kDnsQueryTypes)::value_type::second);
267 if (query_type_it == kDnsQueryTypes.end())
268 return false;
269
270 const base::Value* type_value = dict.Find(kValueTypeKey);
271 if (!type_value)
272 return false;
273 absl::optional<Type> type = TypeFromValue(*type_value);
274 if (!type.has_value())
275 return false;
276
277 const base::Value* source_value = dict.Find(kValueSourceKey);
278 if (!source_value)
279 return false;
280 absl::optional<Source> source = SourceFromValue(*source_value);
281 if (!source.has_value())
282 return false;
283
284 absl::optional<base::Time> timed_expiration;
285 const base::Value* timed_expiration_value =
286 dict.Find(kValueTimedExpirationKey);
287 if (require_timed_expiration && !timed_expiration_value)
288 return false;
289 if (timed_expiration_value) {
290 timed_expiration = base::ValueToTime(timed_expiration_value);
291 if (!timed_expiration.has_value())
292 return false;
293 }
294
295 return true;
296 }
297
ToValueBaseDict() const298 base::Value::Dict HostResolverInternalResult::ToValueBaseDict() const {
299 base::Value::Dict dict;
300
301 dict.Set(kValueDomainNameKey, domain_name_);
302 dict.Set(kValueQueryTypeKey, kDnsQueryTypes.at(query_type_));
303 dict.Set(kValueTypeKey, TypeToValue(type_));
304 dict.Set(kValueSourceKey, SourceToValue(source_));
305
306 // `expiration_` is not serialized because it is TimeTicks.
307
308 if (timed_expiration_.has_value()) {
309 dict.Set(kValueTimedExpirationKey,
310 base::TimeToValue(timed_expiration_.value()));
311 }
312
313 return dict;
314 }
315
316 // static
317 std::unique_ptr<HostResolverInternalDataResult>
FromValue(const base::Value & value)318 HostResolverInternalDataResult::FromValue(const base::Value& value) {
319 const base::Value::Dict* dict = value.GetIfDict();
320 if (!dict || !ValidateValueBaseDict(*dict, /*require_timed_expiration=*/true))
321 return nullptr;
322
323 const base::Value::List* endpoint_values = dict->FindList(kValueEndpointsKey);
324 if (!endpoint_values)
325 return nullptr;
326
327 std::vector<IPEndPoint> endpoints;
328 endpoints.reserve(endpoint_values->size());
329 for (const base::Value& endpoint_value : *endpoint_values) {
330 absl::optional<IPEndPoint> endpoint = IPEndPoint::FromValue(endpoint_value);
331 if (!endpoint.has_value())
332 return nullptr;
333
334 endpoints.push_back(std::move(endpoint).value());
335 }
336
337 const base::Value::List* string_values = dict->FindList(kValueStringsKey);
338 if (!string_values)
339 return nullptr;
340
341 std::vector<std::string> strings;
342 strings.reserve(string_values->size());
343 for (const base::Value& string_value : *string_values) {
344 const std::string* string = string_value.GetIfString();
345 if (!string)
346 return nullptr;
347
348 strings.push_back(*string);
349 }
350
351 const base::Value::List* host_values = dict->FindList(kValueHostsKey);
352 if (!host_values)
353 return nullptr;
354
355 std::vector<HostPortPair> hosts;
356 hosts.reserve(host_values->size());
357 for (const base::Value& host_value : *host_values) {
358 absl::optional<HostPortPair> host = HostPortPair::FromValue(host_value);
359 if (!host.has_value())
360 return nullptr;
361
362 hosts.push_back(std::move(host).value());
363 }
364
365 // WrapUnique due to private constructor.
366 return base::WrapUnique(new HostResolverInternalDataResult(
367 *dict, std::move(endpoints), std::move(strings), std::move(hosts)));
368 }
369
HostResolverInternalDataResult(std::string domain_name,DnsQueryType query_type,absl::optional<base::TimeTicks> expiration,base::Time timed_expiration,Source source,std::vector<IPEndPoint> endpoints,std::vector<std::string> strings,std::vector<HostPortPair> hosts)370 HostResolverInternalDataResult::HostResolverInternalDataResult(
371 std::string domain_name,
372 DnsQueryType query_type,
373 absl::optional<base::TimeTicks> expiration,
374 base::Time timed_expiration,
375 Source source,
376 std::vector<IPEndPoint> endpoints,
377 std::vector<std::string> strings,
378 std::vector<HostPortPair> hosts)
379 : HostResolverInternalResult(std::move(domain_name),
380 query_type,
381 expiration,
382 timed_expiration,
383 Type::kData,
384 source),
385 endpoints_(std::move(endpoints)),
386 strings_(std::move(strings)),
387 hosts_(std::move(hosts)) {
388 DCHECK(!endpoints_.empty() || !strings_.empty() || !hosts_.empty());
389 }
390
391 HostResolverInternalDataResult::~HostResolverInternalDataResult() = default;
392
ToValue() const393 base::Value HostResolverInternalDataResult::ToValue() const {
394 base::Value::Dict dict = ToValueBaseDict();
395
396 base::Value::List endpoints_list;
397 endpoints_list.reserve(endpoints_.size());
398 for (IPEndPoint endpoint : endpoints_) {
399 endpoints_list.Append(endpoint.ToValue());
400 }
401 dict.Set(kValueEndpointsKey, std::move(endpoints_list));
402
403 base::Value::List strings_list;
404 strings_list.reserve(strings_.size());
405 for (const std::string& string : strings_) {
406 strings_list.Append(string);
407 }
408 dict.Set(kValueStringsKey, std::move(strings_list));
409
410 base::Value::List hosts_list;
411 hosts_list.reserve(hosts_.size());
412 for (const HostPortPair& host : hosts_) {
413 hosts_list.Append(host.ToValue());
414 }
415 dict.Set(kValueHostsKey, std::move(hosts_list));
416
417 return base::Value(std::move(dict));
418 }
419
HostResolverInternalDataResult(const base::Value::Dict & dict,std::vector<IPEndPoint> endpoints,std::vector<std::string> strings,std::vector<HostPortPair> hosts)420 HostResolverInternalDataResult::HostResolverInternalDataResult(
421 const base::Value::Dict& dict,
422 std::vector<IPEndPoint> endpoints,
423 std::vector<std::string> strings,
424 std::vector<HostPortPair> hosts)
425 : HostResolverInternalResult(dict),
426 endpoints_(std::move(endpoints)),
427 strings_(std::move(strings)),
428 hosts_(std::move(hosts)) {}
429
430 // static
431 std::unique_ptr<HostResolverInternalMetadataResult>
FromValue(const base::Value & value)432 HostResolverInternalMetadataResult::FromValue(const base::Value& value) {
433 const base::Value::Dict* dict = value.GetIfDict();
434 if (!dict || !ValidateValueBaseDict(*dict, /*require_timed_expiration=*/true))
435 return nullptr;
436
437 const base::Value::List* metadata_values = dict->FindList(kValueMetadatasKey);
438 if (!metadata_values)
439 return nullptr;
440
441 std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> metadatas;
442 for (const base::Value& metadata_value : *metadata_values) {
443 absl::optional<std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>>
444 metadata = EndpointMetadataPairFromValue(metadata_value);
445 if (!metadata.has_value())
446 return nullptr;
447 metadatas.insert(std::move(metadata).value());
448 }
449
450 // WrapUnique due to private constructor.
451 return base::WrapUnique(
452 new HostResolverInternalMetadataResult(*dict, std::move(metadatas)));
453 }
454
HostResolverInternalMetadataResult(std::string domain_name,DnsQueryType query_type,absl::optional<base::TimeTicks> expiration,base::Time timed_expiration,Source source,std::multimap<HttpsRecordPriority,ConnectionEndpointMetadata> metadatas)455 HostResolverInternalMetadataResult::HostResolverInternalMetadataResult(
456 std::string domain_name,
457 DnsQueryType query_type,
458 absl::optional<base::TimeTicks> expiration,
459 base::Time timed_expiration,
460 Source source,
461 std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> metadatas)
462 : HostResolverInternalResult(std::move(domain_name),
463 query_type,
464 expiration,
465 timed_expiration,
466 Type::kMetadata,
467 source),
468 metadatas_(std::move(metadatas)) {}
469
470 HostResolverInternalMetadataResult::~HostResolverInternalMetadataResult() =
471 default;
472
ToValue() const473 base::Value HostResolverInternalMetadataResult::ToValue() const {
474 base::Value::Dict dict = ToValueBaseDict();
475
476 base::Value::List metadatas_list;
477 metadatas_list.reserve(metadatas_.size());
478 for (const std::pair<const HttpsRecordPriority, ConnectionEndpointMetadata>&
479 metadata_pair : metadatas_) {
480 metadatas_list.Append(EndpointMetadataPairToValue(metadata_pair));
481 }
482 dict.Set(kValueMetadatasKey, std::move(metadatas_list));
483
484 return base::Value(std::move(dict));
485 }
486
HostResolverInternalMetadataResult(const base::Value::Dict & dict,std::multimap<HttpsRecordPriority,ConnectionEndpointMetadata> metadatas)487 HostResolverInternalMetadataResult::HostResolverInternalMetadataResult(
488 const base::Value::Dict& dict,
489 std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> metadatas)
490 : HostResolverInternalResult(dict), metadatas_(std::move(metadatas)) {}
491
492 // static
493 std::unique_ptr<HostResolverInternalErrorResult>
FromValue(const base::Value & value)494 HostResolverInternalErrorResult::FromValue(const base::Value& value) {
495 const base::Value::Dict* dict = value.GetIfDict();
496 if (!dict ||
497 !ValidateValueBaseDict(*dict, /*require_timed_expiration=*/false)) {
498 return nullptr;
499 }
500
501 absl::optional<int> error = dict->FindInt(kValueErrorKey);
502 if (!error.has_value())
503 return nullptr;
504
505 // WrapUnique due to private constructor.
506 return base::WrapUnique(
507 new HostResolverInternalErrorResult(*dict, error.value()));
508 }
509
HostResolverInternalErrorResult(std::string domain_name,DnsQueryType query_type,absl::optional<base::TimeTicks> expiration,absl::optional<base::Time> timed_expiration,Source source,int error)510 HostResolverInternalErrorResult::HostResolverInternalErrorResult(
511 std::string domain_name,
512 DnsQueryType query_type,
513 absl::optional<base::TimeTicks> expiration,
514 absl::optional<base::Time> timed_expiration,
515 Source source,
516 int error)
517 : HostResolverInternalResult(std::move(domain_name),
518 query_type,
519 expiration,
520 timed_expiration,
521 Type::kError,
522 source),
523 error_(error) {}
524
ToValue() const525 base::Value HostResolverInternalErrorResult::ToValue() const {
526 base::Value::Dict dict = ToValueBaseDict();
527
528 dict.Set(kValueErrorKey, error_);
529
530 return base::Value(std::move(dict));
531 }
532
HostResolverInternalErrorResult(const base::Value::Dict & dict,int error)533 HostResolverInternalErrorResult::HostResolverInternalErrorResult(
534 const base::Value::Dict& dict,
535 int error)
536 : HostResolverInternalResult(dict), error_(error) {
537 DCHECK_NE(error_, OK);
538 }
539
540 // static
541 std::unique_ptr<HostResolverInternalAliasResult>
FromValue(const base::Value & value)542 HostResolverInternalAliasResult::FromValue(const base::Value& value) {
543 const base::Value::Dict* dict = value.GetIfDict();
544 if (!dict || !ValidateValueBaseDict(*dict, /*require_timed_expiration=*/true))
545 return nullptr;
546
547 const std::string* target = dict->FindString(kValueAliasTargetKey);
548 if (!target)
549 return nullptr;
550
551 // WrapUnique due to private constructor.
552 return base::WrapUnique(new HostResolverInternalAliasResult(*dict, *target));
553 }
554
HostResolverInternalAliasResult(std::string domain_name,DnsQueryType query_type,absl::optional<base::TimeTicks> expiration,base::Time timed_expiration,Source source,std::string alias_target)555 HostResolverInternalAliasResult::HostResolverInternalAliasResult(
556 std::string domain_name,
557 DnsQueryType query_type,
558 absl::optional<base::TimeTicks> expiration,
559 base::Time timed_expiration,
560 Source source,
561 std::string alias_target)
562 : HostResolverInternalResult(std::move(domain_name),
563 query_type,
564 expiration,
565 timed_expiration,
566 Type::kAlias,
567 source),
568 alias_target_(MaybeCanonicalizeName(std::move(alias_target))) {
569 DCHECK(!alias_target_.empty());
570 }
571
ToValue() const572 base::Value HostResolverInternalAliasResult::ToValue() const {
573 base::Value::Dict dict = ToValueBaseDict();
574
575 dict.Set(kValueAliasTargetKey, alias_target_);
576
577 return base::Value(std::move(dict));
578 }
579
HostResolverInternalAliasResult(const base::Value::Dict & dict,std::string alias_target)580 HostResolverInternalAliasResult::HostResolverInternalAliasResult(
581 const base::Value::Dict& dict,
582 std::string alias_target)
583 : HostResolverInternalResult(dict),
584 alias_target_(MaybeCanonicalizeName(std::move(alias_target))) {}
585
586 } // namespace net
587