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