• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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