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