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 #include "net/dns/dns_response_result_extractor.h"
6
7 #include <limits.h>
8 #include <stdint.h>
9
10 #include <iterator>
11 #include <map>
12 #include <memory>
13 #include <ostream>
14 #include <set>
15 #include <string>
16 #include <unordered_set>
17 #include <vector>
18
19 #include "base/check.h"
20 #include "base/containers/contains.h"
21 #include "base/dcheck_is_on.h"
22 #include "base/metrics/histogram_macros.h"
23 #include "base/notreached.h"
24 #include "base/numerics/checked_math.h"
25 #include "base/numerics/ostream_operators.h"
26 #include "base/rand_util.h"
27 #include "base/ranges/algorithm.h"
28 #include "base/strings/string_piece.h"
29 #include "base/strings/string_util.h"
30 #include "base/time/time.h"
31 #include "net/base/address_list.h"
32 #include "net/base/connection_endpoint_metadata.h"
33 #include "net/base/host_port_pair.h"
34 #include "net/base/ip_address.h"
35 #include "net/base/ip_endpoint.h"
36 #include "net/base/net_errors.h"
37 #include "net/dns/dns_alias_utility.h"
38 #include "net/dns/dns_names_util.h"
39 #include "net/dns/dns_response.h"
40 #include "net/dns/dns_util.h"
41 #include "net/dns/host_cache.h"
42 #include "net/dns/https_record_rdata.h"
43 #include "net/dns/public/dns_protocol.h"
44 #include "net/dns/public/dns_query_type.h"
45 #include "net/dns/record_parsed.h"
46 #include "net/dns/record_rdata.h"
47 #include "third_party/abseil-cpp/absl/types/optional.h"
48
49 namespace net {
50
51 namespace {
52
53 using AliasMap =
54 std::map<std::string, std::string, dns_names_util::DomainNameComparator>;
55 using ExtractionError = DnsResponseResultExtractor::ExtractionError;
56
SaveMetricsForAdditionalHttpsRecord(const RecordParsed & record,bool is_unsolicited)57 void SaveMetricsForAdditionalHttpsRecord(const RecordParsed& record,
58 bool is_unsolicited) {
59 const HttpsRecordRdata* rdata = record.rdata<HttpsRecordRdata>();
60 DCHECK(rdata);
61
62 // These values are persisted to logs. Entries should not be renumbered and
63 // numeric values should never be reused.
64 enum class UnsolicitedHttpsRecordStatus {
65 kMalformed = 0, // No longer recorded.
66 kAlias = 1,
67 kService = 2,
68 kMaxValue = kService
69 } status;
70
71 if (rdata->IsAlias()) {
72 status = UnsolicitedHttpsRecordStatus::kAlias;
73 } else {
74 status = UnsolicitedHttpsRecordStatus::kService;
75 }
76
77 if (is_unsolicited) {
78 UMA_HISTOGRAM_ENUMERATION("Net.DNS.DnsTask.AdditionalHttps.Unsolicited",
79 status);
80 } else {
81 UMA_HISTOGRAM_ENUMERATION("Net.DNS.DnsTask.AdditionalHttps.Requested",
82 status);
83 }
84 }
85
86 // Sort service targets per RFC2782. In summary, sort first by `priority`,
87 // lowest first. For targets with the same priority, secondary sort randomly
88 // using `weight` with higher weighted objects more likely to go first.
SortServiceTargets(const std::vector<const SrvRecordRdata * > & rdatas)89 std::vector<HostPortPair> SortServiceTargets(
90 const std::vector<const SrvRecordRdata*>& rdatas) {
91 std::map<uint16_t, std::unordered_set<const SrvRecordRdata*>>
92 ordered_by_priority;
93 for (const SrvRecordRdata* rdata : rdatas)
94 ordered_by_priority[rdata->priority()].insert(rdata);
95
96 std::vector<HostPortPair> sorted_targets;
97 for (auto& priority : ordered_by_priority) {
98 // With (num results) <= UINT16_MAX (and in practice, much less) and
99 // (weight per result) <= UINT16_MAX, then it should be the case that
100 // (total weight) <= UINT32_MAX, but use CheckedNumeric for extra safety.
101 auto total_weight = base::MakeCheckedNum<uint32_t>(0);
102 for (const SrvRecordRdata* rdata : priority.second)
103 total_weight += rdata->weight();
104
105 // Add 1 to total weight because, to deal with 0-weight targets, we want
106 // our random selection to be inclusive [0, total].
107 total_weight++;
108
109 // Order by weighted random. Make such random selections, removing from
110 // |priority.second| until |priority.second| only contains 1 rdata.
111 while (priority.second.size() >= 2) {
112 uint32_t random_selection =
113 base::RandGenerator(total_weight.ValueOrDie());
114 const SrvRecordRdata* selected_rdata = nullptr;
115 for (const SrvRecordRdata* rdata : priority.second) {
116 // >= to always select the first target on |random_selection| == 0,
117 // even if its weight is 0.
118 if (rdata->weight() >= random_selection) {
119 selected_rdata = rdata;
120 break;
121 }
122 random_selection -= rdata->weight();
123 }
124
125 DCHECK(selected_rdata);
126 sorted_targets.emplace_back(selected_rdata->target(),
127 selected_rdata->port());
128 total_weight -= selected_rdata->weight();
129 size_t removed = priority.second.erase(selected_rdata);
130 DCHECK_EQ(1u, removed);
131 }
132
133 DCHECK_EQ(1u, priority.second.size());
134 DCHECK_EQ((total_weight - 1).ValueOrDie(),
135 (*priority.second.begin())->weight());
136 const SrvRecordRdata* rdata = *priority.second.begin();
137 sorted_targets.emplace_back(rdata->target(), rdata->port());
138 }
139
140 return sorted_targets;
141 }
142
ValidateNamesAndAliases(base::StringPiece query_name,const AliasMap & aliases,const std::vector<std::unique_ptr<const RecordParsed>> & results)143 ExtractionError ValidateNamesAndAliases(
144 base::StringPiece query_name,
145 const AliasMap& aliases,
146 const std::vector<std::unique_ptr<const RecordParsed>>& results) {
147 // Validate that all aliases form a single non-looping chain, starting from
148 // `query_name`.
149 size_t aliases_in_chain = 0;
150 base::StringPiece final_chain_name = query_name;
151 auto alias = aliases.find(std::string(query_name));
152 while (alias != aliases.end() && aliases_in_chain <= aliases.size()) {
153 aliases_in_chain++;
154 final_chain_name = alias->second;
155 alias = aliases.find(alias->second);
156 }
157
158 if (aliases_in_chain != aliases.size())
159 return ExtractionError::kBadAliasChain;
160
161 // All results must match final alias name.
162 for (const auto& result : results) {
163 DCHECK_NE(result->type(), dns_protocol::kTypeCNAME);
164 if (!base::EqualsCaseInsensitiveASCII(final_chain_name, result->name())) {
165 return ExtractionError::kNameMismatch;
166 }
167 }
168
169 return ExtractionError::kOk;
170 }
171
ExtractResponseRecords(const DnsResponse & response,uint16_t result_qtype,std::vector<std::unique_ptr<const RecordParsed>> * out_records,absl::optional<base::TimeDelta> * out_response_ttl,std::set<std::string> * out_aliases)172 ExtractionError ExtractResponseRecords(
173 const DnsResponse& response,
174 uint16_t result_qtype,
175 std::vector<std::unique_ptr<const RecordParsed>>* out_records,
176 absl::optional<base::TimeDelta>* out_response_ttl,
177 std::set<std::string>* out_aliases) {
178 DCHECK_EQ(response.question_count(), 1u);
179 DCHECK(out_records);
180 DCHECK(out_response_ttl);
181
182 std::vector<std::unique_ptr<const RecordParsed>> records;
183 absl::optional<base::TimeDelta> response_ttl;
184
185 DnsRecordParser parser = response.Parser();
186
187 // Expected to be validated by DnsTransaction.
188 DCHECK_EQ(result_qtype, response.GetSingleQType());
189
190 AliasMap aliases;
191 for (unsigned i = 0; i < response.answer_count(); ++i) {
192 std::unique_ptr<const RecordParsed> record =
193 RecordParsed::CreateFrom(&parser, base::Time::Now());
194
195 if (!record)
196 return ExtractionError::kMalformedRecord;
197
198 DCHECK_NE(result_qtype, dns_protocol::kTypeCNAME);
199 if (record->klass() == dns_protocol::kClassIN &&
200 record->type() == dns_protocol::kTypeCNAME) {
201 // Per RFC2181, multiple CNAME records are not allowed for the same name.
202 if (aliases.find(record->name()) != aliases.end())
203 return ExtractionError::kMultipleCnames;
204
205 const CnameRecordRdata* cname_data = record->rdata<CnameRecordRdata>();
206 if (!cname_data)
207 return ExtractionError::kMalformedCname;
208
209 base::TimeDelta ttl = base::Seconds(record->ttl());
210 response_ttl =
211 std::min(response_ttl.value_or(base::TimeDelta::Max()), ttl);
212
213 bool added = aliases.emplace(record->name(), cname_data->cname()).second;
214 DCHECK(added);
215 } else if (record->klass() == dns_protocol::kClassIN &&
216 record->type() == result_qtype) {
217 base::TimeDelta ttl = base::Seconds(record->ttl());
218 response_ttl =
219 std::min(response_ttl.value_or(base::TimeDelta::Max()), ttl);
220
221 records.push_back(std::move(record));
222 }
223 }
224
225 ExtractionError name_and_alias_validation_error =
226 ValidateNamesAndAliases(response.GetSingleDottedName(), aliases, records);
227 if (name_and_alias_validation_error != ExtractionError::kOk)
228 return name_and_alias_validation_error;
229
230 // For NXDOMAIN or NODATA (NOERROR with 0 answers), attempt to find a TTL
231 // via an SOA record.
232 if (response.rcode() == dns_protocol::kRcodeNXDOMAIN ||
233 (response.answer_count() == 0 &&
234 response.rcode() == dns_protocol::kRcodeNOERROR)) {
235 bool soa_found = false;
236 for (unsigned i = 0; i < response.authority_count(); ++i) {
237 DnsResourceRecord record;
238 if (parser.ReadRecord(&record) && record.type == dns_protocol::kTypeSOA) {
239 soa_found = true;
240 base::TimeDelta ttl = base::Seconds(record.ttl);
241 response_ttl =
242 std::min(response_ttl.value_or(base::TimeDelta::Max()), ttl);
243 }
244 }
245
246 // Per RFC2308, section 5, never cache negative results unless an SOA
247 // record is found.
248 if (!soa_found)
249 response_ttl.reset();
250 }
251
252 for (unsigned i = 0; i < response.additional_answer_count(); ++i) {
253 std::unique_ptr<const RecordParsed> record =
254 RecordParsed::CreateFrom(&parser, base::Time::Now());
255 if (record && record->klass() == dns_protocol::kClassIN &&
256 record->type() == dns_protocol::kTypeHttps) {
257 bool is_unsolicited = result_qtype != dns_protocol::kTypeHttps;
258 SaveMetricsForAdditionalHttpsRecord(*record, is_unsolicited);
259 }
260 }
261
262 *out_records = std::move(records);
263 *out_response_ttl = response_ttl;
264
265 if (out_aliases) {
266 out_aliases->clear();
267 for (const auto& alias : aliases) {
268 std::string canonicalized_alias =
269 dns_names_util::UrlCanonicalizeNameIfAble(alias.second);
270 if (dns_names_util::IsValidDnsRecordName(canonicalized_alias)) {
271 out_aliases->insert(std::move(canonicalized_alias));
272 }
273 }
274 std::string canonicalized_query = dns_names_util::UrlCanonicalizeNameIfAble(
275 response.GetSingleDottedName());
276 if (dns_names_util::IsValidDnsRecordName(canonicalized_query)) {
277 out_aliases->insert(std::move(canonicalized_query));
278 }
279 }
280
281 return ExtractionError::kOk;
282 }
283
ExtractAddressResults(const DnsResponse & response,uint16_t address_qtype,HostCache::Entry * out_results)284 ExtractionError ExtractAddressResults(const DnsResponse& response,
285 uint16_t address_qtype,
286 HostCache::Entry* out_results) {
287 DCHECK_EQ(response.question_count(), 1u);
288 DCHECK(address_qtype == dns_protocol::kTypeA ||
289 address_qtype == dns_protocol::kTypeAAAA);
290 DCHECK(out_results);
291
292 std::vector<std::unique_ptr<const RecordParsed>> records;
293 absl::optional<base::TimeDelta> response_ttl;
294 std::set<std::string> aliases;
295 ExtractionError extraction_error = ExtractResponseRecords(
296 response, address_qtype, &records, &response_ttl, &aliases);
297
298 if (extraction_error != ExtractionError::kOk) {
299 *out_results = HostCache::Entry(ERR_DNS_MALFORMED_RESPONSE,
300 HostCache::Entry::SOURCE_DNS);
301 return extraction_error;
302 }
303
304 std::vector<IPEndPoint> ip_endpoints;
305 std::string canonical_name;
306 for (const auto& record : records) {
307 if (ip_endpoints.empty())
308 canonical_name = record->name();
309
310 // Expect that ExtractResponseRecords validates that all results correctly
311 // have the same name.
312 DCHECK(base::EqualsCaseInsensitiveASCII(canonical_name, record->name()))
313 << "canonical_name: " << canonical_name
314 << "\nrecord->name(): " << record->name();
315
316 IPAddress address;
317 if (address_qtype == dns_protocol::kTypeA) {
318 const ARecordRdata* rdata = record->rdata<ARecordRdata>();
319 address = rdata->address();
320 DCHECK(address.IsIPv4());
321 } else {
322 DCHECK_EQ(address_qtype, dns_protocol::kTypeAAAA);
323 const AAAARecordRdata* rdata = record->rdata<AAAARecordRdata>();
324 address = rdata->address();
325 DCHECK(address.IsIPv6());
326 }
327 ip_endpoints.emplace_back(address, /*port=*/0);
328 }
329 int error_result = ip_endpoints.empty() ? ERR_NAME_NOT_RESOLVED : OK;
330
331 HostCache::Entry results(error_result, std::move(ip_endpoints),
332 std::move(aliases), HostCache::Entry::SOURCE_DNS,
333 response_ttl);
334
335 if (!canonical_name.empty()) {
336 results.set_canonical_names(std::set<std::string>({canonical_name}));
337 }
338
339 *out_results = std::move(results);
340 return ExtractionError::kOk;
341 }
342
ExtractTxtResults(const DnsResponse & response,HostCache::Entry * out_results)343 ExtractionError ExtractTxtResults(const DnsResponse& response,
344 HostCache::Entry* out_results) {
345 DCHECK(out_results);
346
347 std::vector<std::unique_ptr<const RecordParsed>> records;
348 absl::optional<base::TimeDelta> response_ttl;
349 ExtractionError extraction_error =
350 ExtractResponseRecords(response, dns_protocol::kTypeTXT, &records,
351 &response_ttl, nullptr /* out_aliases */);
352
353 if (extraction_error != ExtractionError::kOk) {
354 *out_results = HostCache::Entry(ERR_DNS_MALFORMED_RESPONSE,
355 HostCache::Entry::SOURCE_DNS);
356 return extraction_error;
357 }
358
359 std::vector<std::string> text_records;
360 for (const auto& record : records) {
361 const TxtRecordRdata* rdata = record->rdata<net::TxtRecordRdata>();
362 text_records.insert(text_records.end(), rdata->texts().begin(),
363 rdata->texts().end());
364 }
365
366 *out_results = HostCache::Entry(
367 text_records.empty() ? ERR_NAME_NOT_RESOLVED : OK,
368 std::move(text_records), HostCache::Entry::SOURCE_DNS, response_ttl);
369 return ExtractionError::kOk;
370 }
371
ExtractPointerResults(const DnsResponse & response,HostCache::Entry * out_results)372 ExtractionError ExtractPointerResults(const DnsResponse& response,
373 HostCache::Entry* out_results) {
374 DCHECK(out_results);
375
376 std::vector<std::unique_ptr<const RecordParsed>> records;
377 absl::optional<base::TimeDelta> response_ttl;
378 ExtractionError extraction_error =
379 ExtractResponseRecords(response, dns_protocol::kTypePTR, &records,
380 &response_ttl, nullptr /* out_aliases */);
381
382 if (extraction_error != ExtractionError::kOk) {
383 *out_results = HostCache::Entry(ERR_DNS_MALFORMED_RESPONSE,
384 HostCache::Entry::SOURCE_DNS);
385 return extraction_error;
386 }
387
388 std::vector<HostPortPair> pointers;
389 for (const auto& record : records) {
390 const PtrRecordRdata* rdata = record->rdata<net::PtrRecordRdata>();
391 std::string pointer = rdata->ptrdomain();
392
393 // Skip pointers to the root domain.
394 if (!pointer.empty())
395 pointers.emplace_back(std::move(pointer), 0);
396 }
397
398 *out_results = HostCache::Entry(pointers.empty() ? ERR_NAME_NOT_RESOLVED : OK,
399 std::move(pointers),
400 HostCache::Entry::SOURCE_DNS, response_ttl);
401 return ExtractionError::kOk;
402 }
403
ExtractServiceResults(const DnsResponse & response,HostCache::Entry * out_results)404 ExtractionError ExtractServiceResults(const DnsResponse& response,
405 HostCache::Entry* out_results) {
406 DCHECK(out_results);
407
408 std::vector<std::unique_ptr<const RecordParsed>> records;
409 absl::optional<base::TimeDelta> response_ttl;
410 ExtractionError extraction_error =
411 ExtractResponseRecords(response, dns_protocol::kTypeSRV, &records,
412 &response_ttl, nullptr /* out_aliases */);
413
414 if (extraction_error != ExtractionError::kOk) {
415 *out_results = HostCache::Entry(ERR_DNS_MALFORMED_RESPONSE,
416 HostCache::Entry::SOURCE_DNS);
417 return extraction_error;
418 }
419
420 std::vector<const SrvRecordRdata*> fitered_rdatas;
421 for (const auto& record : records) {
422 const SrvRecordRdata* rdata = record->rdata<net::SrvRecordRdata>();
423
424 // Skip pointers to the root domain.
425 if (!rdata->target().empty())
426 fitered_rdatas.push_back(rdata);
427 }
428
429 std::vector<HostPortPair> ordered_service_targets =
430 SortServiceTargets(fitered_rdatas);
431
432 *out_results = HostCache::Entry(
433 ordered_service_targets.empty() ? ERR_NAME_NOT_RESOLVED : OK,
434 std::move(ordered_service_targets), HostCache::Entry::SOURCE_DNS,
435 response_ttl);
436 return ExtractionError::kOk;
437 }
438
UnwrapRecordPtr(const std::unique_ptr<const RecordParsed> & ptr)439 const RecordParsed* UnwrapRecordPtr(
440 const std::unique_ptr<const RecordParsed>& ptr) {
441 return ptr.get();
442 }
443
RecordIsAlias(const RecordParsed * record)444 bool RecordIsAlias(const RecordParsed* record) {
445 DCHECK(record->rdata<HttpsRecordRdata>());
446 return record->rdata<HttpsRecordRdata>()->IsAlias();
447 }
448
ExtractHttpsResults(const DnsResponse & response,base::StringPiece original_domain_name,uint16_t request_port,HostCache::Entry * out_results)449 ExtractionError ExtractHttpsResults(const DnsResponse& response,
450 base::StringPiece original_domain_name,
451 uint16_t request_port,
452 HostCache::Entry* out_results) {
453 DCHECK(!original_domain_name.empty());
454 DCHECK(out_results);
455
456 absl::optional<base::TimeDelta> response_ttl;
457 std::vector<std::unique_ptr<const RecordParsed>> records;
458 ExtractionError extraction_error =
459 ExtractResponseRecords(response, dns_protocol::kTypeHttps, &records,
460 &response_ttl, nullptr /* out_aliases */);
461
462 if (extraction_error != ExtractionError::kOk) {
463 *out_results = HostCache::Entry(ERR_DNS_MALFORMED_RESPONSE,
464 HostCache::Entry::SOURCE_DNS);
465 return extraction_error;
466 }
467
468 std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> results;
469 std::vector<bool> record_compatibility;
470 bool default_alpn_found = false;
471 #if DCHECK_IS_ON()
472 std::string canonical_name;
473 #endif // DCHECK_IS_ON()
474 for (const auto& record : records) {
475 #if DCHECK_IS_ON()
476 if (canonical_name.empty()) {
477 canonical_name = record->name();
478 } else {
479 DCHECK(record->name() == canonical_name);
480 }
481 #endif // DCHECK_IS_ON()
482
483 const HttpsRecordRdata* rdata = record->rdata<HttpsRecordRdata>();
484 DCHECK(rdata);
485
486 // Chrome does not yet support alias records.
487 if (rdata->IsAlias()) {
488 // Alias records are always considered compatible because they do not
489 // support "mandatory" params.
490 record_compatibility.push_back(true);
491 continue;
492 }
493
494 const ServiceFormHttpsRecordRdata* service = rdata->AsServiceForm();
495 record_compatibility.push_back(service->IsCompatible());
496
497 // Ignore services incompatible with Chrome's HTTPS record parser.
498 // draft-ietf-dnsop-svcb-https-08#section-8
499 if (!service->IsCompatible())
500 continue;
501
502 base::StringPiece target_name = service->service_name().empty()
503 ? record->name()
504 : service->service_name();
505
506 // Chrome does not yet support followup queries. So only support services at
507 // the original domain name or the canonical name (the record name).
508 // Note: HostCache::Entry::GetEndpoints() will not return metadatas which
509 // target name is different from the canonical name of A/AAAA query results.
510 if ((target_name != original_domain_name) &&
511 (target_name != record->name())) {
512 continue;
513 }
514
515 // Ignore services at a different port from the request port. Chrome does
516 // not yet support endpoints diverging by port. Note that before supporting
517 // port redirects, Chrome must ensure redirects to the "bad port list" are
518 // disallowed. Unclear if such logic would belong here or in socket
519 // connection logic.
520 if (service->port().has_value() && service->port().value() != request_port)
521 continue;
522
523 ConnectionEndpointMetadata metadata;
524
525 metadata.supported_protocol_alpns = service->alpn_ids();
526 if (service->default_alpn() &&
527 !base::Contains(metadata.supported_protocol_alpns,
528 dns_protocol::kHttpsServiceDefaultAlpn)) {
529 metadata.supported_protocol_alpns.push_back(
530 dns_protocol::kHttpsServiceDefaultAlpn);
531 }
532
533 // Services with no supported ALPNs (those with "no-default-alpn" and no or
534 // empty "alpn") are not self-consistent and are rejected.
535 // draft-ietf-dnsop-svcb-https-08#section-7.1.1 and
536 // draft-ietf-dnsop-svcb-https-08#section-2.4.3.
537 if (metadata.supported_protocol_alpns.empty())
538 continue;
539
540 metadata.ech_config_list = ConnectionEndpointMetadata::EchConfigList(
541 service->ech_config().cbegin(), service->ech_config().cend());
542
543 metadata.target_name = base::ToLowerASCII(target_name);
544
545 results.emplace(service->priority(), std::move(metadata));
546
547 if (service->default_alpn())
548 default_alpn_found = true;
549 }
550
551 // Ignore all records if any are an alias record. Chrome does not yet support
552 // alias records, but aliases take precedence over any other records.
553 if (base::ranges::any_of(records, &RecordIsAlias, &UnwrapRecordPtr)) {
554 records.clear();
555 results.clear();
556 }
557
558 // Ignore all records if they all mark "no-default-alpn". Domains should
559 // always provide at least one endpoint allowing default ALPN to ensure a
560 // reasonable expectation of connection success.
561 // draft-ietf-dnsop-svcb-https-08#section-7.1.2
562 if (!default_alpn_found) {
563 records.clear();
564 results.clear();
565 }
566
567 *out_results = HostCache::Entry(results.empty() ? ERR_NAME_NOT_RESOLVED : OK,
568 std::move(results),
569 HostCache::Entry::SOURCE_DNS, response_ttl);
570 out_results->set_https_record_compatibility(std::move(record_compatibility));
571 DCHECK_EQ(extraction_error, ExtractionError::kOk);
572 return extraction_error;
573 }
574
575 } // namespace
576
DnsResponseResultExtractor(const DnsResponse * response)577 DnsResponseResultExtractor::DnsResponseResultExtractor(
578 const DnsResponse* response)
579 : response_(response) {
580 DCHECK(response_);
581 }
582
583 DnsResponseResultExtractor::~DnsResponseResultExtractor() = default;
584
585 DnsResponseResultExtractor::ExtractionError
ExtractDnsResults(DnsQueryType query_type,base::StringPiece original_domain_name,uint16_t request_port,HostCache::Entry * out_results) const586 DnsResponseResultExtractor::ExtractDnsResults(
587 DnsQueryType query_type,
588 base::StringPiece original_domain_name,
589 uint16_t request_port,
590 HostCache::Entry* out_results) const {
591 DCHECK(!original_domain_name.empty());
592 DCHECK(out_results);
593
594 switch (query_type) {
595 case DnsQueryType::UNSPECIFIED:
596 // Should create multiple transactions with specified types.
597 NOTREACHED();
598 return ExtractionError::kUnexpected;
599 case DnsQueryType::A:
600 case DnsQueryType::AAAA:
601 return ExtractAddressResults(*response_, DnsQueryTypeToQtype(query_type),
602 out_results);
603 case DnsQueryType::TXT:
604 return ExtractTxtResults(*response_, out_results);
605 case DnsQueryType::PTR:
606 return ExtractPointerResults(*response_, out_results);
607 case DnsQueryType::SRV:
608 return ExtractServiceResults(*response_, out_results);
609 case DnsQueryType::HTTPS:
610 return ExtractHttpsResults(*response_, original_domain_name, request_port,
611 out_results);
612 }
613 }
614
615 // static
CreateEmptyResult(DnsQueryType query_type)616 HostCache::Entry DnsResponseResultExtractor::CreateEmptyResult(
617 DnsQueryType query_type) {
618 if (query_type != DnsQueryType::HTTPS) {
619 // Currently only used for HTTPS.
620 NOTIMPLEMENTED();
621 return HostCache::Entry(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
622 }
623
624 return HostCache::Entry(ERR_NAME_NOT_RESOLVED, std::vector<bool>(),
625 HostCache::Entry::SOURCE_DNS);
626 }
627
628 } // namespace net
629