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