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