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