1 // Copyright 2012 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_config_service.h"
6
7 #include <memory>
8 #include <string>
9 #include <string_view>
10 #include <utility>
11
12 #include "base/cancelable_callback.h"
13 #include "base/functional/bind.h"
14 #include "base/location.h"
15 #include "base/memory/raw_ptr.h"
16 #include "base/run_loop.h"
17 #include "base/strings/string_split.h"
18 #include "base/task/single_thread_task_runner.h"
19 #include "base/task/thread_pool/thread_pool_instance.h"
20 #include "base/test/bind.h"
21 #include "base/test/task_environment.h"
22 #include "base/test/test_timeouts.h"
23 #include "net/base/address_family.h"
24 #include "net/base/ip_address.h"
25 #include "net/dns/dns_hosts.h"
26 #include "net/dns/public/dns_protocol.h"
27 #include "net/dns/test_dns_config_service.h"
28 #include "net/test/test_with_task_environment.h"
29 #include "testing/gmock/include/gmock/gmock.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31
32 namespace net {
33
34 namespace {
35
36 using testing::_;
37 using testing::DoAll;
38 using testing::Return;
39 using testing::SetArgPointee;
40
41 class DnsConfigServiceTest : public TestWithTaskEnvironment {
42 public:
DnsConfigServiceTest()43 DnsConfigServiceTest()
44 : TestWithTaskEnvironment(
45 base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
46
OnConfigChanged(const DnsConfig & config)47 void OnConfigChanged(const DnsConfig& config) {
48 last_config_ = config;
49 if (quit_on_config_)
50 std::move(quit_on_config_).Run();
51 }
52
53 protected:
WaitForConfig()54 void WaitForConfig() {
55 base::RunLoop run_loop;
56 quit_on_config_ = run_loop.QuitClosure();
57
58 // Some work may be performed on `ThreadPool` and is not accounted for in a
59 // `RunLoop::RunUntilIdle()` call.
60 run_loop.RunUntilIdle();
61 base::ThreadPoolInstance::Get()->FlushForTesting();
62 if (!run_loop.AnyQuitCalled())
63 run_loop.RunUntilIdle();
64
65 // Validate a config notification was received.
66 ASSERT_TRUE(run_loop.AnyQuitCalled());
67 }
68
WaitForInvalidationTimeout()69 void WaitForInvalidationTimeout() {
70 base::RunLoop run_loop;
71 quit_on_config_ = run_loop.QuitClosure();
72 FastForwardBy(DnsConfigService::kInvalidationTimeout);
73
74 // Validate a config notification was received, and that it was an empty
75 // config (empty config always expected for invalidation).
76 ASSERT_TRUE(run_loop.AnyQuitCalled());
77 ASSERT_EQ(last_config_, DnsConfig());
78 }
79
ValidateNoNotification()80 void ValidateNoNotification() {
81 base::RunLoop run_loop;
82 quit_on_config_ = run_loop.QuitClosure();
83
84 // Flush any potential work and wait for any potential invalidation timeout.
85 run_loop.RunUntilIdle();
86 base::ThreadPoolInstance::Get()->FlushForTesting();
87 if (!run_loop.AnyQuitCalled())
88 run_loop.RunUntilIdle();
89 FastForwardBy(DnsConfigService::kInvalidationTimeout);
90
91 // Validate no config notification was received.
92 ASSERT_FALSE(run_loop.AnyQuitCalled());
93 quit_on_config_.Reset();
94 }
95
96 // Generate a config using the given seed..
MakeConfig(unsigned seed)97 DnsConfig MakeConfig(unsigned seed) {
98 DnsConfig config;
99 config.nameservers.emplace_back(IPAddress(1, 2, 3, 4), seed & 0xFFFF);
100 EXPECT_TRUE(config.IsValid());
101 return config;
102 }
103
104 // Generate hosts using the given seed.
MakeHosts(unsigned seed)105 DnsHosts MakeHosts(unsigned seed) {
106 DnsHosts hosts;
107 std::string hosts_content = "127.0.0.1 localhost";
108 hosts_content.append(seed, '1');
109 ParseHosts(hosts_content, &hosts);
110 EXPECT_FALSE(hosts.empty());
111 return hosts;
112 }
113
SetUpService(TestDnsConfigService & service)114 void SetUpService(TestDnsConfigService& service) {
115 service.WatchConfig(base::BindRepeating(
116 &DnsConfigServiceTest::OnConfigChanged, base::Unretained(this)));
117
118 // Run through any initial config notifications triggered by starting the
119 // watch.
120 base::RunLoop run_loop;
121 quit_on_config_ = run_loop.QuitClosure();
122 run_loop.RunUntilIdle();
123 base::ThreadPoolInstance::Get()->FlushForTesting();
124 run_loop.RunUntilIdle();
125 FastForwardBy(DnsConfigService::kInvalidationTimeout);
126 quit_on_config_.Reset();
127 }
128
SetUp()129 void SetUp() override {
130 service_ = std::make_unique<TestDnsConfigService>();
131 SetUpService(*service_);
132 EXPECT_FALSE(last_config_.IsValid());
133 }
134
TearDown()135 void TearDown() override {
136 // After test, expect no more config notifications.
137 ValidateNoNotification();
138 }
139
140 DnsConfig last_config_;
141 base::OnceClosure quit_on_config_;
142
143 // Service under test.
144 std::unique_ptr<TestDnsConfigService> service_;
145 };
146
147 class MockHostsParserFactory : public DnsHostsParser {
148 public:
149 HostsReadingTestDnsConfigService::HostsParserFactory GetFactory();
150
151 MOCK_METHOD(bool, ParseHosts, (DnsHosts*), (const, override));
152
153 private:
154 class Delegator : public DnsHostsParser {
155 public:
Delegator(MockHostsParserFactory * factory)156 explicit Delegator(MockHostsParserFactory* factory) : factory_(factory) {}
157
ParseHosts(DnsHosts * hosts) const158 bool ParseHosts(DnsHosts* hosts) const override {
159 return factory_->ParseHosts(hosts);
160 }
161
162 private:
163 raw_ptr<MockHostsParserFactory> factory_;
164 };
165 };
166
167 HostsReadingTestDnsConfigService::HostsParserFactory
GetFactory()168 MockHostsParserFactory::GetFactory() {
169 return base::BindLambdaForTesting(
170 [this]() -> std::unique_ptr<DnsHostsParser> {
171 return std::make_unique<Delegator>(this);
172 });
173 }
174
CreateHostsEntry(std::string_view name,AddressFamily family,IPAddress address)175 DnsHosts::value_type CreateHostsEntry(std::string_view name,
176 AddressFamily family,
177 IPAddress address) {
178 DnsHostsKey key = std::pair(std::string(name), family);
179 return std::pair(std::move(key), address);
180 }
181
182 } // namespace
183
TEST_F(DnsConfigServiceTest,FirstConfig)184 TEST_F(DnsConfigServiceTest, FirstConfig) {
185 DnsConfig config = MakeConfig(1);
186
187 service_->OnConfigRead(config);
188 // No hosts yet, so no config.
189 EXPECT_TRUE(last_config_.Equals(DnsConfig()));
190
191 service_->OnHostsRead(config.hosts);
192 EXPECT_TRUE(last_config_.Equals(config));
193 }
194
TEST_F(DnsConfigServiceTest,Timeout)195 TEST_F(DnsConfigServiceTest, Timeout) {
196 DnsConfig config = MakeConfig(1);
197 config.hosts = MakeHosts(1);
198 ASSERT_TRUE(config.IsValid());
199
200 service_->OnConfigRead(config);
201 service_->OnHostsRead(config.hosts);
202 EXPECT_FALSE(last_config_.Equals(DnsConfig()));
203 EXPECT_TRUE(last_config_.Equals(config));
204
205 service_->InvalidateConfig();
206 WaitForInvalidationTimeout();
207 EXPECT_FALSE(last_config_.Equals(config));
208 EXPECT_TRUE(last_config_.Equals(DnsConfig()));
209
210 service_->OnConfigRead(config);
211 EXPECT_FALSE(last_config_.Equals(DnsConfig()));
212 EXPECT_TRUE(last_config_.Equals(config));
213
214 service_->InvalidateHosts();
215 WaitForInvalidationTimeout();
216 EXPECT_FALSE(last_config_.Equals(config));
217 EXPECT_TRUE(last_config_.Equals(DnsConfig()));
218
219 DnsConfig bad_config = last_config_ = MakeConfig(0xBAD);
220 service_->InvalidateConfig();
221 ValidateNoNotification();
222 EXPECT_TRUE(last_config_.Equals(bad_config)) << "Unexpected change";
223
224 last_config_ = DnsConfig();
225 service_->OnConfigRead(config);
226 service_->OnHostsRead(config.hosts);
227 EXPECT_FALSE(last_config_.Equals(DnsConfig()));
228 EXPECT_TRUE(last_config_.Equals(config));
229 }
230
TEST_F(DnsConfigServiceTest,SameConfig)231 TEST_F(DnsConfigServiceTest, SameConfig) {
232 DnsConfig config = MakeConfig(1);
233 config.hosts = MakeHosts(1);
234
235 service_->OnConfigRead(config);
236 service_->OnHostsRead(config.hosts);
237 EXPECT_FALSE(last_config_.Equals(DnsConfig()));
238 EXPECT_TRUE(last_config_.Equals(config));
239
240 last_config_ = DnsConfig();
241 service_->OnConfigRead(config);
242 EXPECT_TRUE(last_config_.Equals(DnsConfig())) << "Unexpected change";
243
244 service_->OnHostsRead(config.hosts);
245 EXPECT_TRUE(last_config_.Equals(DnsConfig())) << "Unexpected change";
246 }
247
TEST_F(DnsConfigServiceTest,DifferentConfig)248 TEST_F(DnsConfigServiceTest, DifferentConfig) {
249 DnsConfig config1 = MakeConfig(1);
250 DnsConfig config2 = MakeConfig(2);
251 DnsConfig config3 = MakeConfig(1);
252 config1.hosts = MakeHosts(1);
253 config2.hosts = MakeHosts(1);
254 config3.hosts = MakeHosts(2);
255 ASSERT_TRUE(config1.EqualsIgnoreHosts(config3));
256 ASSERT_FALSE(config1.Equals(config2));
257 ASSERT_FALSE(config1.Equals(config3));
258 ASSERT_FALSE(config2.Equals(config3));
259
260 service_->OnConfigRead(config1);
261 service_->OnHostsRead(config1.hosts);
262 EXPECT_FALSE(last_config_.Equals(DnsConfig()));
263 EXPECT_TRUE(last_config_.Equals(config1));
264
265 // It doesn't matter for this tests, but increases coverage.
266 service_->InvalidateConfig();
267 service_->InvalidateHosts();
268
269 service_->OnConfigRead(config2);
270 EXPECT_TRUE(last_config_.Equals(config1)) << "Unexpected change";
271 service_->OnHostsRead(config2.hosts); // Not an actual change.
272 EXPECT_FALSE(last_config_.Equals(config1));
273 EXPECT_TRUE(last_config_.Equals(config2));
274
275 service_->OnConfigRead(config3);
276 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config3));
277 service_->OnHostsRead(config3.hosts);
278 EXPECT_FALSE(last_config_.Equals(config2));
279 EXPECT_TRUE(last_config_.Equals(config3));
280 }
281
TEST_F(DnsConfigServiceTest,WatchFailure)282 TEST_F(DnsConfigServiceTest, WatchFailure) {
283 DnsConfig config1 = MakeConfig(1);
284 DnsConfig config2 = MakeConfig(2);
285 config1.hosts = MakeHosts(1);
286 config2.hosts = MakeHosts(2);
287
288 service_->OnConfigRead(config1);
289 service_->OnHostsRead(config1.hosts);
290 EXPECT_FALSE(last_config_.Equals(DnsConfig()));
291 EXPECT_TRUE(last_config_.Equals(config1));
292
293 // Simulate watch failure.
294 service_->set_watch_failed_for_testing(true);
295 service_->InvalidateConfig();
296 WaitForInvalidationTimeout();
297 EXPECT_FALSE(last_config_.Equals(config1));
298 EXPECT_TRUE(last_config_.Equals(DnsConfig()));
299
300 DnsConfig bad_config = last_config_ = MakeConfig(0xBAD);
301 // Actual change in config, so expect an update, but it should be empty.
302 service_->OnConfigRead(config1);
303 EXPECT_FALSE(last_config_.Equals(bad_config));
304 EXPECT_TRUE(last_config_.Equals(DnsConfig()));
305
306 last_config_ = bad_config;
307 // Actual change in config, so expect an update, but it should be empty.
308 service_->InvalidateConfig();
309 service_->OnConfigRead(config2);
310 EXPECT_FALSE(last_config_.Equals(bad_config));
311 EXPECT_TRUE(last_config_.Equals(DnsConfig()));
312
313 last_config_ = bad_config;
314 // No change, so no update.
315 service_->InvalidateConfig();
316 service_->OnConfigRead(config2);
317 EXPECT_TRUE(last_config_.Equals(bad_config));
318 }
319
TEST_F(DnsConfigServiceTest,HostsReadFailure)320 TEST_F(DnsConfigServiceTest, HostsReadFailure) {
321 MockHostsParserFactory parser;
322 EXPECT_CALL(parser, ParseHosts(_))
323 .WillRepeatedly(DoAll(SetArgPointee<0>(DnsHosts()), Return(false)));
324
325 auto service =
326 std::make_unique<HostsReadingTestDnsConfigService>(parser.GetFactory());
327 SetUpService(*service);
328
329 service->OnConfigRead(MakeConfig(1));
330 // No successfully read hosts, so no config result.
331 EXPECT_EQ(last_config_, DnsConfig());
332
333 // No change from retriggering read.
334 service->TriggerHostsChangeNotification(/*success=*/true);
335 ValidateNoNotification();
336 EXPECT_EQ(last_config_, DnsConfig());
337 }
338
TEST_F(DnsConfigServiceTest,ReadEmptyHosts)339 TEST_F(DnsConfigServiceTest, ReadEmptyHosts) {
340 MockHostsParserFactory parser;
341 EXPECT_CALL(parser, ParseHosts(_))
342 .WillRepeatedly(DoAll(SetArgPointee<0>(DnsHosts()), Return(true)));
343
344 auto service =
345 std::make_unique<HostsReadingTestDnsConfigService>(parser.GetFactory());
346 SetUpService(*service);
347
348 // Expect immediate result on reading config because HOSTS should already have
349 // been read on initting watch in `SetUpService()`.
350 DnsConfig config = MakeConfig(1);
351 service->OnConfigRead(config);
352 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config));
353 EXPECT_EQ(last_config_.hosts, DnsHosts());
354
355 // No change from retriggering read.
356 service->TriggerHostsChangeNotification(/*success=*/true);
357 ValidateNoNotification();
358 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config));
359 EXPECT_EQ(last_config_.hosts, DnsHosts());
360 }
361
TEST_F(DnsConfigServiceTest,ReadSingleHosts)362 TEST_F(DnsConfigServiceTest, ReadSingleHosts) {
363 DnsHosts hosts = {
364 CreateHostsEntry("name", ADDRESS_FAMILY_IPV4, {IPAddress(1, 2, 3, 4)})};
365
366 MockHostsParserFactory parser;
367 EXPECT_CALL(parser, ParseHosts(_))
368 .WillRepeatedly(DoAll(SetArgPointee<0>(hosts), Return(true)));
369
370 auto service =
371 std::make_unique<HostsReadingTestDnsConfigService>(parser.GetFactory());
372 SetUpService(*service);
373
374 // Expect immediate result on reading config because HOSTS should already have
375 // been read on initting watch in `SetUpService()`.
376 DnsConfig config = MakeConfig(1);
377 service->OnConfigRead(config);
378 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config));
379 EXPECT_EQ(last_config_.hosts, hosts);
380
381 // No change from retriggering read.
382 service->TriggerHostsChangeNotification(/*success=*/true);
383 ValidateNoNotification();
384 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config));
385 EXPECT_EQ(last_config_.hosts, hosts);
386 }
387
TEST_F(DnsConfigServiceTest,ReadMultipleHosts)388 TEST_F(DnsConfigServiceTest, ReadMultipleHosts) {
389 DnsHosts hosts = {
390 CreateHostsEntry("name1", ADDRESS_FAMILY_IPV4, {IPAddress(1, 2, 3, 4)}),
391 CreateHostsEntry("name2", ADDRESS_FAMILY_IPV4, {IPAddress(1, 2, 3, 5)}),
392 CreateHostsEntry(
393 "name1", ADDRESS_FAMILY_IPV6,
394 {IPAddress(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15)})};
395
396 MockHostsParserFactory parser;
397 EXPECT_CALL(parser, ParseHosts(_))
398 .WillRepeatedly(DoAll(SetArgPointee<0>(hosts), Return(true)));
399
400 auto service =
401 std::make_unique<HostsReadingTestDnsConfigService>(parser.GetFactory());
402 SetUpService(*service);
403
404 // Expect immediate result on reading config because HOSTS should already have
405 // been read on initting watch in `SetUpService()`.
406 DnsConfig config = MakeConfig(1);
407 service->OnConfigRead(config);
408 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config));
409 EXPECT_EQ(last_config_.hosts, hosts);
410
411 // No change from retriggering read.
412 service->TriggerHostsChangeNotification(/*success=*/true);
413 ValidateNoNotification();
414 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config));
415 EXPECT_EQ(last_config_.hosts, hosts);
416 }
417
TEST_F(DnsConfigServiceTest,HostsReadSubsequentFailure)418 TEST_F(DnsConfigServiceTest, HostsReadSubsequentFailure) {
419 DnsHosts hosts = {
420 CreateHostsEntry("name", ADDRESS_FAMILY_IPV4, {IPAddress(1, 2, 3, 4)})};
421
422 MockHostsParserFactory parser;
423 EXPECT_CALL(parser, ParseHosts(_))
424 .WillOnce(DoAll(SetArgPointee<0>(hosts), Return(true)))
425 .WillOnce(DoAll(SetArgPointee<0>(DnsHosts()), Return(false)));
426
427 auto service =
428 std::make_unique<HostsReadingTestDnsConfigService>(parser.GetFactory());
429 SetUpService(*service);
430
431 // Expect immediate result on reading config because HOSTS should already have
432 // been read on initting watch in `SetUpService()`.
433 DnsConfig config = MakeConfig(1);
434 service->OnConfigRead(config);
435 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config));
436 EXPECT_EQ(last_config_.hosts, hosts);
437
438 // Config cleared after subsequent read.
439 service->TriggerHostsChangeNotification(/*success=*/true);
440 WaitForInvalidationTimeout();
441 EXPECT_EQ(last_config_, DnsConfig());
442 }
443
TEST_F(DnsConfigServiceTest,HostsReadSubsequentSuccess)444 TEST_F(DnsConfigServiceTest, HostsReadSubsequentSuccess) {
445 DnsHosts hosts = {
446 CreateHostsEntry("name", ADDRESS_FAMILY_IPV4, {IPAddress(1, 2, 3, 4)})};
447
448 MockHostsParserFactory parser;
449 EXPECT_CALL(parser, ParseHosts(_))
450 .WillOnce(DoAll(SetArgPointee<0>(DnsHosts()), Return(false)))
451 .WillOnce(DoAll(SetArgPointee<0>(hosts), Return(true)));
452
453 auto service =
454 std::make_unique<HostsReadingTestDnsConfigService>(parser.GetFactory());
455 SetUpService(*service);
456
457 DnsConfig config = MakeConfig(1);
458 service->OnConfigRead(config);
459 // No successfully read hosts, so no config result.
460 EXPECT_EQ(last_config_, DnsConfig());
461
462 // Expect success after subsequent read.
463 service->TriggerHostsChangeNotification(/*success=*/true);
464 WaitForConfig();
465 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config));
466 EXPECT_EQ(last_config_.hosts, hosts);
467 }
468
TEST_F(DnsConfigServiceTest,ConfigReadDuringHostsReRead)469 TEST_F(DnsConfigServiceTest, ConfigReadDuringHostsReRead) {
470 DnsHosts hosts = {
471 CreateHostsEntry("name", ADDRESS_FAMILY_IPV4, {IPAddress(1, 2, 3, 4)})};
472
473 MockHostsParserFactory parser;
474 EXPECT_CALL(parser, ParseHosts(_))
475 .WillRepeatedly(DoAll(SetArgPointee<0>(hosts), Return(true)));
476
477 auto service =
478 std::make_unique<HostsReadingTestDnsConfigService>(parser.GetFactory());
479 SetUpService(*service);
480
481 // Expect immediate result on reading config because HOSTS should already have
482 // been read on initting watch in `SetUpService()`.
483 DnsConfig config1 = MakeConfig(1);
484 service->OnConfigRead(config1);
485 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config1));
486 EXPECT_EQ(last_config_.hosts, hosts);
487
488 // Trigger HOSTS read, and expect no new-config notification yet.
489 service->TriggerHostsChangeNotification(/*success=*/true);
490 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config1));
491 EXPECT_EQ(last_config_.hosts, hosts);
492
493 // Simulate completion of a Config read. Expect no new-config notification
494 // while HOSTS read still in progress.
495 DnsConfig config2 = MakeConfig(2);
496 service->OnConfigRead(config2);
497 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config1));
498 EXPECT_EQ(last_config_.hosts, hosts);
499
500 // Expect new config on completion of HOSTS read.
501 WaitForConfig();
502 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config2));
503 EXPECT_EQ(last_config_.hosts, hosts);
504 }
505
TEST_F(DnsConfigServiceTest,HostsWatcherFailure)506 TEST_F(DnsConfigServiceTest, HostsWatcherFailure) {
507 DnsHosts hosts = {
508 CreateHostsEntry("name", ADDRESS_FAMILY_IPV4, {IPAddress(1, 2, 3, 4)})};
509
510 MockHostsParserFactory parser;
511 EXPECT_CALL(parser, ParseHosts(_))
512 .WillOnce(DoAll(SetArgPointee<0>(hosts), Return(true)));
513
514 auto service =
515 std::make_unique<HostsReadingTestDnsConfigService>(parser.GetFactory());
516 SetUpService(*service);
517
518 // Expect immediate result on reading config because HOSTS should already have
519 // been read on initting watch in `SetUpService()`.
520 DnsConfig config = MakeConfig(1);
521 service->OnConfigRead(config);
522 EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config));
523 EXPECT_EQ(last_config_.hosts, hosts);
524
525 // Simulate watcher failure.
526 service->TriggerHostsChangeNotification(/*success=*/false);
527 WaitForInvalidationTimeout();
528 EXPECT_EQ(last_config_, DnsConfig());
529 }
530
531 } // namespace net
532