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