• 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 <optional>
8 #include <string>
9 #include <utility>
10 #include <vector>
11 
12 #include "base/ranges/algorithm.h"
13 #include "base/test/simple_test_clock.h"
14 #include "base/test/simple_test_tick_clock.h"
15 #include "base/time/time.h"
16 #include "net/base/connection_endpoint_metadata_test_util.h"
17 #include "net/base/host_port_pair.h"
18 #include "net/base/ip_address.h"
19 #include "net/base/ip_endpoint.h"
20 #include "net/base/net_errors.h"
21 #include "net/dns/dns_query.h"
22 #include "net/dns/dns_response.h"
23 #include "net/dns/dns_test_util.h"
24 #include "net/dns/host_cache.h"
25 #include "net/dns/host_resolver_internal_result.h"
26 #include "net/dns/host_resolver_internal_result_test_util.h"
27 #include "net/dns/host_resolver_results_test_util.h"
28 #include "net/dns/public/dns_protocol.h"
29 #include "net/dns/public/dns_query_type.h"
30 #include "net/test/gtest_util.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33 
34 namespace net {
35 namespace {
36 
37 using ::testing::AllOf;
38 using ::testing::ElementsAre;
39 using ::testing::ElementsAreArray;
40 using ::testing::Eq;
41 using ::testing::IsEmpty;
42 using ::testing::Ne;
43 using ::testing::Optional;
44 using ::testing::Pair;
45 using ::testing::Pointee;
46 using ::testing::ResultOf;
47 using ::testing::SizeIs;
48 using ::testing::UnorderedElementsAre;
49 
50 using ExtractionError = DnsResponseResultExtractor::ExtractionError;
51 using ResultsOrError = DnsResponseResultExtractor::ResultsOrError;
52 
53 constexpr HostResolverInternalResult::Source kDnsSource =
54     HostResolverInternalResult::Source::kDns;
55 
56 class DnsResponseResultExtractorTest : public ::testing::Test {
57  protected:
58   base::SimpleTestClock clock_;
59   base::SimpleTestTickClock tick_clock_;
60 };
61 
TEST_F(DnsResponseResultExtractorTest,ExtractsSingleARecord)62 TEST_F(DnsResponseResultExtractorTest, ExtractsSingleARecord) {
63   constexpr char kName[] = "address.test";
64   const IPAddress kExpected(192, 168, 0, 1);
65 
66   DnsResponse response = BuildTestDnsAddressResponse(kName, kExpected);
67   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
68 
69   ResultsOrError results =
70       extractor.ExtractDnsResults(DnsQueryType::A,
71                                   /*original_domain_name=*/kName,
72                                   /*request_port=*/0);
73 
74   ASSERT_TRUE(results.has_value());
75   EXPECT_THAT(results.value(),
76               ElementsAre(Pointee(ExpectHostResolverInternalDataResult(
77                   kName, DnsQueryType::A, kDnsSource,
78                   /*expiration_matcher=*/Ne(std::nullopt),
79                   /*timed_expiration_matcher=*/Ne(std::nullopt),
80                   ElementsAre(IPEndPoint(kExpected, /*port=*/0))))));
81 }
82 
TEST_F(DnsResponseResultExtractorTest,ExtractsSingleAAAARecord)83 TEST_F(DnsResponseResultExtractorTest, ExtractsSingleAAAARecord) {
84   constexpr char kName[] = "address.test";
85 
86   IPAddress expected;
87   CHECK(expected.AssignFromIPLiteral("2001:4860:4860::8888"));
88 
89   DnsResponse response = BuildTestDnsAddressResponse(kName, expected);
90   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
91 
92   ResultsOrError results =
93       extractor.ExtractDnsResults(DnsQueryType::AAAA,
94                                   /*original_domain_name=*/kName,
95                                   /*request_port=*/0);
96 
97   ASSERT_TRUE(results.has_value());
98   EXPECT_THAT(results.value(),
99               ElementsAre(Pointee(ExpectHostResolverInternalDataResult(
100                   kName, DnsQueryType::AAAA, kDnsSource,
101                   /*expiration_matcher=*/Ne(std::nullopt),
102                   /*timed_expiration_matcher=*/Ne(std::nullopt),
103                   ElementsAre(IPEndPoint(expected, /*port=*/0))))));
104 }
105 
TEST_F(DnsResponseResultExtractorTest,ExtractsSingleARecordWithCname)106 TEST_F(DnsResponseResultExtractorTest, ExtractsSingleARecordWithCname) {
107   const IPAddress kExpected(192, 168, 0, 1);
108   constexpr char kName[] = "address.test";
109   constexpr char kCanonicalName[] = "alias.test";
110 
111   DnsResponse response =
112       BuildTestDnsAddressResponseWithCname(kName, kExpected, kCanonicalName);
113   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
114 
115   ResultsOrError results =
116       extractor.ExtractDnsResults(DnsQueryType::A,
117                                   /*original_domain_name=*/kName,
118                                   /*request_port=*/0);
119 
120   ASSERT_TRUE(results.has_value());
121   EXPECT_THAT(
122       results.value(),
123       UnorderedElementsAre(
124           Pointee(ExpectHostResolverInternalDataResult(
125               kCanonicalName, DnsQueryType::A, kDnsSource,
126               /*expiration_matcher=*/Ne(std::nullopt),
127               /*timed_expiration_matcher=*/Ne(std::nullopt),
128               ElementsAre(IPEndPoint(kExpected, /*port=*/0)))),
129           Pointee(ExpectHostResolverInternalAliasResult(
130               kName, DnsQueryType::A, kDnsSource,
131               /*expiration_matcher=*/Ne(std::nullopt),
132               /*timed_expiration_matcher=*/Ne(std::nullopt), kCanonicalName))));
133 }
134 
TEST_F(DnsResponseResultExtractorTest,ExtractsARecordsWithCname)135 TEST_F(DnsResponseResultExtractorTest, ExtractsARecordsWithCname) {
136   constexpr char kName[] = "addresses.test";
137 
138   DnsResponse response = BuildTestDnsResponse(
139       "addresses.test", dns_protocol::kTypeA,
140       {
141           BuildTestAddressRecord("alias.test", IPAddress(74, 125, 226, 179)),
142           BuildTestAddressRecord("alias.test", IPAddress(74, 125, 226, 180)),
143           BuildTestCnameRecord(kName, "alias.test"),
144           BuildTestAddressRecord("alias.test", IPAddress(74, 125, 226, 176)),
145           BuildTestAddressRecord("alias.test", IPAddress(74, 125, 226, 177)),
146           BuildTestAddressRecord("alias.test", IPAddress(74, 125, 226, 178)),
147       });
148   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
149 
150   ResultsOrError results =
151       extractor.ExtractDnsResults(DnsQueryType::A,
152                                   /*original_domain_name=*/kName,
153                                   /*request_port=*/0);
154 
155   ASSERT_TRUE(results.has_value());
156   EXPECT_THAT(
157       results.value(),
158       UnorderedElementsAre(
159           Pointee(ExpectHostResolverInternalDataResult(
160               "alias.test", DnsQueryType::A, kDnsSource,
161               /*expiration_matcher=*/Ne(std::nullopt),
162               /*timed_expiration_matcher=*/Ne(std::nullopt),
163               UnorderedElementsAre(
164                   IPEndPoint(IPAddress(74, 125, 226, 179), /*port=*/0),
165                   IPEndPoint(IPAddress(74, 125, 226, 180), /*port=*/0),
166                   IPEndPoint(IPAddress(74, 125, 226, 176), /*port=*/0),
167                   IPEndPoint(IPAddress(74, 125, 226, 177), /*port=*/0),
168                   IPEndPoint(IPAddress(74, 125, 226, 178), /*port=*/0)))),
169           Pointee(ExpectHostResolverInternalAliasResult(
170               kName, DnsQueryType::A, kDnsSource,
171               /*expiration_matcher=*/Ne(std::nullopt),
172               /*timed_expiration_matcher=*/Ne(std::nullopt), "alias.test"))));
173 }
174 
TEST_F(DnsResponseResultExtractorTest,ExtractsNxdomainAResponses)175 TEST_F(DnsResponseResultExtractorTest, ExtractsNxdomainAResponses) {
176   constexpr char kName[] = "address.test";
177   constexpr auto kTtl = base::Hours(2);
178 
179   DnsResponse response = BuildTestDnsResponse(
180       kName, dns_protocol::kTypeA, /*answers=*/{},
181       /*authority=*/
182       {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)},
183       /*additional=*/{}, dns_protocol::kRcodeNXDOMAIN);
184   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
185 
186   ResultsOrError results =
187       extractor.ExtractDnsResults(DnsQueryType::A,
188                                   /*original_domain_name=*/kName,
189                                   /*request_port=*/0);
190 
191   ASSERT_TRUE(results.has_value());
192   EXPECT_THAT(results.value(),
193               ElementsAre(Pointee(ExpectHostResolverInternalErrorResult(
194                   kName, DnsQueryType::A, kDnsSource,
195                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kTtl),
196                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kTtl),
197                   ERR_NAME_NOT_RESOLVED))));
198 }
199 
TEST_F(DnsResponseResultExtractorTest,ExtractsNodataAResponses)200 TEST_F(DnsResponseResultExtractorTest, ExtractsNodataAResponses) {
201   constexpr char kName[] = "address.test";
202   constexpr auto kTtl = base::Minutes(15);
203 
204   DnsResponse response = BuildTestDnsResponse(
205       kName, dns_protocol::kTypeA, /*answers=*/{},
206       /*authority=*/
207       {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)});
208   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
209 
210   ResultsOrError results =
211       extractor.ExtractDnsResults(DnsQueryType::A,
212                                   /*original_domain_name=*/kName,
213                                   /*request_port=*/0);
214 
215   ASSERT_TRUE(results.has_value());
216   EXPECT_THAT(results.value(),
217               ElementsAre(Pointee(ExpectHostResolverInternalErrorResult(
218                   kName, DnsQueryType::A, kDnsSource,
219                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kTtl),
220                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kTtl),
221                   ERR_NAME_NOT_RESOLVED))));
222 }
223 
TEST_F(DnsResponseResultExtractorTest,ExtractsNodataAResponsesWithoutTtl)224 TEST_F(DnsResponseResultExtractorTest, ExtractsNodataAResponsesWithoutTtl) {
225   constexpr char kName[] = "address.test";
226 
227   // Response without a TTL-containing SOA record.
228   DnsResponse response =
229       BuildTestDnsResponse(kName, dns_protocol::kTypeA, /*answers=*/{});
230   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
231 
232   ResultsOrError results =
233       extractor.ExtractDnsResults(DnsQueryType::A,
234                                   /*original_domain_name=*/kName,
235                                   /*request_port=*/0);
236 
237   // Expect empty result because not cacheable.
238   ASSERT_TRUE(results.has_value());
239   EXPECT_THAT(results.value(), IsEmpty());
240 }
241 
TEST_F(DnsResponseResultExtractorTest,RejectsMalformedARecord)242 TEST_F(DnsResponseResultExtractorTest, RejectsMalformedARecord) {
243   constexpr char kName[] = "address.test";
244 
245   DnsResponse response = BuildTestDnsResponse(
246       kName, dns_protocol::kTypeA,
247       {BuildTestDnsRecord(kName, dns_protocol::kTypeA,
248                           "malformed rdata")} /* answers */);
249   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
250 
251   EXPECT_EQ(extractor
252                 .ExtractDnsResults(DnsQueryType::A,
253                                    /*original_domain_name=*/kName,
254                                    /*request_port=*/0)
255                 .error_or(ExtractionError::kOk),
256             ExtractionError::kMalformedRecord);
257 }
258 
TEST_F(DnsResponseResultExtractorTest,RejectsWrongNameARecord)259 TEST_F(DnsResponseResultExtractorTest, RejectsWrongNameARecord) {
260   constexpr char kName[] = "address.test";
261 
262   DnsResponse response = BuildTestDnsAddressResponse(
263       kName, IPAddress(1, 2, 3, 4), "different.test");
264   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
265 
266   EXPECT_EQ(extractor
267                 .ExtractDnsResults(DnsQueryType::A,
268                                    /*original_domain_name=*/kName,
269                                    /*request_port=*/0)
270                 .error_or(ExtractionError::kOk),
271             ExtractionError::kNameMismatch);
272 }
273 
TEST_F(DnsResponseResultExtractorTest,IgnoresWrongTypeRecordsInAResponse)274 TEST_F(DnsResponseResultExtractorTest, IgnoresWrongTypeRecordsInAResponse) {
275   constexpr char kName[] = "address.test";
276 
277   DnsResponse response = BuildTestDnsResponse(
278       kName, dns_protocol::kTypeA,
279       {BuildTestTextRecord("address.test", {"foo"} /* text_strings */)});
280   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
281 
282   ResultsOrError results =
283       extractor.ExtractDnsResults(DnsQueryType::A,
284                                   /*original_domain_name=*/kName,
285                                   /*request_port=*/0);
286 
287   // Expect empty results because NODATA is not cacheable (due to no TTL).
288   ASSERT_TRUE(results.has_value());
289   EXPECT_THAT(results.value(), IsEmpty());
290 }
291 
TEST_F(DnsResponseResultExtractorTest,IgnoresWrongTypeRecordsMixedWithARecords)292 TEST_F(DnsResponseResultExtractorTest,
293        IgnoresWrongTypeRecordsMixedWithARecords) {
294   constexpr char kName[] = "address.test";
295   const IPAddress kExpected(8, 8, 8, 8);
296   constexpr auto kTtl = base::Days(3);
297 
298   DnsResponse response = BuildTestDnsResponse(
299       kName, dns_protocol::kTypeA,
300       {BuildTestTextRecord(kName, /*text_strings=*/{"foo"}, base::Hours(2)),
301        BuildTestAddressRecord(kName, kExpected, kTtl)});
302   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
303 
304   ResultsOrError results =
305       extractor.ExtractDnsResults(DnsQueryType::A,
306                                   /*original_domain_name=*/kName,
307                                   /*request_port=*/0);
308 
309   ASSERT_TRUE(results.has_value());
310   EXPECT_THAT(results.value(),
311               ElementsAre(Pointee(ExpectHostResolverInternalDataResult(
312                   kName, DnsQueryType::A, kDnsSource,
313                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kTtl),
314                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kTtl),
315                   ElementsAre(IPEndPoint(kExpected, /*port=*/0))))));
316 }
317 
TEST_F(DnsResponseResultExtractorTest,ExtractsMinATtl)318 TEST_F(DnsResponseResultExtractorTest, ExtractsMinATtl) {
319   constexpr char kName[] = "name.test";
320   constexpr base::TimeDelta kMinTtl = base::Minutes(4);
321 
322   DnsResponse response = BuildTestDnsResponse(
323       kName, dns_protocol::kTypeA,
324       {BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4), base::Hours(3)),
325        BuildTestAddressRecord(kName, IPAddress(2, 3, 4, 5), kMinTtl),
326        BuildTestAddressRecord(kName, IPAddress(3, 4, 5, 6),
327                               base::Minutes(15))});
328   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
329 
330   ResultsOrError results =
331       extractor.ExtractDnsResults(DnsQueryType::A,
332                                   /*original_domain_name=*/kName,
333                                   /*request_port=*/0);
334 
335   ASSERT_TRUE(results.has_value());
336   EXPECT_THAT(results.value(),
337               ElementsAre(Pointee(ExpectHostResolverInternalDataResult(
338                   kName, DnsQueryType::A, kDnsSource,
339                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kMinTtl),
340                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kMinTtl),
341                   /*endpoints_matcher=*/SizeIs(3)))));
342 }
343 
344 MATCHER_P(ContainsContiguousElements, elements, "") {
345   return base::ranges::search(arg, elements) != arg.end();
346 }
347 
TEST_F(DnsResponseResultExtractorTest,ExtractsTxtResponses)348 TEST_F(DnsResponseResultExtractorTest, ExtractsTxtResponses) {
349   constexpr char kName[] = "name.test";
350 
351   // Simulate two separate DNS records, each with multiple strings.
352   std::vector<std::string> foo_records = {"foo1", "foo2", "foo3"};
353   std::vector<std::string> bar_records = {"bar1", "bar2"};
354   std::vector<std::vector<std::string>> text_records = {foo_records,
355                                                         bar_records};
356 
357   DnsResponse response =
358       BuildTestDnsTextResponse(kName, std::move(text_records));
359   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
360 
361   ResultsOrError results =
362       extractor.ExtractDnsResults(DnsQueryType::TXT,
363                                   /*original_domain_name=*/kName,
364                                   /*request_port=*/0);
365 
366   ASSERT_TRUE(results.has_value());
367   // Order between separate DNS records is undefined, but each record should
368   // stay in order as that order may be meaningful.
369   EXPECT_THAT(
370       results.value(),
371       ElementsAre(Pointee(ExpectHostResolverInternalDataResult(
372           kName, DnsQueryType::TXT, kDnsSource,
373           /*expiration_matcher=*/Ne(std::nullopt),
374           /*timed_expiration_matcher=*/Ne(std::nullopt),
375           /*endpoints_matcher=*/IsEmpty(),
376           /*strings_matcher=*/
377           AllOf(UnorderedElementsAre("foo1", "foo2", "foo3", "bar1", "bar2"),
378                 ContainsContiguousElements(foo_records),
379                 ContainsContiguousElements(bar_records))))));
380 }
381 
TEST_F(DnsResponseResultExtractorTest,ExtractsNxdomainTxtResponses)382 TEST_F(DnsResponseResultExtractorTest, ExtractsNxdomainTxtResponses) {
383   constexpr char kName[] = "name.test";
384   constexpr auto kTtl = base::Days(4);
385 
386   DnsResponse response = BuildTestDnsResponse(
387       kName, dns_protocol::kTypeTXT, /*answers=*/{},
388       /*authority=*/
389       {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)},
390       /*additional=*/{}, dns_protocol::kRcodeNXDOMAIN);
391   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
392 
393   ResultsOrError results =
394       extractor.ExtractDnsResults(DnsQueryType::TXT,
395                                   /*original_domain_name=*/kName,
396                                   /*request_port=*/0);
397 
398   ASSERT_TRUE(results.has_value());
399   EXPECT_THAT(results.value(),
400               ElementsAre(Pointee(ExpectHostResolverInternalErrorResult(
401                   kName, DnsQueryType::TXT, kDnsSource,
402                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kTtl),
403                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kTtl),
404                   ERR_NAME_NOT_RESOLVED))));
405 }
406 
TEST_F(DnsResponseResultExtractorTest,ExtractsNodataTxtResponses)407 TEST_F(DnsResponseResultExtractorTest, ExtractsNodataTxtResponses) {
408   constexpr char kName[] = "name.test";
409   constexpr auto kTtl = base::Minutes(42);
410 
411   DnsResponse response = BuildTestDnsResponse(
412       kName, dns_protocol::kTypeTXT,
413       /*answers=*/{}, /*authority=*/
414       {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)});
415   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
416 
417   ResultsOrError results =
418       extractor.ExtractDnsResults(DnsQueryType::TXT,
419                                   /*original_domain_name=*/kName,
420                                   /*request_port=*/0);
421 
422   ASSERT_TRUE(results.has_value());
423   EXPECT_THAT(results.value(),
424               ElementsAre(Pointee(ExpectHostResolverInternalErrorResult(
425                   kName, DnsQueryType::TXT, kDnsSource,
426                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kTtl),
427                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kTtl),
428                   ERR_NAME_NOT_RESOLVED))));
429 }
430 
TEST_F(DnsResponseResultExtractorTest,RejectsMalformedTxtRecord)431 TEST_F(DnsResponseResultExtractorTest, RejectsMalformedTxtRecord) {
432   constexpr char kName[] = "name.test";
433 
434   DnsResponse response = BuildTestDnsResponse(
435       kName, dns_protocol::kTypeTXT,
436       {BuildTestDnsRecord(kName, dns_protocol::kTypeTXT,
437                           "malformed rdata")} /* answers */);
438   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
439 
440   EXPECT_EQ(extractor
441                 .ExtractDnsResults(DnsQueryType::TXT,
442                                    /*original_domain_name=*/kName,
443                                    /*request_port=*/0)
444                 .error_or(ExtractionError::kOk),
445             ExtractionError::kMalformedRecord);
446 }
447 
TEST_F(DnsResponseResultExtractorTest,RejectsWrongNameTxtRecord)448 TEST_F(DnsResponseResultExtractorTest, RejectsWrongNameTxtRecord) {
449   constexpr char kName[] = "name.test";
450 
451   DnsResponse response =
452       BuildTestDnsTextResponse(kName, {{"foo"}}, "different.test");
453   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
454 
455   EXPECT_EQ(extractor
456                 .ExtractDnsResults(DnsQueryType::TXT,
457                                    /*original_domain_name=*/kName,
458                                    /*request_port=*/0)
459                 .error_or(ExtractionError::kOk),
460             ExtractionError::kNameMismatch);
461 }
462 
TEST_F(DnsResponseResultExtractorTest,IgnoresWrongTypeTxtResponses)463 TEST_F(DnsResponseResultExtractorTest, IgnoresWrongTypeTxtResponses) {
464   constexpr char kName[] = "name.test";
465 
466   DnsResponse response = BuildTestDnsResponse(
467       kName, dns_protocol::kTypeTXT,
468       {BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4))});
469   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
470 
471   ResultsOrError results =
472       extractor.ExtractDnsResults(DnsQueryType::TXT,
473                                   /*original_domain_name=*/kName,
474                                   /*request_port=*/0);
475 
476   // Expect empty results because NODATA is not cacheable (due to no TTL).
477   ASSERT_TRUE(results.has_value());
478   EXPECT_THAT(results.value(), IsEmpty());
479 }
480 
TEST_F(DnsResponseResultExtractorTest,ExtractsMinTxtTtl)481 TEST_F(DnsResponseResultExtractorTest, ExtractsMinTxtTtl) {
482   constexpr char kName[] = "name.test";
483   constexpr base::TimeDelta kMinTtl = base::Minutes(4);
484 
485   DnsResponse response = BuildTestDnsResponse(
486       kName, dns_protocol::kTypeTXT,
487       {BuildTestTextRecord(kName, {"foo"}, base::Hours(3)),
488        BuildTestTextRecord(kName, {"bar"}, kMinTtl),
489        BuildTestTextRecord(kName, {"baz"}, base::Minutes(15))});
490   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
491 
492   ResultsOrError results =
493       extractor.ExtractDnsResults(DnsQueryType::TXT,
494                                   /*original_domain_name=*/kName,
495                                   /*request_port=*/0);
496 
497   ASSERT_TRUE(results.has_value());
498   EXPECT_THAT(results.value(),
499               ElementsAre(Pointee(ExpectHostResolverInternalDataResult(
500                   kName, DnsQueryType::TXT, kDnsSource,
501                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kMinTtl),
502                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kMinTtl),
503                   /*endpoints_matcher=*/IsEmpty(),
504                   /*strings_matcher=*/SizeIs(3)))));
505 }
506 
TEST_F(DnsResponseResultExtractorTest,ExtractsPtrResponses)507 TEST_F(DnsResponseResultExtractorTest, ExtractsPtrResponses) {
508   constexpr char kName[] = "name.test";
509 
510   DnsResponse response =
511       BuildTestDnsPointerResponse(kName, {"foo.com", "bar.com"});
512   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
513 
514   ResultsOrError results =
515       extractor.ExtractDnsResults(DnsQueryType::PTR,
516                                   /*original_domain_name=*/kName,
517                                   /*request_port=*/0);
518 
519   ASSERT_TRUE(results.has_value());
520   EXPECT_THAT(results.value(),
521               ElementsAre(Pointee(ExpectHostResolverInternalDataResult(
522                   kName, DnsQueryType::PTR, kDnsSource,
523                   /*expiration_matcher=*/Ne(std::nullopt),
524                   /*timed_expiration_matcher=*/Ne(std::nullopt),
525                   /*endpoints_matcher=*/IsEmpty(),
526                   /*strings_matcher=*/IsEmpty(),
527                   /*hosts_matcher=*/
528                   UnorderedElementsAre(HostPortPair("foo.com", 0),
529                                        HostPortPair("bar.com", 0))))));
530 }
531 
TEST_F(DnsResponseResultExtractorTest,ExtractsNxdomainPtrResponses)532 TEST_F(DnsResponseResultExtractorTest, ExtractsNxdomainPtrResponses) {
533   constexpr char kName[] = "name.test";
534   constexpr auto kTtl = base::Hours(5);
535 
536   DnsResponse response = BuildTestDnsResponse(
537       kName, dns_protocol::kTypePTR, /*answers=*/{},
538       /*authority=*/
539       {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)},
540       /*additional=*/{}, dns_protocol::kRcodeNXDOMAIN);
541   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
542 
543   ResultsOrError results =
544       extractor.ExtractDnsResults(DnsQueryType::PTR,
545                                   /*original_domain_name=*/kName,
546                                   /*request_port=*/0);
547 
548   ASSERT_TRUE(results.has_value());
549   EXPECT_THAT(results.value(),
550               ElementsAre(Pointee(ExpectHostResolverInternalErrorResult(
551                   kName, DnsQueryType::PTR, kDnsSource,
552                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kTtl),
553                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kTtl),
554                   ERR_NAME_NOT_RESOLVED))));
555 }
556 
TEST_F(DnsResponseResultExtractorTest,ExtractsNodataPtrResponses)557 TEST_F(DnsResponseResultExtractorTest, ExtractsNodataPtrResponses) {
558   constexpr char kName[] = "name.test";
559   constexpr auto kTtl = base::Minutes(50);
560 
561   DnsResponse response = BuildTestDnsResponse(
562       kName, dns_protocol::kTypePTR, /*answers=*/{},
563       /*authority=*/
564       {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)});
565   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
566 
567   ResultsOrError results =
568       extractor.ExtractDnsResults(DnsQueryType::PTR,
569                                   /*original_domain_name=*/kName,
570                                   /*request_port=*/0);
571 
572   ASSERT_TRUE(results.has_value());
573   EXPECT_THAT(results.value(),
574               ElementsAre(Pointee(ExpectHostResolverInternalErrorResult(
575                   kName, DnsQueryType::PTR, kDnsSource,
576                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kTtl),
577                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kTtl),
578                   ERR_NAME_NOT_RESOLVED))));
579 }
580 
TEST_F(DnsResponseResultExtractorTest,RejectsMalformedPtrRecord)581 TEST_F(DnsResponseResultExtractorTest, RejectsMalformedPtrRecord) {
582   constexpr char kName[] = "name.test";
583 
584   DnsResponse response = BuildTestDnsResponse(
585       kName, dns_protocol::kTypePTR,
586       {BuildTestDnsRecord(kName, dns_protocol::kTypePTR,
587                           "malformed rdata")} /* answers */);
588   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
589 
590   EXPECT_EQ(extractor
591                 .ExtractDnsResults(DnsQueryType::PTR,
592                                    /*original_domain_name=*/kName,
593                                    /*request_port=*/0)
594                 .error_or(ExtractionError::kOk),
595             ExtractionError::kMalformedRecord);
596 }
597 
TEST_F(DnsResponseResultExtractorTest,RejectsWrongNamePtrRecord)598 TEST_F(DnsResponseResultExtractorTest, RejectsWrongNamePtrRecord) {
599   constexpr char kName[] = "name.test";
600 
601   DnsResponse response = BuildTestDnsPointerResponse(
602       kName, {"foo.com", "bar.com"}, "different.test");
603   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
604 
605   EXPECT_EQ(extractor
606                 .ExtractDnsResults(DnsQueryType::PTR,
607                                    /*original_domain_name=*/kName,
608                                    /*request_port=*/0)
609                 .error_or(ExtractionError::kOk),
610             ExtractionError::kNameMismatch);
611 }
612 
TEST_F(DnsResponseResultExtractorTest,IgnoresWrongTypePtrResponses)613 TEST_F(DnsResponseResultExtractorTest, IgnoresWrongTypePtrResponses) {
614   constexpr char kName[] = "name.test";
615 
616   DnsResponse response = BuildTestDnsResponse(
617       kName, dns_protocol::kTypePTR,
618       {BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4))});
619   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
620 
621   ResultsOrError results =
622       extractor.ExtractDnsResults(DnsQueryType::PTR,
623                                   /*original_domain_name=*/kName,
624                                   /*request_port=*/0);
625 
626   // Expect empty results because NODATA is not cacheable (due to no TTL).
627   ASSERT_TRUE(results.has_value());
628   EXPECT_THAT(results.value(), IsEmpty());
629 }
630 
TEST_F(DnsResponseResultExtractorTest,ExtractsSrvResponses)631 TEST_F(DnsResponseResultExtractorTest, ExtractsSrvResponses) {
632   constexpr char kName[] = "name.test";
633 
634   const TestServiceRecord kRecord1 = {2, 3, 1223, "foo.com"};
635   const TestServiceRecord kRecord2 = {5, 10, 80, "bar.com"};
636   const TestServiceRecord kRecord3 = {5, 1, 5, "google.com"};
637   const TestServiceRecord kRecord4 = {2, 100, 12345, "chromium.org"};
638 
639   DnsResponse response = BuildTestDnsServiceResponse(
640       kName, {kRecord1, kRecord2, kRecord3, kRecord4});
641   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
642 
643   ResultsOrError results =
644       extractor.ExtractDnsResults(DnsQueryType::SRV,
645                                   /*original_domain_name=*/kName,
646                                   /*request_port=*/0);
647 
648   ASSERT_TRUE(results.has_value());
649   EXPECT_THAT(results.value(),
650               ElementsAre(Pointee(ExpectHostResolverInternalDataResult(
651                   kName, DnsQueryType::SRV, kDnsSource,
652                   /*expiration_matcher=*/Ne(std::nullopt),
653                   /*timed_expiration_matcher=*/Ne(std::nullopt),
654                   /*endpoints_matcher=*/IsEmpty(),
655                   /*strings_matcher=*/IsEmpty(),
656                   /*hosts_matcher=*/
657                   UnorderedElementsAre(HostPortPair("foo.com", 1223),
658                                        HostPortPair("bar.com", 80),
659                                        HostPortPair("google.com", 5),
660                                        HostPortPair("chromium.org", 12345))))));
661 
662   // Expect ordered by priority, and random within a priority.
663   std::vector<HostPortPair> result_hosts =
664       (*results.value().begin())->AsData().hosts();
665   auto priority2 =
666       std::vector<HostPortPair>(result_hosts.begin(), result_hosts.begin() + 2);
667   EXPECT_THAT(priority2, testing::UnorderedElementsAre(
668                              HostPortPair("foo.com", 1223),
669                              HostPortPair("chromium.org", 12345)));
670   auto priority5 =
671       std::vector<HostPortPair>(result_hosts.begin() + 2, result_hosts.end());
672   EXPECT_THAT(priority5,
673               testing::UnorderedElementsAre(HostPortPair("bar.com", 80),
674                                             HostPortPair("google.com", 5)));
675 }
676 
677 // 0-weight services are allowed. Ensure that we can handle such records,
678 // especially the case where all entries have weight 0.
TEST_F(DnsResponseResultExtractorTest,ExtractsZeroWeightSrvResponses)679 TEST_F(DnsResponseResultExtractorTest, ExtractsZeroWeightSrvResponses) {
680   constexpr char kName[] = "name.test";
681 
682   const TestServiceRecord kRecord1 = {5, 0, 80, "bar.com"};
683   const TestServiceRecord kRecord2 = {5, 0, 5, "google.com"};
684 
685   DnsResponse response =
686       BuildTestDnsServiceResponse(kName, {kRecord1, kRecord2});
687   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
688 
689   ResultsOrError results =
690       extractor.ExtractDnsResults(DnsQueryType::SRV,
691                                   /*original_domain_name=*/kName,
692                                   /*request_port=*/0);
693 
694   ASSERT_TRUE(results.has_value());
695   EXPECT_THAT(results.value(),
696               ElementsAre(Pointee(ExpectHostResolverInternalDataResult(
697                   kName, DnsQueryType::SRV, kDnsSource,
698                   /*expiration_matcher=*/Ne(std::nullopt),
699                   /*timed_expiration_matcher=*/Ne(std::nullopt),
700                   /*endpoints_matcher=*/IsEmpty(),
701                   /*strings_matcher=*/IsEmpty(),
702                   /*hosts_matcher=*/
703                   UnorderedElementsAre(HostPortPair("bar.com", 80),
704                                        HostPortPair("google.com", 5))))));
705 }
706 
TEST_F(DnsResponseResultExtractorTest,ExtractsNxdomainSrvResponses)707 TEST_F(DnsResponseResultExtractorTest, ExtractsNxdomainSrvResponses) {
708   constexpr char kName[] = "name.test";
709   constexpr auto kTtl = base::Days(7);
710 
711   DnsResponse response = BuildTestDnsResponse(
712       kName, dns_protocol::kTypeSRV, /*answers=*/{},
713       /*authority=*/
714       {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)},
715       /*additional=*/{}, dns_protocol::kRcodeNXDOMAIN);
716   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
717 
718   ResultsOrError results =
719       extractor.ExtractDnsResults(DnsQueryType::SRV,
720                                   /*original_domain_name=*/kName,
721                                   /*request_port=*/0);
722 
723   ASSERT_TRUE(results.has_value());
724   EXPECT_THAT(results.value(),
725               ElementsAre(Pointee(ExpectHostResolverInternalErrorResult(
726                   kName, DnsQueryType::SRV, kDnsSource,
727                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kTtl),
728                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kTtl),
729                   ERR_NAME_NOT_RESOLVED))));
730 }
731 
TEST_F(DnsResponseResultExtractorTest,ExtractsNodataSrvResponses)732 TEST_F(DnsResponseResultExtractorTest, ExtractsNodataSrvResponses) {
733   constexpr char kName[] = "name.test";
734   constexpr auto kTtl = base::Hours(12);
735 
736   DnsResponse response = BuildTestDnsResponse(
737       kName, dns_protocol::kTypeSRV, /*answers=*/{},
738       /*authority=*/
739       {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)});
740   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
741 
742   ResultsOrError results =
743       extractor.ExtractDnsResults(DnsQueryType::SRV,
744                                   /*original_domain_name=*/kName,
745                                   /*request_port=*/0);
746 
747   ASSERT_TRUE(results.has_value());
748   EXPECT_THAT(results.value(),
749               ElementsAre(Pointee(ExpectHostResolverInternalErrorResult(
750                   kName, DnsQueryType::SRV, kDnsSource,
751                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kTtl),
752                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kTtl),
753                   ERR_NAME_NOT_RESOLVED))));
754 }
755 
TEST_F(DnsResponseResultExtractorTest,RejectsMalformedSrvRecord)756 TEST_F(DnsResponseResultExtractorTest, RejectsMalformedSrvRecord) {
757   constexpr char kName[] = "name.test";
758 
759   DnsResponse response = BuildTestDnsResponse(
760       kName, dns_protocol::kTypeSRV,
761       {BuildTestDnsRecord(kName, dns_protocol::kTypeSRV,
762                           "malformed rdata")} /* answers */);
763   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
764 
765   EXPECT_EQ(extractor
766                 .ExtractDnsResults(DnsQueryType::SRV,
767                                    /*original_domain_name=*/kName,
768                                    /*request_port=*/0)
769                 .error_or(ExtractionError::kOk),
770             ExtractionError::kMalformedRecord);
771 }
772 
TEST_F(DnsResponseResultExtractorTest,RejectsWrongNameSrvRecord)773 TEST_F(DnsResponseResultExtractorTest, RejectsWrongNameSrvRecord) {
774   constexpr char kName[] = "name.test";
775 
776   const TestServiceRecord kRecord = {2, 3, 1223, "foo.com"};
777   DnsResponse response =
778       BuildTestDnsServiceResponse(kName, {kRecord}, "different.test");
779   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
780 
781   EXPECT_EQ(extractor
782                 .ExtractDnsResults(DnsQueryType::SRV,
783                                    /*original_domain_name=*/kName,
784                                    /*request_port=*/0)
785                 .error_or(ExtractionError::kOk),
786             ExtractionError::kNameMismatch);
787 }
788 
TEST_F(DnsResponseResultExtractorTest,IgnoresWrongTypeSrvResponses)789 TEST_F(DnsResponseResultExtractorTest, IgnoresWrongTypeSrvResponses) {
790   constexpr char kName[] = "name.test";
791 
792   DnsResponse response = BuildTestDnsResponse(
793       kName, dns_protocol::kTypeSRV,
794       {BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4))});
795   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
796 
797   ResultsOrError results =
798       extractor.ExtractDnsResults(DnsQueryType::SRV,
799                                   /*original_domain_name=*/kName,
800                                   /*request_port=*/0);
801 
802   // Expect empty results because NODATA is not cacheable (due to no TTL).
803   ASSERT_TRUE(results.has_value());
804   EXPECT_THAT(results.value(), IsEmpty());
805 }
806 
TEST_F(DnsResponseResultExtractorTest,ExtractsBasicHttpsResponses)807 TEST_F(DnsResponseResultExtractorTest, ExtractsBasicHttpsResponses) {
808   constexpr char kName[] = "https.test";
809   constexpr auto kTtl = base::Hours(12);
810 
811   DnsResponse response =
812       BuildTestDnsResponse(kName, dns_protocol::kTypeHttps,
813                            {BuildTestHttpsServiceRecord(kName,
814                                                         /*priority=*/4,
815                                                         /*service_name=*/".",
816                                                         /*params=*/{}, kTtl)});
817   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
818 
819   ResultsOrError results =
820       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
821                                   /*original_domain_name=*/kName,
822                                   /*request_port=*/0);
823 
824   ASSERT_TRUE(results.has_value());
825   EXPECT_THAT(
826       results.value(),
827       ElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
828           kName, DnsQueryType::HTTPS, kDnsSource,
829           Eq(tick_clock_.NowTicks() + kTtl), Eq(clock_.Now() + kTtl),
830           ElementsAre(
831               Pair(4, ExpectConnectionEndpointMetadata(
832                           ElementsAre(dns_protocol::kHttpsServiceDefaultAlpn),
833                           /*ech_config_list_matcher=*/IsEmpty(), kName)))))));
834 }
835 
TEST_F(DnsResponseResultExtractorTest,ExtractsComprehensiveHttpsResponses)836 TEST_F(DnsResponseResultExtractorTest, ExtractsComprehensiveHttpsResponses) {
837   constexpr char kName[] = "https.test";
838   constexpr char kAlpn[] = "foo";
839   constexpr uint8_t kEchConfig[] = "EEEEEEEEECH!";
840   constexpr auto kTtl = base::Hours(12);
841 
842   DnsResponse response = BuildTestDnsResponse(
843       kName, dns_protocol::kTypeHttps,
844       {BuildTestHttpsServiceRecord(
845            kName, /*priority=*/4,
846            /*service_name=*/".",
847            /*params=*/
848            {BuildTestHttpsServiceAlpnParam({kAlpn}),
849             BuildTestHttpsServiceEchConfigParam(kEchConfig)},
850            kTtl),
851        BuildTestHttpsServiceRecord(
852            kName, /*priority=*/3,
853            /*service_name=*/".",
854            /*params=*/
855            {BuildTestHttpsServiceAlpnParam({kAlpn}),
856             {dns_protocol::kHttpsServiceParamKeyNoDefaultAlpn, ""}},
857            /*ttl=*/base::Days(3))});
858   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
859 
860   ResultsOrError results =
861       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
862                                   /*original_domain_name=*/kName,
863                                   /*request_port=*/0);
864 
865   ASSERT_TRUE(results.has_value());
866   EXPECT_THAT(
867       results.value(),
868       ElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
869           kName, DnsQueryType::HTTPS, kDnsSource,
870           Eq(tick_clock_.NowTicks() + kTtl), Eq(clock_.Now() + kTtl),
871           ElementsAre(
872               Pair(3, ExpectConnectionEndpointMetadata(
873                           ElementsAre(kAlpn),
874                           /*ech_config_list_matcher=*/IsEmpty(), kName)),
875               Pair(4, ExpectConnectionEndpointMetadata(
876                           ElementsAre(kAlpn,
877                                       dns_protocol::kHttpsServiceDefaultAlpn),
878                           ElementsAreArray(kEchConfig), kName)))))));
879 }
880 
TEST_F(DnsResponseResultExtractorTest,IgnoresHttpsResponseWithJustAlias)881 TEST_F(DnsResponseResultExtractorTest, IgnoresHttpsResponseWithJustAlias) {
882   constexpr char kName[] = "https.test";
883   constexpr base::TimeDelta kTtl = base::Days(5);
884 
885   DnsResponse response = BuildTestDnsResponse(
886       kName, dns_protocol::kTypeHttps,
887       {BuildTestHttpsAliasRecord(kName, "alias.test", kTtl)});
888   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
889 
890   ResultsOrError results =
891       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
892                                   /*original_domain_name=*/kName,
893                                   /*request_port=*/0);
894 
895   // Expect empty metadata result to signify compatible HTTPS records with no
896   // data of use to Chrome. Still expect expiration from record, so the empty
897   // response can be cached.
898   ASSERT_TRUE(results.has_value());
899   EXPECT_THAT(
900       results.value(),
901       ElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
902           kName, DnsQueryType::HTTPS, kDnsSource,
903           /*expiration_matcher=*/Optional(tick_clock_.NowTicks() + kTtl),
904           /*timed_expiration_matcher=*/Optional(clock_.Now() + kTtl),
905           /*metadatas_matcher=*/IsEmpty()))));
906 }
907 
TEST_F(DnsResponseResultExtractorTest,IgnoresHttpsResponseWithAlias)908 TEST_F(DnsResponseResultExtractorTest, IgnoresHttpsResponseWithAlias) {
909   constexpr char kName[] = "https.test";
910   constexpr base::TimeDelta kLowestTtl = base::Minutes(32);
911 
912   DnsResponse response = BuildTestDnsResponse(
913       kName, dns_protocol::kTypeHttps,
914       {BuildTestHttpsServiceRecord(kName,
915                                    /*priority=*/4,
916                                    /*service_name=*/".",
917                                    /*params=*/{}, base::Days(1)),
918        BuildTestHttpsAliasRecord(kName, "alias.test", kLowestTtl)});
919   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
920 
921   ResultsOrError results =
922       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
923                                   /*original_domain_name=*/kName,
924                                   /*request_port=*/0);
925 
926   // Expect empty metadata result to signify compatible HTTPS records with no
927   // data of use to Chrome. Expiration should match lowest TTL from all
928   // compatible records.
929   ASSERT_TRUE(results.has_value());
930   EXPECT_THAT(
931       results.value(),
932       ElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
933           kName, DnsQueryType::HTTPS, kDnsSource,
934           /*expiration_matcher=*/Optional(tick_clock_.NowTicks() + kLowestTtl),
935           /*timed_expiration_matcher=*/Optional(clock_.Now() + kLowestTtl),
936           /*metadatas_matcher=*/IsEmpty()))));
937 }
938 
939 // Expect the entire response to be ignored if all HTTPS records have the
940 // "no-default-alpn" param.
TEST_F(DnsResponseResultExtractorTest,IgnoresHttpsResponseWithNoDefaultAlpn)941 TEST_F(DnsResponseResultExtractorTest, IgnoresHttpsResponseWithNoDefaultAlpn) {
942   constexpr char kName[] = "https.test";
943   constexpr base::TimeDelta kLowestTtl = base::Hours(3);
944 
945   DnsResponse response = BuildTestDnsResponse(
946       kName, dns_protocol::kTypeHttps,
947       {BuildTestHttpsServiceRecord(
948            kName, /*priority=*/4,
949            /*service_name=*/".",
950            /*params=*/
951            {BuildTestHttpsServiceAlpnParam({"foo1"}),
952             {dns_protocol::kHttpsServiceParamKeyNoDefaultAlpn, ""}},
953            kLowestTtl),
954        BuildTestHttpsServiceRecord(
955            kName, /*priority=*/5,
956            /*service_name=*/".",
957            /*params=*/
958            {BuildTestHttpsServiceAlpnParam({"foo2"}),
959             {dns_protocol::kHttpsServiceParamKeyNoDefaultAlpn, ""}},
960            base::Days(3))});
961   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
962 
963   ResultsOrError results =
964       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
965                                   /*original_domain_name=*/kName,
966                                   /*request_port=*/0);
967 
968   // Expect empty metadata result to signify compatible HTTPS records with no
969   // data of use to Chrome.
970   ASSERT_TRUE(results.has_value());
971   EXPECT_THAT(
972       results.value(),
973       ElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
974           kName, DnsQueryType::HTTPS, kDnsSource,
975           /*expiration_matcher=*/Optional(tick_clock_.NowTicks() + kLowestTtl),
976           /*timed_expiration_matcher=*/Optional(clock_.Now() + kLowestTtl),
977           /*metadatas_matcher=*/IsEmpty()))));
978 }
979 
980 // Unsupported/unknown HTTPS params are simply ignored if not marked mandatory.
TEST_F(DnsResponseResultExtractorTest,IgnoresUnsupportedParamsInHttpsRecord)981 TEST_F(DnsResponseResultExtractorTest, IgnoresUnsupportedParamsInHttpsRecord) {
982   constexpr char kName[] = "https.test";
983   constexpr uint16_t kMadeUpParamKey = 65500;  // From the private-use block.
984 
985   DnsResponse response = BuildTestDnsResponse(
986       kName, dns_protocol::kTypeHttps,
987       {BuildTestHttpsServiceRecord(kName, /*priority=*/4,
988                                    /*service_name=*/".",
989                                    /*params=*/
990                                    {{kMadeUpParamKey, "foo"}})});
991   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
992 
993   ResultsOrError results =
994       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
995                                   /*original_domain_name=*/kName,
996                                   /*request_port=*/0);
997 
998   ASSERT_TRUE(results.has_value());
999   EXPECT_THAT(
1000       results.value(),
1001       ElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
1002           kName, DnsQueryType::HTTPS, kDnsSource,
1003           /*expiration_matcher=*/Ne(std::nullopt),
1004           /*timed_expiration_matcher=*/Ne(std::nullopt),
1005           ElementsAre(
1006               Pair(4, ExpectConnectionEndpointMetadata(
1007                           ElementsAre(dns_protocol::kHttpsServiceDefaultAlpn),
1008                           /*ech_config_list_matcher=*/IsEmpty(), kName)))))));
1009 }
1010 
1011 // Entire record is dropped if an unsupported/unknown HTTPS param is marked
1012 // mandatory.
TEST_F(DnsResponseResultExtractorTest,IgnoresHttpsRecordWithUnsupportedMandatoryParam)1013 TEST_F(DnsResponseResultExtractorTest,
1014        IgnoresHttpsRecordWithUnsupportedMandatoryParam) {
1015   constexpr char kName[] = "https.test";
1016   constexpr uint16_t kMadeUpParamKey = 65500;  // From the private-use block.
1017   constexpr base::TimeDelta kTtl = base::Days(5);
1018 
1019   DnsResponse response = BuildTestDnsResponse(
1020       kName, dns_protocol::kTypeHttps,
1021       {BuildTestHttpsServiceRecord(
1022            kName, /*priority=*/4,
1023            /*service_name=*/".",
1024            /*params=*/
1025            {BuildTestHttpsServiceAlpnParam({"ignored_alpn"}),
1026             BuildTestHttpsServiceMandatoryParam({kMadeUpParamKey}),
1027             {kMadeUpParamKey, "foo"}},
1028            base::Hours(2)),
1029        BuildTestHttpsServiceRecord(
1030            kName, /*priority=*/5,
1031            /*service_name=*/".",
1032            /*params=*/{BuildTestHttpsServiceAlpnParam({"foo"})}, kTtl)});
1033   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1034 
1035   ResultsOrError results =
1036       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1037                                   /*original_domain_name=*/kName,
1038                                   /*request_port=*/0);
1039 
1040   ASSERT_TRUE(results.has_value());
1041 
1042   // Expect expiration to be derived only from non-ignored records.
1043   EXPECT_THAT(
1044       results.value(),
1045       ElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
1046           kName, DnsQueryType::HTTPS, kDnsSource,
1047           /*expiration_matcher=*/Optional(tick_clock_.NowTicks() + kTtl),
1048           /*timed_expiration_matcher=*/Optional(clock_.Now() + kTtl),
1049           ElementsAre(Pair(
1050               5, ExpectConnectionEndpointMetadata(
1051                      ElementsAre("foo", dns_protocol::kHttpsServiceDefaultAlpn),
1052                      /*ech_config_list_matcher=*/IsEmpty(), kName)))))));
1053 }
1054 
TEST_F(DnsResponseResultExtractorTest,ExtractsHttpsRecordWithMatchingServiceName)1055 TEST_F(DnsResponseResultExtractorTest,
1056        ExtractsHttpsRecordWithMatchingServiceName) {
1057   constexpr char kName[] = "https.test";
1058 
1059   DnsResponse response = BuildTestDnsResponse(
1060       kName, dns_protocol::kTypeHttps,
1061       {BuildTestHttpsServiceRecord(kName, /*priority=*/4,
1062                                    /*service_name=*/kName,
1063                                    /*params=*/
1064                                    {BuildTestHttpsServiceAlpnParam({"foo"})})});
1065   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1066 
1067   ResultsOrError results =
1068       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1069                                   /*original_domain_name=*/kName,
1070                                   /*request_port=*/0);
1071 
1072   ASSERT_TRUE(results.has_value());
1073   EXPECT_THAT(
1074       results.value(),
1075       ElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
1076           kName, DnsQueryType::HTTPS, kDnsSource,
1077           /*expiration_matcher=*/Ne(std::nullopt),
1078           /*timed_expiration_matcher=*/Ne(std::nullopt),
1079           ElementsAre(Pair(
1080               4, ExpectConnectionEndpointMetadata(
1081                      ElementsAre("foo", dns_protocol::kHttpsServiceDefaultAlpn),
1082                      /*ech_config_list_matcher=*/IsEmpty(), kName)))))));
1083 }
1084 
TEST_F(DnsResponseResultExtractorTest,ExtractsHttpsRecordWithMatchingDefaultServiceName)1085 TEST_F(DnsResponseResultExtractorTest,
1086        ExtractsHttpsRecordWithMatchingDefaultServiceName) {
1087   constexpr char kName[] = "https.test";
1088 
1089   DnsResponse response = BuildTestDnsResponse(
1090       kName, dns_protocol::kTypeHttps,
1091       {BuildTestHttpsServiceRecord(kName, /*priority=*/4,
1092                                    /*service_name=*/".",
1093                                    /*params=*/
1094                                    {BuildTestHttpsServiceAlpnParam({"foo"})})});
1095   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1096 
1097   ResultsOrError results =
1098       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1099                                   /*original_domain_name=*/kName,
1100                                   /*request_port=*/0);
1101 
1102   ASSERT_TRUE(results.has_value());
1103   EXPECT_THAT(
1104       results.value(),
1105       ElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
1106           kName, DnsQueryType::HTTPS, kDnsSource,
1107           /*expiration_matcher=*/Ne(std::nullopt),
1108           /*timed_expiration_matcher=*/Ne(std::nullopt),
1109           ElementsAre(Pair(
1110               4, ExpectConnectionEndpointMetadata(
1111                      ElementsAre("foo", dns_protocol::kHttpsServiceDefaultAlpn),
1112                      /*ech_config_list_matcher=*/IsEmpty(), kName)))))));
1113 }
1114 
TEST_F(DnsResponseResultExtractorTest,ExtractsHttpsRecordWithPrefixedNameAndMatchingServiceName)1115 TEST_F(DnsResponseResultExtractorTest,
1116        ExtractsHttpsRecordWithPrefixedNameAndMatchingServiceName) {
1117   constexpr char kName[] = "https.test";
1118   constexpr char kPrefixedName[] = "_444._https.https.test";
1119 
1120   DnsResponse response = BuildTestDnsResponse(
1121       kPrefixedName, dns_protocol::kTypeHttps,
1122       {BuildTestHttpsServiceRecord(kPrefixedName, /*priority=*/4,
1123                                    /*service_name=*/kName,
1124                                    /*params=*/
1125                                    {BuildTestHttpsServiceAlpnParam({"foo"})})});
1126   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1127 
1128   ResultsOrError results =
1129       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1130                                   /*original_domain_name=*/kName,
1131                                   /*request_port=*/0);
1132 
1133   ASSERT_TRUE(results.has_value());
1134   EXPECT_THAT(
1135       results.value(),
1136       ElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
1137           kPrefixedName, DnsQueryType::HTTPS, kDnsSource,
1138           /*expiration_matcher=*/Ne(std::nullopt),
1139           /*timed_expiration_matcher=*/Ne(std::nullopt),
1140           ElementsAre(Pair(
1141               4, ExpectConnectionEndpointMetadata(
1142                      ElementsAre("foo", dns_protocol::kHttpsServiceDefaultAlpn),
1143                      /*ech_config_list_matcher=*/IsEmpty(), kName)))))));
1144 }
1145 
TEST_F(DnsResponseResultExtractorTest,ExtractsHttpsRecordWithAliasingAndMatchingServiceName)1146 TEST_F(DnsResponseResultExtractorTest,
1147        ExtractsHttpsRecordWithAliasingAndMatchingServiceName) {
1148   constexpr char kName[] = "https.test";
1149 
1150   DnsResponse response = BuildTestDnsResponse(
1151       kName, dns_protocol::kTypeHttps,
1152       {BuildTestCnameRecord(kName, "alias.test"),
1153        BuildTestHttpsServiceRecord("alias.test", /*priority=*/4,
1154                                    /*service_name=*/kName,
1155                                    /*params=*/
1156                                    {BuildTestHttpsServiceAlpnParam({"foo"})})});
1157   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1158 
1159   ResultsOrError results =
1160       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1161                                   /*original_domain_name=*/kName,
1162                                   /*request_port=*/0);
1163 
1164   ASSERT_TRUE(results.has_value());
1165   EXPECT_THAT(
1166       results.value(),
1167       UnorderedElementsAre(
1168           Pointee(ExpectHostResolverInternalAliasResult(
1169               kName, DnsQueryType::HTTPS, kDnsSource,
1170               /*expiration_matcher=*/Ne(std::nullopt),
1171               /*timed_expiration_matcher=*/Ne(std::nullopt), "alias.test")),
1172           Pointee(ExpectHostResolverInternalMetadataResult(
1173               "alias.test", DnsQueryType::HTTPS, kDnsSource,
1174               /*expiration_matcher=*/Ne(std::nullopt),
1175               /*timed_expiration_matcher=*/Ne(std::nullopt),
1176               ElementsAre(Pair(
1177                   4, ExpectConnectionEndpointMetadata(
1178                          ElementsAre("foo",
1179                                      dns_protocol::kHttpsServiceDefaultAlpn),
1180                          /*ech_config_list_matcher=*/IsEmpty(), kName)))))));
1181 }
1182 
TEST_F(DnsResponseResultExtractorTest,IgnoreHttpsRecordWithNonMatchingServiceName)1183 TEST_F(DnsResponseResultExtractorTest,
1184        IgnoreHttpsRecordWithNonMatchingServiceName) {
1185   constexpr char kName[] = "https.test";
1186   constexpr base::TimeDelta kTtl = base::Hours(14);
1187 
1188   DnsResponse response = BuildTestDnsResponse(
1189       kName, dns_protocol::kTypeHttps,
1190       {BuildTestHttpsServiceRecord(
1191            kName, /*priority=*/4,
1192            /*service_name=*/"other.service.test",
1193            /*params=*/
1194            {BuildTestHttpsServiceAlpnParam({"ignored"})}, base::Hours(3)),
1195        BuildTestHttpsServiceRecord("https.test", /*priority=*/5,
1196                                    /*service_name=*/".",
1197                                    /*params=*/
1198                                    {BuildTestHttpsServiceAlpnParam({"foo"})},
1199                                    kTtl)});
1200   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1201 
1202   ResultsOrError results =
1203       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1204                                   /*original_domain_name=*/kName,
1205                                   /*request_port=*/0);
1206 
1207   ASSERT_TRUE(results.has_value());
1208 
1209   // Expect expiration to be derived only from non-ignored records.
1210   EXPECT_THAT(
1211       results.value(),
1212       ElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
1213           kName, DnsQueryType::HTTPS, kDnsSource,
1214           /*expiration_matcher=*/Optional(tick_clock_.NowTicks() + kTtl),
1215           /*timed_expiration_matcher=*/Optional(clock_.Now() + kTtl),
1216           ElementsAre(Pair(
1217               5, ExpectConnectionEndpointMetadata(
1218                      ElementsAre("foo", dns_protocol::kHttpsServiceDefaultAlpn),
1219                      /*ech_config_list_matcher=*/IsEmpty(), kName)))))));
1220 }
1221 
TEST_F(DnsResponseResultExtractorTest,ExtractsHttpsRecordWithPrefixedNameAndDefaultServiceName)1222 TEST_F(DnsResponseResultExtractorTest,
1223        ExtractsHttpsRecordWithPrefixedNameAndDefaultServiceName) {
1224   constexpr char kPrefixedName[] = "_445._https.https.test";
1225 
1226   DnsResponse response = BuildTestDnsResponse(
1227       kPrefixedName, dns_protocol::kTypeHttps,
1228       {BuildTestHttpsServiceRecord(kPrefixedName, /*priority=*/4,
1229                                    /*service_name=*/".",
1230                                    /*params=*/
1231                                    {BuildTestHttpsServiceAlpnParam({"foo"})})});
1232   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1233 
1234   ResultsOrError results =
1235       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1236                                   /*original_domain_name=*/"https.test",
1237                                   /*request_port=*/0);
1238 
1239   ASSERT_TRUE(results.has_value());
1240   EXPECT_THAT(
1241       results.value(),
1242       ElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
1243           kPrefixedName, DnsQueryType::HTTPS, kDnsSource,
1244           /*expiration_matcher=*/Ne(std::nullopt),
1245           /*timed_expiration_matcher=*/Ne(std::nullopt),
1246           ElementsAre(Pair(
1247               4,
1248               ExpectConnectionEndpointMetadata(
1249                   ElementsAre("foo", dns_protocol::kHttpsServiceDefaultAlpn),
1250                   /*ech_config_list_matcher=*/IsEmpty(), kPrefixedName)))))));
1251 }
1252 
TEST_F(DnsResponseResultExtractorTest,ExtractsHttpsRecordWithAliasingAndDefaultServiceName)1253 TEST_F(DnsResponseResultExtractorTest,
1254        ExtractsHttpsRecordWithAliasingAndDefaultServiceName) {
1255   constexpr char kName[] = "https.test";
1256 
1257   DnsResponse response = BuildTestDnsResponse(
1258       kName, dns_protocol::kTypeHttps,
1259       {BuildTestCnameRecord(kName, "alias.test"),
1260        BuildTestHttpsServiceRecord("alias.test", /*priority=*/4,
1261                                    /*service_name=*/".",
1262                                    /*params=*/
1263                                    {BuildTestHttpsServiceAlpnParam({"foo"})})});
1264   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1265 
1266   ResultsOrError results =
1267       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1268                                   /*original_domain_name=*/kName,
1269                                   /*request_port=*/0);
1270 
1271   ASSERT_TRUE(results.has_value());
1272   EXPECT_THAT(
1273       results.value(),
1274       UnorderedElementsAre(
1275           Pointee(ExpectHostResolverInternalAliasResult(
1276               kName, DnsQueryType::HTTPS, kDnsSource,
1277               /*expiration_matcher=*/Ne(std::nullopt),
1278               /*timed_expiration_matcher=*/Ne(std::nullopt), "alias.test")),
1279           Pointee(ExpectHostResolverInternalMetadataResult(
1280               "alias.test", DnsQueryType::HTTPS, kDnsSource,
1281               /*expiration_matcher=*/Ne(std::nullopt),
1282               /*timed_expiration_matcher=*/Ne(std::nullopt),
1283               ElementsAre(Pair(
1284                   4, ExpectConnectionEndpointMetadata(
1285                          ElementsAre("foo",
1286                                      dns_protocol::kHttpsServiceDefaultAlpn),
1287                          /*ech_config_list_matcher=*/IsEmpty(),
1288                          "alias.test")))))));
1289 }
1290 
TEST_F(DnsResponseResultExtractorTest,ExtractsHttpsRecordWithMatchingPort)1291 TEST_F(DnsResponseResultExtractorTest, ExtractsHttpsRecordWithMatchingPort) {
1292   constexpr char kName[] = "https.test";
1293   constexpr uint16_t kPort = 4567;
1294 
1295   DnsResponse response = BuildTestDnsResponse(
1296       kName, dns_protocol::kTypeHttps,
1297       {BuildTestHttpsServiceRecord(kName, /*priority=*/4,
1298                                    /*service_name=*/".",
1299                                    /*params=*/
1300                                    {BuildTestHttpsServiceAlpnParam({"foo"}),
1301                                     BuildTestHttpsServicePortParam(kPort)})});
1302   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1303 
1304   ResultsOrError results =
1305       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1306                                   /*original_domain_name=*/kName,
1307                                   /*request_port=*/kPort);
1308 
1309   ASSERT_TRUE(results.has_value());
1310   EXPECT_THAT(
1311       results.value(),
1312       UnorderedElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
1313           kName, DnsQueryType::HTTPS, kDnsSource,
1314           /*expiration_matcher=*/Ne(std::nullopt),
1315           /*timed_expiration_matcher=*/Ne(std::nullopt),
1316           ElementsAre(Pair(
1317               4, ExpectConnectionEndpointMetadata(
1318                      ElementsAre("foo", dns_protocol::kHttpsServiceDefaultAlpn),
1319                      /*ech_config_list_matcher=*/IsEmpty(), kName)))))));
1320 }
1321 
TEST_F(DnsResponseResultExtractorTest,IgnoresHttpsRecordWithMismatchingPort)1322 TEST_F(DnsResponseResultExtractorTest, IgnoresHttpsRecordWithMismatchingPort) {
1323   constexpr char kName[] = "https.test";
1324   constexpr base::TimeDelta kTtl = base::Days(14);
1325 
1326   DnsResponse response = BuildTestDnsResponse(
1327       kName, dns_protocol::kTypeHttps,
1328       {BuildTestHttpsServiceRecord(kName, /*priority=*/4,
1329                                    /*service_name=*/".",
1330                                    /*params=*/
1331                                    {BuildTestHttpsServiceAlpnParam({"ignored"}),
1332                                     BuildTestHttpsServicePortParam(1003)},
1333                                    base::Hours(12)),
1334        BuildTestHttpsServiceRecord(kName, /*priority=*/4,
1335                                    /*service_name=*/".",
1336                                    /*params=*/
1337                                    {BuildTestHttpsServiceAlpnParam({"foo"})},
1338                                    kTtl)});
1339   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1340 
1341   ResultsOrError results =
1342       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1343                                   /*original_domain_name=*/kName,
1344                                   /*request_port=*/55);
1345 
1346   ASSERT_TRUE(results.has_value());
1347 
1348   // Expect expiration to be derived only from non-ignored records.
1349   EXPECT_THAT(
1350       results.value(),
1351       UnorderedElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
1352           kName, DnsQueryType::HTTPS, kDnsSource,
1353           /*expiration_matcher=*/Optional(tick_clock_.NowTicks() + kTtl),
1354           /*timed_expiration_matcher=*/Optional(clock_.Now() + kTtl),
1355           ElementsAre(Pair(
1356               4, ExpectConnectionEndpointMetadata(
1357                      ElementsAre("foo", dns_protocol::kHttpsServiceDefaultAlpn),
1358                      /*ech_config_list_matcher=*/IsEmpty(), kName)))))));
1359 }
1360 
1361 // HTTPS records with "no-default-alpn" but also no "alpn" are not
1362 // "self-consistent" and should be ignored.
TEST_F(DnsResponseResultExtractorTest,IgnoresHttpsRecordWithNoAlpn)1363 TEST_F(DnsResponseResultExtractorTest, IgnoresHttpsRecordWithNoAlpn) {
1364   constexpr char kName[] = "https.test";
1365   constexpr base::TimeDelta kTtl = base::Minutes(150);
1366 
1367   DnsResponse response = BuildTestDnsResponse(
1368       kName, dns_protocol::kTypeHttps,
1369       {BuildTestHttpsServiceRecord(
1370            kName, /*priority=*/4,
1371            /*service_name=*/".",
1372            /*params=*/
1373            {{dns_protocol::kHttpsServiceParamKeyNoDefaultAlpn, ""}},
1374            base::Minutes(10)),
1375        BuildTestHttpsServiceRecord(kName, /*priority=*/4,
1376                                    /*service_name=*/".",
1377                                    /*params=*/
1378                                    {BuildTestHttpsServiceAlpnParam({"foo"})},
1379                                    kTtl)});
1380   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1381 
1382   ResultsOrError results =
1383       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1384                                   /*original_domain_name=*/kName,
1385                                   /*request_port=*/55);
1386 
1387   ASSERT_TRUE(results.has_value());
1388 
1389   // Expect expiration to be derived only from non-ignored records.
1390   EXPECT_THAT(
1391       results.value(),
1392       UnorderedElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
1393           kName, DnsQueryType::HTTPS, kDnsSource,
1394           /*expiration_matcher=*/Optional(tick_clock_.NowTicks() + kTtl),
1395           /*timed_expiration_matcher=*/Optional(clock_.Now() + kTtl),
1396           ElementsAre(Pair(
1397               4, ExpectConnectionEndpointMetadata(
1398                      ElementsAre("foo", dns_protocol::kHttpsServiceDefaultAlpn),
1399                      /*ech_config_list_matcher=*/IsEmpty(), kName)))))));
1400 }
1401 
1402 // Expect the entire response to be ignored if all HTTPS records have the
1403 // "no-default-alpn" param.
TEST_F(DnsResponseResultExtractorTest,IgnoresHttpsResponseWithNoCompatibleDefaultAlpn)1404 TEST_F(DnsResponseResultExtractorTest,
1405        IgnoresHttpsResponseWithNoCompatibleDefaultAlpn) {
1406   constexpr char kName[] = "https.test";
1407   constexpr uint16_t kMadeUpParamKey = 65500;  // From the private-use block.
1408   constexpr base::TimeDelta kLowestTtl = base::Days(2);
1409 
1410   DnsResponse response = BuildTestDnsResponse(
1411       kName, dns_protocol::kTypeHttps,
1412       {BuildTestHttpsServiceRecord(
1413            kName, /*priority=*/4,
1414            /*service_name=*/".",
1415            /*params=*/
1416            {BuildTestHttpsServiceAlpnParam({"foo1"}),
1417             {dns_protocol::kHttpsServiceParamKeyNoDefaultAlpn, ""}},
1418            base::Days(3)),
1419        BuildTestHttpsServiceRecord(
1420            kName, /*priority=*/5,
1421            /*service_name=*/".",
1422            /*params=*/
1423            {BuildTestHttpsServiceAlpnParam({"foo2"}),
1424             {dns_protocol::kHttpsServiceParamKeyNoDefaultAlpn, ""}},
1425            base::Days(4)),
1426        // Allows default ALPN, but ignored due to non-matching service name.
1427        BuildTestHttpsServiceRecord(kName, /*priority=*/3,
1428                                    /*service_name=*/"other.test",
1429                                    /*params=*/{}, kLowestTtl),
1430        // Allows default ALPN, but ignored due to incompatible param.
1431        BuildTestHttpsServiceRecord(
1432            kName, /*priority=*/6,
1433            /*service_name=*/".",
1434            /*params=*/
1435            {BuildTestHttpsServiceMandatoryParam({kMadeUpParamKey}),
1436             {kMadeUpParamKey, "foo"}},
1437            base::Hours(1)),
1438        // Allows default ALPN, but ignored due to mismatching port.
1439        BuildTestHttpsServiceRecord(
1440            kName, /*priority=*/10,
1441            /*service_name=*/".",
1442            /*params=*/{BuildTestHttpsServicePortParam(1005)}, base::Days(5))});
1443   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1444 
1445   ResultsOrError results =
1446       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1447                                   /*original_domain_name=*/kName,
1448                                   /*request_port=*/0);
1449 
1450   ASSERT_TRUE(results.has_value());
1451 
1452   // Expect expiration to be from the lowest TTL from the "compatible" records
1453   // that don't have incompatible params.
1454   EXPECT_THAT(
1455       results.value(),
1456       UnorderedElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
1457           kName, DnsQueryType::HTTPS, kDnsSource,
1458           /*expiration_matcher=*/Optional(tick_clock_.NowTicks() + kLowestTtl),
1459           /*timed_expiration_matcher=*/Optional(clock_.Now() + kLowestTtl),
1460           /*metadatas_matcher=*/IsEmpty()))));
1461 }
1462 
TEST_F(DnsResponseResultExtractorTest,ExtractsNxdomainHttpsResponses)1463 TEST_F(DnsResponseResultExtractorTest, ExtractsNxdomainHttpsResponses) {
1464   constexpr char kName[] = "https.test";
1465   constexpr auto kTtl = base::Minutes(45);
1466 
1467   DnsResponse response = BuildTestDnsResponse(
1468       kName, dns_protocol::kTypeHttps, /*answers=*/{},
1469       /*authority=*/
1470       {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)},
1471       /*additional=*/{}, dns_protocol::kRcodeNXDOMAIN);
1472   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1473 
1474   ResultsOrError results =
1475       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1476                                   /*original_domain_name=*/kName,
1477                                   /*request_port=*/0);
1478 
1479   ASSERT_TRUE(results.has_value());
1480   EXPECT_THAT(results.value(),
1481               ElementsAre(Pointee(ExpectHostResolverInternalErrorResult(
1482                   kName, DnsQueryType::HTTPS, kDnsSource,
1483                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kTtl),
1484                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kTtl),
1485                   ERR_NAME_NOT_RESOLVED))));
1486 }
1487 
TEST_F(DnsResponseResultExtractorTest,ExtractsNodataHttpsResponses)1488 TEST_F(DnsResponseResultExtractorTest, ExtractsNodataHttpsResponses) {
1489   constexpr char kName[] = "https.test";
1490   constexpr auto kTtl = base::Hours(36);
1491 
1492   DnsResponse response = BuildTestDnsResponse(
1493       kName, dns_protocol::kTypeHttps, /*answers=*/{},
1494       /*authority=*/
1495       {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)});
1496   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1497 
1498   ResultsOrError results =
1499       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1500                                   /*original_domain_name=*/kName,
1501                                   /*request_port=*/0);
1502 
1503   ASSERT_TRUE(results.has_value());
1504   EXPECT_THAT(results.value(),
1505               ElementsAre(Pointee(ExpectHostResolverInternalErrorResult(
1506                   kName, DnsQueryType::HTTPS, kDnsSource,
1507                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kTtl),
1508                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kTtl),
1509                   ERR_NAME_NOT_RESOLVED))));
1510 }
1511 
TEST_F(DnsResponseResultExtractorTest,ExtractsNodataHttpsResponsesWithoutTtl)1512 TEST_F(DnsResponseResultExtractorTest, ExtractsNodataHttpsResponsesWithoutTtl) {
1513   constexpr char kName[] = "https.test";
1514 
1515   // Response without a TTL-containing SOA record.
1516   DnsResponse response =
1517       BuildTestDnsResponse(kName, dns_protocol::kTypeHttps, /*answers=*/{});
1518   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1519 
1520   ResultsOrError results =
1521       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1522                                   /*original_domain_name=*/kName,
1523                                   /*request_port=*/0);
1524 
1525   // Expect empty result because not cacheable.
1526   ASSERT_TRUE(results.has_value());
1527   EXPECT_THAT(results.value(), IsEmpty());
1528 }
1529 
TEST_F(DnsResponseResultExtractorTest,RejectsMalformedHttpsRecord)1530 TEST_F(DnsResponseResultExtractorTest, RejectsMalformedHttpsRecord) {
1531   constexpr char kName[] = "https.test";
1532 
1533   DnsResponse response = BuildTestDnsResponse(
1534       kName, dns_protocol::kTypeHttps,
1535       {BuildTestDnsRecord(kName, dns_protocol::kTypeHttps,
1536                           "malformed rdata")} /* answers */);
1537   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1538 
1539   EXPECT_EQ(extractor
1540                 .ExtractDnsResults(DnsQueryType::HTTPS,
1541                                    /*original_domain_name=*/kName,
1542                                    /*request_port=*/0)
1543                 .error_or(ExtractionError::kOk),
1544             ExtractionError::kMalformedRecord);
1545 }
1546 
TEST_F(DnsResponseResultExtractorTest,RejectsWrongNameHttpsRecord)1547 TEST_F(DnsResponseResultExtractorTest, RejectsWrongNameHttpsRecord) {
1548   constexpr char kName[] = "https.test";
1549 
1550   DnsResponse response = BuildTestDnsResponse(
1551       kName, dns_protocol::kTypeHttps,
1552       {BuildTestHttpsAliasRecord("different.test", "alias.test")});
1553   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1554 
1555   EXPECT_EQ(extractor
1556                 .ExtractDnsResults(DnsQueryType::HTTPS,
1557                                    /*original_domain_name=*/kName,
1558                                    /*request_port=*/0)
1559                 .error_or(ExtractionError::kOk),
1560             ExtractionError::kNameMismatch);
1561 }
1562 
TEST_F(DnsResponseResultExtractorTest,IgnoresWrongTypeHttpsResponses)1563 TEST_F(DnsResponseResultExtractorTest, IgnoresWrongTypeHttpsResponses) {
1564   constexpr char kName[] = "https.test";
1565 
1566   DnsResponse response = BuildTestDnsResponse(
1567       kName, dns_protocol::kTypeHttps,
1568       {BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4))});
1569   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1570 
1571   ResultsOrError results =
1572       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1573                                   /*original_domain_name=*/kName,
1574                                   /*request_port=*/0);
1575 
1576   ASSERT_TRUE(results.has_value());
1577   EXPECT_THAT(results.value(), IsEmpty());
1578 }
1579 
TEST_F(DnsResponseResultExtractorTest,IgnoresAdditionalHttpsRecords)1580 TEST_F(DnsResponseResultExtractorTest, IgnoresAdditionalHttpsRecords) {
1581   constexpr char kName[] = "https.test";
1582   constexpr auto kTtl = base::Days(5);
1583 
1584   // Give all records an "alpn" value to help validate that only the correct
1585   // record is used.
1586   DnsResponse response = BuildTestDnsResponse(
1587       kName, dns_protocol::kTypeHttps,
1588       /*answers=*/
1589       {BuildTestHttpsServiceRecord(kName, /*priority=*/5u,
1590                                    /*service_name=*/".",
1591                                    /*params=*/
1592                                    {BuildTestHttpsServiceAlpnParam({"foo1"})},
1593                                    kTtl)},
1594       /*authority=*/{},
1595       /*additional=*/
1596       {BuildTestHttpsServiceRecord(kName, /*priority=*/3u,
1597                                    /*service_name=*/".",
1598                                    /*params=*/
1599                                    {BuildTestHttpsServiceAlpnParam({"foo2"})},
1600                                    base::Minutes(44)),
1601        BuildTestHttpsServiceRecord(kName, /*priority=*/2u,
1602                                    /*service_name=*/".",
1603                                    /*params=*/
1604                                    {BuildTestHttpsServiceAlpnParam({"foo3"})},
1605                                    base::Minutes(30))});
1606   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1607 
1608   ResultsOrError results =
1609       extractor.ExtractDnsResults(DnsQueryType::HTTPS,
1610                                   /*original_domain_name=*/kName,
1611                                   /*request_port=*/0);
1612 
1613   ASSERT_TRUE(results.has_value());
1614   EXPECT_THAT(
1615       results.value(),
1616       UnorderedElementsAre(Pointee(ExpectHostResolverInternalMetadataResult(
1617           kName, DnsQueryType::HTTPS, kDnsSource,
1618           Eq(tick_clock_.NowTicks() + kTtl), Eq(clock_.Now() + kTtl),
1619           ElementsAre(Pair(
1620               5,
1621               ExpectConnectionEndpointMetadata(
1622                   ElementsAre("foo1", dns_protocol::kHttpsServiceDefaultAlpn),
1623                   /*ech_config_list_matcher=*/IsEmpty(), kName)))))));
1624 }
1625 
TEST_F(DnsResponseResultExtractorTest,IgnoresUnsolicitedHttpsRecords)1626 TEST_F(DnsResponseResultExtractorTest, IgnoresUnsolicitedHttpsRecords) {
1627   constexpr char kName[] = "name.test";
1628   constexpr auto kTtl = base::Minutes(45);
1629 
1630   DnsResponse response = BuildTestDnsResponse(
1631       kName, dns_protocol::kTypeTXT,
1632       /*answers=*/
1633       {BuildTestDnsRecord(kName, dns_protocol::kTypeTXT, "\003foo", kTtl)},
1634       /*authority=*/{},
1635       /*additional=*/
1636       {BuildTestHttpsServiceRecord(
1637            "https.test", /*priority=*/3u, /*service_name=*/".",
1638            /*params=*/
1639            {BuildTestHttpsServiceAlpnParam({"foo2"})}, base::Minutes(44)),
1640        BuildTestHttpsServiceRecord("https.test", /*priority=*/2u,
1641                                    /*service_name=*/".",
1642                                    /*params=*/
1643                                    {BuildTestHttpsServiceAlpnParam({"foo3"})},
1644                                    base::Minutes(30))});
1645   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1646 
1647   ResultsOrError results =
1648       extractor.ExtractDnsResults(DnsQueryType::TXT,
1649                                   /*original_domain_name=*/kName,
1650                                   /*request_port=*/0);
1651 
1652   ASSERT_TRUE(results.has_value());
1653 
1654   // Expect expiration to be derived only from the non-ignored answer record.
1655   EXPECT_THAT(results.value(),
1656               ElementsAre(Pointee(ExpectHostResolverInternalDataResult(
1657                   kName, DnsQueryType::TXT, kDnsSource,
1658                   /*expiration_matcher=*/Eq(tick_clock_.NowTicks() + kTtl),
1659                   /*timed_expiration_matcher=*/Eq(clock_.Now() + kTtl),
1660                   /*endpoints_matcher=*/IsEmpty(), ElementsAre("foo")))));
1661 }
1662 
TEST_F(DnsResponseResultExtractorTest,HandlesInOrderCnameChain)1663 TEST_F(DnsResponseResultExtractorTest, HandlesInOrderCnameChain) {
1664   constexpr char kName[] = "first.test";
1665 
1666   DnsResponse response =
1667       BuildTestDnsResponse(kName, dns_protocol::kTypeTXT,
1668                            {BuildTestCnameRecord(kName, "second.test"),
1669                             BuildTestCnameRecord("second.test", "third.test"),
1670                             BuildTestCnameRecord("third.test", "fourth.test"),
1671                             BuildTestTextRecord("fourth.test", {"foo"}),
1672                             BuildTestTextRecord("fourth.test", {"bar"})});
1673   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1674 
1675   ResultsOrError results =
1676       extractor.ExtractDnsResults(DnsQueryType::TXT,
1677                                   /*original_domain_name=*/kName,
1678                                   /*request_port=*/0);
1679 
1680   ASSERT_TRUE(results.has_value());
1681   EXPECT_THAT(
1682       results.value(),
1683       UnorderedElementsAre(
1684           Pointee(ExpectHostResolverInternalAliasResult(
1685               kName, DnsQueryType::TXT, kDnsSource,
1686               /*expiration_matcher=*/Ne(std::nullopt),
1687               /*timed_expiration_matcher=*/Ne(std::nullopt), "second.test")),
1688           Pointee(ExpectHostResolverInternalAliasResult(
1689               "second.test", DnsQueryType::TXT, kDnsSource,
1690               /*expiration_matcher=*/Ne(std::nullopt),
1691               /*timed_expiration_matcher=*/Ne(std::nullopt), "third.test")),
1692           Pointee(ExpectHostResolverInternalAliasResult(
1693               "third.test", DnsQueryType::TXT, kDnsSource,
1694               /*expiration_matcher=*/Ne(std::nullopt),
1695               /*timed_expiration_matcher=*/Ne(std::nullopt), "fourth.test")),
1696           Pointee(ExpectHostResolverInternalDataResult(
1697               "fourth.test", DnsQueryType::TXT, kDnsSource,
1698               /*expiration_matcher=*/Ne(std::nullopt),
1699               /*timed_expiration_matcher=*/Ne(std::nullopt),
1700               /*endpoints_matcher=*/IsEmpty(),
1701               UnorderedElementsAre("foo", "bar")))));
1702 }
1703 
TEST_F(DnsResponseResultExtractorTest,HandlesInOrderCnameChainTypeA)1704 TEST_F(DnsResponseResultExtractorTest, HandlesInOrderCnameChainTypeA) {
1705   constexpr char kName[] = "first.test";
1706 
1707   const IPAddress kExpected(192, 168, 0, 1);
1708   IPEndPoint expected_endpoint(kExpected, 0 /* port */);
1709 
1710   DnsResponse response =
1711       BuildTestDnsResponse(kName, dns_protocol::kTypeA,
1712                            {BuildTestCnameRecord(kName, "second.test"),
1713                             BuildTestCnameRecord("second.test", "third.test"),
1714                             BuildTestCnameRecord("third.test", "fourth.test"),
1715                             BuildTestAddressRecord("fourth.test", kExpected)});
1716   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1717 
1718   ResultsOrError results =
1719       extractor.ExtractDnsResults(DnsQueryType::A,
1720                                   /*original_domain_name=*/kName,
1721                                   /*request_port=*/0);
1722 
1723   ASSERT_TRUE(results.has_value());
1724   EXPECT_THAT(
1725       results.value(),
1726       UnorderedElementsAre(
1727           Pointee(ExpectHostResolverInternalAliasResult(
1728               kName, DnsQueryType::A, kDnsSource,
1729               /*expiration_matcher=*/Ne(std::nullopt),
1730               /*timed_expiration_matcher=*/Ne(std::nullopt), "second.test")),
1731           Pointee(ExpectHostResolverInternalAliasResult(
1732               "second.test", DnsQueryType::A, kDnsSource,
1733               /*expiration_matcher=*/Ne(std::nullopt),
1734               /*timed_expiration_matcher=*/Ne(std::nullopt), "third.test")),
1735           Pointee(ExpectHostResolverInternalAliasResult(
1736               "third.test", DnsQueryType::A, kDnsSource,
1737               /*expiration_matcher=*/Ne(std::nullopt),
1738               /*timed_expiration_matcher=*/Ne(std::nullopt), "fourth.test")),
1739           Pointee(ExpectHostResolverInternalDataResult(
1740               "fourth.test", DnsQueryType::A, kDnsSource,
1741               /*expiration_matcher=*/Ne(std::nullopt),
1742               /*timed_expiration_matcher=*/Ne(std::nullopt),
1743               ElementsAre(expected_endpoint)))));
1744 }
1745 
TEST_F(DnsResponseResultExtractorTest,HandlesReverseOrderCnameChain)1746 TEST_F(DnsResponseResultExtractorTest, HandlesReverseOrderCnameChain) {
1747   constexpr char kName[] = "first.test";
1748 
1749   DnsResponse response =
1750       BuildTestDnsResponse(kName, dns_protocol::kTypeTXT,
1751                            {BuildTestTextRecord("fourth.test", {"foo"}),
1752                             BuildTestCnameRecord("third.test", "fourth.test"),
1753                             BuildTestCnameRecord("second.test", "third.test"),
1754                             BuildTestCnameRecord(kName, "second.test")});
1755   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1756 
1757   ResultsOrError results =
1758       extractor.ExtractDnsResults(DnsQueryType::TXT,
1759                                   /*original_domain_name=*/kName,
1760                                   /*request_port=*/0);
1761 
1762   ASSERT_TRUE(results.has_value());
1763   EXPECT_THAT(
1764       results.value(),
1765       UnorderedElementsAre(
1766           Pointee(ExpectHostResolverInternalAliasResult(
1767               kName, DnsQueryType::TXT, kDnsSource,
1768               /*expiration_matcher=*/Ne(std::nullopt),
1769               /*timed_expiration_matcher=*/Ne(std::nullopt), "second.test")),
1770           Pointee(ExpectHostResolverInternalAliasResult(
1771               "second.test", DnsQueryType::TXT, kDnsSource,
1772               /*expiration_matcher=*/Ne(std::nullopt),
1773               /*timed_expiration_matcher=*/Ne(std::nullopt), "third.test")),
1774           Pointee(ExpectHostResolverInternalAliasResult(
1775               "third.test", DnsQueryType::TXT, kDnsSource,
1776               /*expiration_matcher=*/Ne(std::nullopt),
1777               /*timed_expiration_matcher=*/Ne(std::nullopt), "fourth.test")),
1778           Pointee(ExpectHostResolverInternalDataResult(
1779               "fourth.test", DnsQueryType::TXT, kDnsSource,
1780               /*expiration_matcher=*/Ne(std::nullopt),
1781               /*timed_expiration_matcher=*/Ne(std::nullopt),
1782               /*endpoints_matcher=*/IsEmpty(), ElementsAre("foo")))));
1783 }
1784 
TEST_F(DnsResponseResultExtractorTest,HandlesReverseOrderCnameChainTypeA)1785 TEST_F(DnsResponseResultExtractorTest, HandlesReverseOrderCnameChainTypeA) {
1786   constexpr char kName[] = "first.test";
1787 
1788   const IPAddress kExpected(192, 168, 0, 1);
1789   IPEndPoint expected_endpoint(kExpected, 0 /* port */);
1790 
1791   DnsResponse response =
1792       BuildTestDnsResponse(kName, dns_protocol::kTypeA,
1793                            {BuildTestAddressRecord("fourth.test", kExpected),
1794                             BuildTestCnameRecord("third.test", "fourth.test"),
1795                             BuildTestCnameRecord("second.test", "third.test"),
1796                             BuildTestCnameRecord(kName, "second.test")});
1797   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1798 
1799   ResultsOrError results =
1800       extractor.ExtractDnsResults(DnsQueryType::A,
1801                                   /*original_domain_name=*/kName,
1802                                   /*request_port=*/0);
1803 
1804   ASSERT_TRUE(results.has_value());
1805   EXPECT_THAT(
1806       results.value(),
1807       UnorderedElementsAre(
1808           Pointee(ExpectHostResolverInternalAliasResult(
1809               kName, DnsQueryType::A, kDnsSource,
1810               /*expiration_matcher=*/Ne(std::nullopt),
1811               /*timed_expiration_matcher=*/Ne(std::nullopt), "second.test")),
1812           Pointee(ExpectHostResolverInternalAliasResult(
1813               "second.test", DnsQueryType::A, kDnsSource,
1814               /*expiration_matcher=*/Ne(std::nullopt),
1815               /*timed_expiration_matcher=*/Ne(std::nullopt), "third.test")),
1816           Pointee(ExpectHostResolverInternalAliasResult(
1817               "third.test", DnsQueryType::A, kDnsSource,
1818               /*expiration_matcher=*/Ne(std::nullopt),
1819               /*timed_expiration_matcher=*/Ne(std::nullopt), "fourth.test")),
1820           Pointee(ExpectHostResolverInternalDataResult(
1821               "fourth.test", DnsQueryType::A, kDnsSource,
1822               /*expiration_matcher=*/Ne(std::nullopt),
1823               /*timed_expiration_matcher=*/Ne(std::nullopt),
1824               ElementsAre(expected_endpoint)))));
1825 }
1826 
TEST_F(DnsResponseResultExtractorTest,HandlesArbitraryOrderCnameChain)1827 TEST_F(DnsResponseResultExtractorTest, HandlesArbitraryOrderCnameChain) {
1828   constexpr char kName[] = "first.test";
1829 
1830   DnsResponse response =
1831       BuildTestDnsResponse(kName, dns_protocol::kTypeTXT,
1832                            {BuildTestCnameRecord("second.test", "third.test"),
1833                             BuildTestTextRecord("fourth.test", {"foo"}),
1834                             BuildTestCnameRecord("third.test", "fourth.test"),
1835                             BuildTestCnameRecord(kName, "second.test")});
1836   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1837 
1838   ResultsOrError results =
1839       extractor.ExtractDnsResults(DnsQueryType::TXT,
1840                                   /*original_domain_name=*/kName,
1841                                   /*request_port=*/0);
1842 
1843   ASSERT_TRUE(results.has_value());
1844   EXPECT_THAT(
1845       results.value(),
1846       UnorderedElementsAre(
1847           Pointee(ExpectHostResolverInternalAliasResult(
1848               kName, DnsQueryType::TXT, kDnsSource,
1849               /*expiration_matcher=*/Ne(std::nullopt),
1850               /*timed_expiration_matcher=*/Ne(std::nullopt), "second.test")),
1851           Pointee(ExpectHostResolverInternalAliasResult(
1852               "second.test", DnsQueryType::TXT, kDnsSource,
1853               /*expiration_matcher=*/Ne(std::nullopt),
1854               /*timed_expiration_matcher=*/Ne(std::nullopt), "third.test")),
1855           Pointee(ExpectHostResolverInternalAliasResult(
1856               "third.test", DnsQueryType::TXT, kDnsSource,
1857               /*expiration_matcher=*/Ne(std::nullopt),
1858               /*timed_expiration_matcher=*/Ne(std::nullopt), "fourth.test")),
1859           Pointee(ExpectHostResolverInternalDataResult(
1860               "fourth.test", DnsQueryType::TXT, kDnsSource,
1861               /*expiration_matcher=*/Ne(std::nullopt),
1862               /*timed_expiration_matcher=*/Ne(std::nullopt),
1863               /*endpoints_matcher=*/IsEmpty(), ElementsAre("foo")))));
1864 }
1865 
TEST_F(DnsResponseResultExtractorTest,HandlesArbitraryOrderCnameChainTypeA)1866 TEST_F(DnsResponseResultExtractorTest, HandlesArbitraryOrderCnameChainTypeA) {
1867   constexpr char kName[] = "first.test";
1868 
1869   const IPAddress kExpected(192, 168, 0, 1);
1870   IPEndPoint expected_endpoint(kExpected, 0 /* port */);
1871 
1872   // Alias names are chosen so that the chain order is not in alphabetical
1873   // order.
1874   DnsResponse response =
1875       BuildTestDnsResponse(kName, dns_protocol::kTypeA,
1876                            {BuildTestCnameRecord("qsecond.test", "athird.test"),
1877                             BuildTestAddressRecord("zfourth.test", kExpected),
1878                             BuildTestCnameRecord("athird.test", "zfourth.test"),
1879                             BuildTestCnameRecord(kName, "qsecond.test")});
1880   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1881 
1882   ResultsOrError results =
1883       extractor.ExtractDnsResults(DnsQueryType::A,
1884                                   /*original_domain_name=*/kName,
1885                                   /*request_port=*/0);
1886 
1887   ASSERT_TRUE(results.has_value());
1888   EXPECT_THAT(
1889       results.value(),
1890       UnorderedElementsAre(
1891           Pointee(ExpectHostResolverInternalAliasResult(
1892               kName, DnsQueryType::A, kDnsSource,
1893               /*expiration_matcher=*/Ne(std::nullopt),
1894               /*timed_expiration_matcher=*/Ne(std::nullopt), "qsecond.test")),
1895           Pointee(ExpectHostResolverInternalAliasResult(
1896               "qsecond.test", DnsQueryType::A, kDnsSource,
1897               /*expiration_matcher=*/Ne(std::nullopt),
1898               /*timed_expiration_matcher=*/Ne(std::nullopt), "athird.test")),
1899           Pointee(ExpectHostResolverInternalAliasResult(
1900               "athird.test", DnsQueryType::A, kDnsSource,
1901               /*expiration_matcher=*/Ne(std::nullopt),
1902               /*timed_expiration_matcher=*/Ne(std::nullopt), "zfourth.test")),
1903           Pointee(ExpectHostResolverInternalDataResult(
1904               "zfourth.test", DnsQueryType::A, kDnsSource,
1905               /*expiration_matcher=*/Ne(std::nullopt),
1906               /*timed_expiration_matcher=*/Ne(std::nullopt),
1907               ElementsAre(expected_endpoint)))));
1908 }
1909 
TEST_F(DnsResponseResultExtractorTest,IgnoresNonResultTypesMixedWithCnameChain)1910 TEST_F(DnsResponseResultExtractorTest,
1911        IgnoresNonResultTypesMixedWithCnameChain) {
1912   constexpr char kName[] = "first.test";
1913 
1914   DnsResponse response = BuildTestDnsResponse(
1915       kName, dns_protocol::kTypeTXT,
1916       {BuildTestCnameRecord("second.test", "third.test"),
1917        BuildTestTextRecord("fourth.test", {"foo"}),
1918        BuildTestCnameRecord("third.test", "fourth.test"),
1919        BuildTestAddressRecord("third.test", IPAddress(1, 2, 3, 4)),
1920        BuildTestCnameRecord(kName, "second.test"),
1921        BuildTestAddressRecord("fourth.test", IPAddress(2, 3, 4, 5))});
1922   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1923 
1924   ResultsOrError results =
1925       extractor.ExtractDnsResults(DnsQueryType::TXT,
1926                                   /*original_domain_name=*/kName,
1927                                   /*request_port=*/0);
1928 
1929   ASSERT_TRUE(results.has_value());
1930   EXPECT_THAT(
1931       results.value(),
1932       UnorderedElementsAre(
1933           Pointee(ExpectHostResolverInternalAliasResult(
1934               kName, DnsQueryType::TXT, kDnsSource,
1935               /*expiration_matcher=*/Ne(std::nullopt),
1936               /*timed_expiration_matcher=*/Ne(std::nullopt), "second.test")),
1937           Pointee(ExpectHostResolverInternalAliasResult(
1938               "second.test", DnsQueryType::TXT, kDnsSource,
1939               /*expiration_matcher=*/Ne(std::nullopt),
1940               /*timed_expiration_matcher=*/Ne(std::nullopt), "third.test")),
1941           Pointee(ExpectHostResolverInternalAliasResult(
1942               "third.test", DnsQueryType::TXT, kDnsSource,
1943               /*expiration_matcher=*/Ne(std::nullopt),
1944               /*timed_expiration_matcher=*/Ne(std::nullopt), "fourth.test")),
1945           Pointee(ExpectHostResolverInternalDataResult(
1946               "fourth.test", DnsQueryType::TXT, kDnsSource,
1947               /*expiration_matcher=*/Ne(std::nullopt),
1948               /*timed_expiration_matcher=*/Ne(std::nullopt),
1949               /*endpoints_matcher=*/IsEmpty(), ElementsAre("foo")))));
1950 }
1951 
TEST_F(DnsResponseResultExtractorTest,IgnoresNonResultTypesMixedWithCnameChainTypeA)1952 TEST_F(DnsResponseResultExtractorTest,
1953        IgnoresNonResultTypesMixedWithCnameChainTypeA) {
1954   constexpr char kName[] = "first.test";
1955 
1956   const IPAddress kExpected(192, 168, 0, 1);
1957   IPEndPoint expected_endpoint(kExpected, 0 /* port */);
1958 
1959   DnsResponse response =
1960       BuildTestDnsResponse(kName, dns_protocol::kTypeA,
1961                            {BuildTestCnameRecord("second.test", "third.test"),
1962                             BuildTestTextRecord("fourth.test", {"foo"}),
1963                             BuildTestCnameRecord("third.test", "fourth.test"),
1964                             BuildTestCnameRecord(kName, "second.test"),
1965                             BuildTestAddressRecord("fourth.test", kExpected)});
1966   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
1967 
1968   ResultsOrError results =
1969       extractor.ExtractDnsResults(DnsQueryType::A,
1970                                   /*original_domain_name=*/kName,
1971                                   /*request_port=*/0);
1972 
1973   ASSERT_TRUE(results.has_value());
1974   EXPECT_THAT(
1975       results.value(),
1976       UnorderedElementsAre(
1977           Pointee(ExpectHostResolverInternalAliasResult(
1978               kName, DnsQueryType::A, kDnsSource,
1979               /*expiration_matcher=*/Ne(std::nullopt),
1980               /*timed_expiration_matcher=*/Ne(std::nullopt), "second.test")),
1981           Pointee(ExpectHostResolverInternalAliasResult(
1982               "second.test", DnsQueryType::A, kDnsSource,
1983               /*expiration_matcher=*/Ne(std::nullopt),
1984               /*timed_expiration_matcher=*/Ne(std::nullopt), "third.test")),
1985           Pointee(ExpectHostResolverInternalAliasResult(
1986               "third.test", DnsQueryType::A, kDnsSource,
1987               /*expiration_matcher=*/Ne(std::nullopt),
1988               /*timed_expiration_matcher=*/Ne(std::nullopt), "fourth.test")),
1989           Pointee(ExpectHostResolverInternalDataResult(
1990               "fourth.test", DnsQueryType::A, kDnsSource,
1991               /*expiration_matcher=*/Ne(std::nullopt),
1992               /*timed_expiration_matcher=*/Ne(std::nullopt),
1993               ElementsAre(expected_endpoint)))));
1994 }
1995 
TEST_F(DnsResponseResultExtractorTest,HandlesCnameChainWithoutResult)1996 TEST_F(DnsResponseResultExtractorTest, HandlesCnameChainWithoutResult) {
1997   constexpr char kName[] = "first.test";
1998 
1999   DnsResponse response =
2000       BuildTestDnsResponse(kName, dns_protocol::kTypeTXT,
2001                            {BuildTestCnameRecord("second.test", "third.test"),
2002                             BuildTestCnameRecord("third.test", "fourth.test"),
2003                             BuildTestCnameRecord(kName, "second.test")});
2004   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2005 
2006   ResultsOrError results =
2007       extractor.ExtractDnsResults(DnsQueryType::TXT,
2008                                   /*original_domain_name=*/kName,
2009                                   /*request_port=*/0);
2010 
2011   ASSERT_TRUE(results.has_value());
2012   EXPECT_THAT(
2013       results.value(),
2014       UnorderedElementsAre(
2015           Pointee(ExpectHostResolverInternalAliasResult(
2016               kName, DnsQueryType::TXT, kDnsSource,
2017               /*expiration_matcher=*/Ne(std::nullopt),
2018               /*timed_expiration_matcher=*/Ne(std::nullopt), "second.test")),
2019           Pointee(ExpectHostResolverInternalAliasResult(
2020               "second.test", DnsQueryType::TXT, kDnsSource,
2021               /*expiration_matcher=*/Ne(std::nullopt),
2022               /*timed_expiration_matcher=*/Ne(std::nullopt), "third.test")),
2023           Pointee(ExpectHostResolverInternalAliasResult(
2024               "third.test", DnsQueryType::TXT, kDnsSource,
2025               /*expiration_matcher=*/Ne(std::nullopt),
2026               /*timed_expiration_matcher=*/Ne(std::nullopt), "fourth.test"))));
2027 }
2028 
TEST_F(DnsResponseResultExtractorTest,HandlesCnameChainWithoutResultTypeA)2029 TEST_F(DnsResponseResultExtractorTest, HandlesCnameChainWithoutResultTypeA) {
2030   constexpr char kName[] = "first.test";
2031 
2032   DnsResponse response =
2033       BuildTestDnsResponse(kName, dns_protocol::kTypeA,
2034                            {BuildTestCnameRecord("second.test", "third.test"),
2035                             BuildTestCnameRecord("third.test", "fourth.test"),
2036                             BuildTestCnameRecord(kName, "second.test")});
2037   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2038 
2039   ResultsOrError results =
2040       extractor.ExtractDnsResults(DnsQueryType::A,
2041                                   /*original_domain_name=*/kName,
2042                                   /*request_port=*/0);
2043 
2044   ASSERT_TRUE(results.has_value());
2045   EXPECT_THAT(
2046       results.value(),
2047       UnorderedElementsAre(
2048           Pointee(ExpectHostResolverInternalAliasResult(
2049               kName, DnsQueryType::A, kDnsSource,
2050               /*expiration_matcher=*/Ne(std::nullopt),
2051               /*timed_expiration_matcher=*/Ne(std::nullopt), "second.test")),
2052           Pointee(ExpectHostResolverInternalAliasResult(
2053               "second.test", DnsQueryType::A, kDnsSource,
2054               /*expiration_matcher=*/Ne(std::nullopt),
2055               /*timed_expiration_matcher=*/Ne(std::nullopt), "third.test")),
2056           Pointee(ExpectHostResolverInternalAliasResult(
2057               "third.test", DnsQueryType::A, kDnsSource,
2058               /*expiration_matcher=*/Ne(std::nullopt),
2059               /*timed_expiration_matcher=*/Ne(std::nullopt), "fourth.test"))));
2060 }
2061 
TEST_F(DnsResponseResultExtractorTest,RejectsCnameChainWithLoop)2062 TEST_F(DnsResponseResultExtractorTest, RejectsCnameChainWithLoop) {
2063   constexpr char kName[] = "first.test";
2064 
2065   DnsResponse response =
2066       BuildTestDnsResponse(kName, dns_protocol::kTypeTXT,
2067                            {BuildTestCnameRecord("second.test", "third.test"),
2068                             BuildTestTextRecord("third.test", {"foo"}),
2069                             BuildTestCnameRecord("third.test", "second.test"),
2070                             BuildTestCnameRecord(kName, "second.test")});
2071   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2072 
2073   EXPECT_EQ(extractor
2074                 .ExtractDnsResults(DnsQueryType::TXT,
2075                                    /*original_domain_name=*/kName,
2076                                    /*request_port=*/0)
2077                 .error_or(ExtractionError::kOk),
2078             ExtractionError::kBadAliasChain);
2079 }
2080 
TEST_F(DnsResponseResultExtractorTest,RejectsCnameChainWithLoopToBeginning)2081 TEST_F(DnsResponseResultExtractorTest, RejectsCnameChainWithLoopToBeginning) {
2082   constexpr char kName[] = "first.test";
2083 
2084   DnsResponse response =
2085       BuildTestDnsResponse(kName, dns_protocol::kTypeTXT,
2086                            {BuildTestCnameRecord("second.test", "third.test"),
2087                             BuildTestTextRecord("third.test", {"foo"}),
2088                             BuildTestCnameRecord("third.test", "first.test"),
2089                             BuildTestCnameRecord(kName, "second.test")});
2090   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2091 
2092   EXPECT_EQ(extractor
2093                 .ExtractDnsResults(DnsQueryType::TXT,
2094                                    /*original_domain_name=*/kName,
2095                                    /*request_port=*/0)
2096                 .error_or(ExtractionError::kOk),
2097             ExtractionError::kBadAliasChain);
2098 }
2099 
TEST_F(DnsResponseResultExtractorTest,RejectsCnameChainWithLoopToBeginningWithoutResult)2100 TEST_F(DnsResponseResultExtractorTest,
2101        RejectsCnameChainWithLoopToBeginningWithoutResult) {
2102   constexpr char kName[] = "first.test";
2103 
2104   DnsResponse response =
2105       BuildTestDnsResponse(kName, dns_protocol::kTypeTXT,
2106                            {BuildTestCnameRecord("second.test", "third.test"),
2107                             BuildTestCnameRecord("third.test", "first.test"),
2108                             BuildTestCnameRecord(kName, "second.test")});
2109   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2110 
2111   EXPECT_EQ(extractor
2112                 .ExtractDnsResults(DnsQueryType::TXT,
2113                                    /*original_domain_name=*/kName,
2114                                    /*request_port=*/0)
2115                 .error_or(ExtractionError::kOk),
2116             ExtractionError::kBadAliasChain);
2117 }
2118 
TEST_F(DnsResponseResultExtractorTest,RejectsCnameChainWithWrongStart)2119 TEST_F(DnsResponseResultExtractorTest, RejectsCnameChainWithWrongStart) {
2120   constexpr char kName[] = "test.test";
2121 
2122   DnsResponse response =
2123       BuildTestDnsResponse(kName, dns_protocol::kTypeTXT,
2124                            {BuildTestCnameRecord("second.test", "third.test"),
2125                             BuildTestTextRecord("fourth.test", {"foo"}),
2126                             BuildTestCnameRecord("third.test", "fourth.test"),
2127                             BuildTestCnameRecord("first.test", "second.test")});
2128   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2129 
2130   EXPECT_EQ(extractor
2131                 .ExtractDnsResults(DnsQueryType::TXT,
2132                                    /*original_domain_name=*/kName,
2133                                    /*request_port=*/0)
2134                 .error_or(ExtractionError::kOk),
2135             ExtractionError::kBadAliasChain);
2136 }
2137 
TEST_F(DnsResponseResultExtractorTest,RejectsCnameChainWithWrongResultName)2138 TEST_F(DnsResponseResultExtractorTest, RejectsCnameChainWithWrongResultName) {
2139   constexpr char kName[] = "first.test";
2140 
2141   DnsResponse response =
2142       BuildTestDnsResponse(kName, dns_protocol::kTypeTXT,
2143                            {BuildTestCnameRecord("second.test", "third.test"),
2144                             BuildTestTextRecord("third.test", {"foo"}),
2145                             BuildTestCnameRecord("third.test", "fourth.test"),
2146                             BuildTestCnameRecord(kName, "second.test")});
2147   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2148 
2149   EXPECT_EQ(extractor
2150                 .ExtractDnsResults(DnsQueryType::TXT,
2151                                    /*original_domain_name=*/kName,
2152                                    /*request_port=*/0)
2153                 .error_or(ExtractionError::kOk),
2154             ExtractionError::kNameMismatch);
2155 }
2156 
TEST_F(DnsResponseResultExtractorTest,RejectsCnameSharedWithResult)2157 TEST_F(DnsResponseResultExtractorTest, RejectsCnameSharedWithResult) {
2158   constexpr char kName[] = "first.test";
2159 
2160   DnsResponse response =
2161       BuildTestDnsResponse(kName, dns_protocol::kTypeTXT,
2162                            {BuildTestCnameRecord("second.test", "third.test"),
2163                             BuildTestTextRecord(kName, {"foo"}),
2164                             BuildTestCnameRecord("third.test", "fourth.test"),
2165                             BuildTestCnameRecord(kName, "second.test")});
2166   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2167 
2168   EXPECT_EQ(extractor
2169                 .ExtractDnsResults(DnsQueryType::TXT,
2170                                    /*original_domain_name=*/kName,
2171                                    /*request_port=*/0)
2172                 .error_or(ExtractionError::kOk),
2173             ExtractionError::kNameMismatch);
2174 }
2175 
TEST_F(DnsResponseResultExtractorTest,RejectsDisjointCnameChain)2176 TEST_F(DnsResponseResultExtractorTest, RejectsDisjointCnameChain) {
2177   constexpr char kName[] = "first.test";
2178 
2179   DnsResponse response = BuildTestDnsResponse(
2180       kName, dns_protocol::kTypeTXT,
2181       {BuildTestCnameRecord("second.test", "third.test"),
2182        BuildTestTextRecord("fourth.test", {"foo"}),
2183        BuildTestCnameRecord("third.test", "fourth.test"),
2184        BuildTestCnameRecord("other1.test", "other2.test"),
2185        BuildTestCnameRecord(kName, "second.test"),
2186        BuildTestCnameRecord("other2.test", "other3.test")});
2187   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2188 
2189   EXPECT_EQ(extractor
2190                 .ExtractDnsResults(DnsQueryType::TXT,
2191                                    /*original_domain_name=*/kName,
2192                                    /*request_port=*/0)
2193                 .error_or(ExtractionError::kOk),
2194             ExtractionError::kBadAliasChain);
2195 }
2196 
TEST_F(DnsResponseResultExtractorTest,RejectsDoubledCnames)2197 TEST_F(DnsResponseResultExtractorTest, RejectsDoubledCnames) {
2198   constexpr char kName[] = "first.test";
2199 
2200   DnsResponse response =
2201       BuildTestDnsResponse(kName, dns_protocol::kTypeTXT,
2202                            {BuildTestCnameRecord("second.test", "third.test"),
2203                             BuildTestTextRecord("fourth.test", {"foo"}),
2204                             BuildTestCnameRecord("third.test", "fourth.test"),
2205                             BuildTestCnameRecord("third.test", "fifth.test"),
2206                             BuildTestCnameRecord(kName, "second.test")});
2207   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2208 
2209   EXPECT_EQ(extractor
2210                 .ExtractDnsResults(DnsQueryType::TXT,
2211                                    /*original_domain_name=*/kName,
2212                                    /*request_port=*/0)
2213                 .error_or(ExtractionError::kOk),
2214             ExtractionError::kMultipleCnames);
2215 }
2216 
TEST_F(DnsResponseResultExtractorTest,IgnoresTtlFromNonResultType)2217 TEST_F(DnsResponseResultExtractorTest, IgnoresTtlFromNonResultType) {
2218   constexpr char kName[] = "name.test";
2219   constexpr base::TimeDelta kMinTtl = base::Minutes(4);
2220 
2221   DnsResponse response = BuildTestDnsResponse(
2222       kName, dns_protocol::kTypeTXT,
2223       {BuildTestTextRecord(kName, {"foo"}, base::Hours(3)),
2224        BuildTestTextRecord(kName, {"bar"}, kMinTtl),
2225        BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4), base::Seconds(2)),
2226        BuildTestTextRecord(kName, {"baz"}, base::Minutes(15))});
2227   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2228 
2229   ResultsOrError results =
2230       extractor.ExtractDnsResults(DnsQueryType::TXT,
2231                                   /*original_domain_name=*/kName,
2232                                   /*request_port=*/0);
2233 
2234   ASSERT_TRUE(results.has_value());
2235   EXPECT_THAT(
2236       results.value(),
2237       ElementsAre(Pointee(ExpectHostResolverInternalDataResult(
2238           kName, DnsQueryType::TXT, kDnsSource,
2239           Eq(tick_clock_.NowTicks() + kMinTtl), Eq(clock_.Now() + kMinTtl),
2240           /*endpoints_matcher=*/IsEmpty(),
2241           UnorderedElementsAre("foo", "bar", "baz")))));
2242 }
2243 
TEST_F(DnsResponseResultExtractorTest,ExtractsTtlFromCname)2244 TEST_F(DnsResponseResultExtractorTest, ExtractsTtlFromCname) {
2245   constexpr char kName[] = "name.test";
2246   constexpr char kAlias[] = "alias.test";
2247   constexpr base::TimeDelta kTtl = base::Minutes(4);
2248 
2249   DnsResponse response =
2250       BuildTestDnsResponse("name.test", dns_protocol::kTypeTXT,
2251                            {BuildTestCnameRecord(kName, kAlias, kTtl)});
2252   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2253 
2254   ResultsOrError results =
2255       extractor.ExtractDnsResults(DnsQueryType::TXT,
2256                                   /*original_domain_name=*/kName,
2257                                   /*request_port=*/0);
2258 
2259   ASSERT_TRUE(results.has_value());
2260   EXPECT_THAT(
2261       results.value(),
2262       UnorderedElementsAre(Pointee(ExpectHostResolverInternalAliasResult(
2263           kName, DnsQueryType::TXT, kDnsSource,
2264           Eq(tick_clock_.NowTicks() + kTtl), Eq(clock_.Now() + kTtl),
2265           kAlias))));
2266 }
2267 
TEST_F(DnsResponseResultExtractorTest,ValidatesAliasNames)2268 TEST_F(DnsResponseResultExtractorTest, ValidatesAliasNames) {
2269   constexpr char kName[] = "first.test";
2270 
2271   const IPAddress kExpected(192, 168, 0, 1);
2272   IPEndPoint expected_endpoint(kExpected, 0 /* port */);
2273 
2274   DnsResponse response =
2275       BuildTestDnsResponse(kName, dns_protocol::kTypeA,
2276                            {BuildTestCnameRecord(kName, "second.test"),
2277                             BuildTestCnameRecord("second.test", "localhost"),
2278                             BuildTestCnameRecord("localhost", "fourth.test"),
2279                             BuildTestAddressRecord("fourth.test", kExpected)});
2280   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2281 
2282   EXPECT_EQ(extractor
2283                 .ExtractDnsResults(DnsQueryType::A,
2284                                    /*original_domain_name=*/kName,
2285                                    /*request_port=*/0)
2286                 .error_or(ExtractionError::kOk),
2287             ExtractionError::kMalformedRecord);
2288 }
2289 
TEST_F(DnsResponseResultExtractorTest,CanonicalizesAliasNames)2290 TEST_F(DnsResponseResultExtractorTest, CanonicalizesAliasNames) {
2291   const IPAddress kExpected(192, 168, 0, 1);
2292   constexpr char kName[] = "address.test";
2293   constexpr char kCname[] = "\005ALIAS\004test\000";
2294 
2295   // Need to build records directly in order to manually encode alias target
2296   // name because BuildTestDnsAddressResponseWithCname() uses
2297   // DNSDomainFromDot() which does not support non-URL-canonicalized names.
2298   std::vector<DnsResourceRecord> answers = {
2299       BuildTestDnsRecord(kName, dns_protocol::kTypeCNAME,
2300                          std::string(kCname, sizeof(kCname) - 1)),
2301       BuildTestAddressRecord("alias.test", kExpected)};
2302   DnsResponse response =
2303       BuildTestDnsResponse(kName, dns_protocol::kTypeA, answers);
2304 
2305   DnsResponseResultExtractor extractor(response, clock_, tick_clock_);
2306 
2307   ResultsOrError results =
2308       extractor.ExtractDnsResults(DnsQueryType::A,
2309                                   /*original_domain_name=*/kName,
2310                                   /*request_port=*/0);
2311 
2312   ASSERT_TRUE(results.has_value());
2313   EXPECT_THAT(
2314       results.value(),
2315       UnorderedElementsAre(
2316           Pointee(ExpectHostResolverInternalAliasResult(
2317               kName, DnsQueryType::A, kDnsSource,
2318               /*expiration_matcher=*/Ne(std::nullopt),
2319               /*timed_expiration_matcher=*/Ne(std::nullopt), "alias.test")),
2320           Pointee(ExpectHostResolverInternalDataResult(
2321               "alias.test", DnsQueryType::A, kDnsSource,
2322               /*expiration_matcher=*/Ne(std::nullopt),
2323               /*timed_expiration_matcher=*/Ne(std::nullopt),
2324               ElementsAre(IPEndPoint(kExpected, /*port=*/0))))));
2325 }
2326 
2327 }  // namespace
2328 }  // namespace net
2329