1 // Copyright 2019 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/system_dns_config_change_notifier.h"
6
7 #include <optional>
8 #include <utility>
9 #include <vector>
10
11 #include "base/functional/bind.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/run_loop.h"
14 #include "base/task/sequenced_task_runner.h"
15 #include "base/task/task_traits.h"
16 #include "base/task/thread_pool.h"
17 #include "net/base/ip_address.h"
18 #include "net/base/ip_endpoint.h"
19 #include "net/dns/dns_hosts.h"
20 #include "net/dns/test_dns_config_service.h"
21 #include "net/test/test_with_task_environment.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 namespace net {
26
27 namespace {
28 const std::vector<IPEndPoint> kNameservers = {
29 IPEndPoint(IPAddress(1, 2, 3, 4), 95)};
30 const std::vector<IPEndPoint> kNameservers2 = {
31 IPEndPoint(IPAddress(2, 3, 4, 5), 195)};
32 const DnsConfig kConfig(kNameservers);
33 const DnsConfig kConfig2(kNameservers2);
34 } // namespace
35
36 class SystemDnsConfigChangeNotifierTest : public TestWithTaskEnvironment {
37 public:
38 // Set up a change notifier, owned on a dedicated blockable task runner, with
39 // a faked underlying DnsConfigService.
SystemDnsConfigChangeNotifierTest()40 SystemDnsConfigChangeNotifierTest()
41 : notifier_task_runner_(
42 base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})) {
43 auto test_service = std::make_unique<TestDnsConfigService>();
44 notifier_task_runner_->PostTask(
45 FROM_HERE,
46 base::BindOnce(&TestDnsConfigService::OnHostsRead,
47 base::Unretained(test_service.get()), DnsHosts()));
48 test_config_service_ = test_service.get();
49
50 notifier_ = std::make_unique<SystemDnsConfigChangeNotifier>(
51 notifier_task_runner_, std::move(test_service));
52 }
53
54 protected:
55 // Test observer implementation that records all notifications received in a
56 // vector, and also validates that all notifications are received on the
57 // expected sequence.
58 class TestObserver : public SystemDnsConfigChangeNotifier::Observer {
59 public:
OnSystemDnsConfigChanged(std::optional<DnsConfig> config)60 void OnSystemDnsConfigChanged(std::optional<DnsConfig> config) override {
61 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
62 configs_received_.push_back(std::move(config));
63
64 DCHECK_GT(notifications_remaining_, 0);
65 if (--notifications_remaining_ == 0)
66 run_loop_->Quit();
67 }
68
WaitForNotification()69 void WaitForNotification() { WaitForNotifications(1); }
WaitForNotifications(int num_notifications)70 void WaitForNotifications(int num_notifications) {
71 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
72
73 notifications_remaining_ = num_notifications;
74 run_loop_->Run();
75 run_loop_ = std::make_unique<base::RunLoop>();
76 }
77
ExpectNoMoreNotifications()78 void ExpectNoMoreNotifications() {
79 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
80 configs_received_.clear();
81 base::RunLoop().RunUntilIdle();
82 EXPECT_TRUE(configs_received_.empty());
83 }
84
configs_received()85 std::vector<std::optional<DnsConfig>>& configs_received() {
86 return configs_received_;
87 }
88
89 private:
90 int notifications_remaining_ = 0;
91 std::unique_ptr<base::RunLoop> run_loop_ =
92 std::make_unique<base::RunLoop>();
93 std::vector<std::optional<DnsConfig>> configs_received_;
94 SEQUENCE_CHECKER(sequence_checker_);
95 };
96
97 // Load a config and wait for it to be received by the notifier.
LoadConfig(const DnsConfig & config,bool already_loaded=false)98 void LoadConfig(const DnsConfig& config, bool already_loaded = false) {
99 TestObserver observer;
100 notifier_->AddObserver(&observer);
101
102 // If |notifier_| already has a config loaded, |observer| will first get a
103 // notification for that initial config.
104 if (already_loaded)
105 observer.WaitForNotification();
106
107 notifier_task_runner_->PostTask(
108 FROM_HERE,
109 base::BindOnce(&TestDnsConfigService::OnConfigRead,
110 base::Unretained(test_config_service_), config));
111 observer.WaitForNotification();
112
113 notifier_->RemoveObserver(&observer);
114 }
115
116 scoped_refptr<base::SequencedTaskRunner> notifier_task_runner_;
117 std::unique_ptr<SystemDnsConfigChangeNotifier> notifier_;
118 // Owned by |notifier_|.
119 raw_ptr<TestDnsConfigService> test_config_service_;
120 };
121
TEST_F(SystemDnsConfigChangeNotifierTest,ReceiveNotification)122 TEST_F(SystemDnsConfigChangeNotifierTest, ReceiveNotification) {
123 TestObserver observer;
124
125 notifier_->AddObserver(&observer);
126 notifier_task_runner_->PostTask(
127 FROM_HERE,
128 base::BindOnce(&TestDnsConfigService::OnConfigRead,
129 base::Unretained(test_config_service_), kConfig));
130 observer.WaitForNotification();
131
132 EXPECT_THAT(observer.configs_received(),
133 testing::ElementsAre(testing::Optional(kConfig)));
134 observer.ExpectNoMoreNotifications();
135
136 notifier_->RemoveObserver(&observer);
137 }
138
TEST_F(SystemDnsConfigChangeNotifierTest,ReceiveNotification_Multiple)139 TEST_F(SystemDnsConfigChangeNotifierTest, ReceiveNotification_Multiple) {
140 TestObserver observer;
141
142 notifier_->AddObserver(&observer);
143 notifier_task_runner_->PostTask(
144 FROM_HERE,
145 base::BindOnce(&TestDnsConfigService::OnConfigRead,
146 base::Unretained(test_config_service_), kConfig));
147 notifier_task_runner_->PostTask(
148 FROM_HERE,
149 base::BindOnce(&TestDnsConfigService::OnConfigRead,
150 base::Unretained(test_config_service_), kConfig2));
151 observer.WaitForNotifications(2);
152
153 EXPECT_THAT(observer.configs_received(),
154 testing::ElementsAre(testing::Optional(kConfig),
155 testing::Optional(kConfig2)));
156 observer.ExpectNoMoreNotifications();
157
158 notifier_->RemoveObserver(&observer);
159 }
160
161 // If the notifier already has a config loaded, a new observer should receive an
162 // initial notification for that config.
TEST_F(SystemDnsConfigChangeNotifierTest,ReceiveInitialNotification)163 TEST_F(SystemDnsConfigChangeNotifierTest, ReceiveInitialNotification) {
164 LoadConfig(kConfig);
165
166 TestObserver observer;
167 notifier_->AddObserver(&observer);
168 observer.WaitForNotification();
169
170 EXPECT_THAT(observer.configs_received(),
171 testing::ElementsAre(testing::Optional(kConfig)));
172 observer.ExpectNoMoreNotifications();
173
174 notifier_->RemoveObserver(&observer);
175 }
176
177 // If multiple configs have been read before adding an Observer, should notify
178 // it only of the most recent.
TEST_F(SystemDnsConfigChangeNotifierTest,ReceiveInitialNotification_Multiple)179 TEST_F(SystemDnsConfigChangeNotifierTest, ReceiveInitialNotification_Multiple) {
180 LoadConfig(kConfig);
181 LoadConfig(kConfig2, true /* already_loaded */);
182
183 TestObserver observer;
184 notifier_->AddObserver(&observer);
185 observer.WaitForNotification();
186
187 EXPECT_THAT(observer.configs_received(),
188 testing::ElementsAre(testing::Optional(kConfig2)));
189 observer.ExpectNoMoreNotifications();
190
191 notifier_->RemoveObserver(&observer);
192 }
193
TEST_F(SystemDnsConfigChangeNotifierTest,NotificationsStopAfterRemoval)194 TEST_F(SystemDnsConfigChangeNotifierTest, NotificationsStopAfterRemoval) {
195 TestObserver observer;
196 notifier_->AddObserver(&observer);
197 notifier_->RemoveObserver(&observer);
198
199 LoadConfig(kConfig);
200 LoadConfig(kConfig2, true /* already_loaded */);
201
202 EXPECT_TRUE(observer.configs_received().empty());
203 observer.ExpectNoMoreNotifications();
204 }
205
TEST_F(SystemDnsConfigChangeNotifierTest,UnchangedConfigs)206 TEST_F(SystemDnsConfigChangeNotifierTest, UnchangedConfigs) {
207 LoadConfig(kConfig);
208
209 TestObserver observer;
210 notifier_->AddObserver(&observer);
211 observer.WaitForNotification();
212
213 // Expect no notifications from duplicate configs.
214 notifier_task_runner_->PostTask(
215 FROM_HERE,
216 base::BindOnce(&TestDnsConfigService::OnConfigRead,
217 base::Unretained(test_config_service_), kConfig));
218 notifier_task_runner_->PostTask(
219 FROM_HERE,
220 base::BindOnce(&TestDnsConfigService::OnConfigRead,
221 base::Unretained(test_config_service_), kConfig));
222 observer.ExpectNoMoreNotifications();
223
224 // Notification on new config.
225 notifier_task_runner_->PostTask(
226 FROM_HERE,
227 base::BindOnce(&TestDnsConfigService::OnConfigRead,
228 base::Unretained(test_config_service_), kConfig2));
229 observer.WaitForNotification();
230 EXPECT_THAT(observer.configs_received(),
231 testing::ElementsAre(testing::Optional(kConfig2)));
232 observer.ExpectNoMoreNotifications();
233
234 notifier_->RemoveObserver(&observer);
235 }
236
TEST_F(SystemDnsConfigChangeNotifierTest,UnloadedConfig)237 TEST_F(SystemDnsConfigChangeNotifierTest, UnloadedConfig) {
238 LoadConfig(kConfig);
239
240 TestObserver observer;
241 notifier_->AddObserver(&observer);
242 // Initial config.
243 observer.WaitForNotification();
244
245 notifier_task_runner_->PostTask(
246 FROM_HERE, base::BindOnce(&TestDnsConfigService::InvalidateConfig,
247 base::Unretained(test_config_service_)));
248 observer.WaitForNotification();
249
250 EXPECT_THAT(observer.configs_received(),
251 testing::ElementsAre(testing::Optional(kConfig), std::nullopt));
252 observer.ExpectNoMoreNotifications();
253
254 notifier_->RemoveObserver(&observer);
255 }
256
257 // All invalid configs are considered the same for notifications, so only expect
258 // a single notification on multiple config invalidations.
TEST_F(SystemDnsConfigChangeNotifierTest,UnloadedConfig_Multiple)259 TEST_F(SystemDnsConfigChangeNotifierTest, UnloadedConfig_Multiple) {
260 LoadConfig(kConfig);
261
262 TestObserver observer;
263 notifier_->AddObserver(&observer);
264 // Initial config.
265 observer.WaitForNotification();
266
267 notifier_task_runner_->PostTask(
268 FROM_HERE, base::BindOnce(&TestDnsConfigService::InvalidateConfig,
269 base::Unretained(test_config_service_)));
270 notifier_task_runner_->PostTask(
271 FROM_HERE, base::BindOnce(&TestDnsConfigService::InvalidateConfig,
272 base::Unretained(test_config_service_)));
273 observer.WaitForNotification(); // Only 1 notification expected.
274
275 EXPECT_THAT(observer.configs_received(),
276 testing::ElementsAre(testing::Optional(kConfig), std::nullopt));
277 observer.ExpectNoMoreNotifications();
278
279 notifier_->RemoveObserver(&observer);
280 }
281
TEST_F(SystemDnsConfigChangeNotifierTest,InitialConfigInvalid)282 TEST_F(SystemDnsConfigChangeNotifierTest, InitialConfigInvalid) {
283 // Add and invalidate a config (using an extra observer to wait for
284 // invalidation to complete).
285 LoadConfig(kConfig);
286 TestObserver setup_observer;
287 notifier_->AddObserver(&setup_observer);
288 setup_observer.WaitForNotification();
289 notifier_task_runner_->PostTask(
290 FROM_HERE, base::BindOnce(&TestDnsConfigService::InvalidateConfig,
291 base::Unretained(test_config_service_)));
292 setup_observer.WaitForNotification();
293 notifier_->RemoveObserver(&setup_observer);
294
295 TestObserver observer;
296 notifier_->AddObserver(&observer);
297
298 // No notification expected until first valid config.
299 observer.ExpectNoMoreNotifications();
300
301 // Notification on new config.
302 notifier_task_runner_->PostTask(
303 FROM_HERE,
304 base::BindOnce(&TestDnsConfigService::OnConfigRead,
305 base::Unretained(test_config_service_), kConfig));
306 observer.WaitForNotification();
307 EXPECT_THAT(observer.configs_received(),
308 testing::ElementsAre(testing::Optional(kConfig)));
309 observer.ExpectNoMoreNotifications();
310
311 notifier_->RemoveObserver(&observer);
312 }
313
TEST_F(SystemDnsConfigChangeNotifierTest,RefreshConfig)314 TEST_F(SystemDnsConfigChangeNotifierTest, RefreshConfig) {
315 test_config_service_->SetConfigForRefresh(kConfig);
316
317 TestObserver observer;
318 notifier_->AddObserver(&observer);
319
320 notifier_->RefreshConfig();
321 observer.WaitForNotification();
322
323 EXPECT_THAT(observer.configs_received(),
324 testing::ElementsAre(testing::Optional(kConfig)));
325 observer.ExpectNoMoreNotifications();
326
327 notifier_->RemoveObserver(&observer);
328 }
329
330 } // namespace net
331