xref: /aosp_15_r20/external/cronet/net/dns/dns_config_service_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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