blob: d38d5897076091894b7e5814193c5a22229a8770 [file] [log] [blame]
[email protected]d84316a2011-08-18 04:41:211// Copyright (c) 2011 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 <arpa/inet.h>
6#include <resolv.h>
7
8#include "base/bind.h"
9#include "base/compiler_specific.h"
10#include "base/message_loop.h"
11#include "base/message_loop_proxy.h"
12#include "base/synchronization/waitable_event.h"
13#include "base/threading/thread.h"
14#include "net/base/ip_endpoint.h"
15#include "net/dns/dns_config_service_posix.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18namespace net {
19
20namespace {
21
22void CompareConfig(const struct __res_state &res, const DnsConfig& config) {
23 EXPECT_EQ(config.ndots, static_cast<int>(res.ndots));
24 EXPECT_EQ(config.edns0, (res.options & RES_USE_EDNS0) != 0);
25 EXPECT_EQ(config.rotate, (res.options & RES_ROTATE) != 0);
26 EXPECT_EQ(config.timeout.InSeconds(), res.retrans);
27 EXPECT_EQ(config.attempts, res.retry);
28
29 // Compare nameservers. IPv6 precede IPv4.
[email protected]5c5b7382011-08-18 18:02:3330#if defined(OS_LINUX)
[email protected]d84316a2011-08-18 04:41:2131 size_t nscount6 = res._u._ext.nscount6;
32#else
33 size_t nscount6 = 0;
34#endif
35 size_t nscount4 = res.nscount;
36 ASSERT_EQ(config.nameservers.size(), nscount6 + nscount4);
[email protected]5c5b7382011-08-18 18:02:3337#if defined(OS_LINUX)
[email protected]d84316a2011-08-18 04:41:2138 for (size_t i = 0; i < nscount6; ++i) {
39 IPEndPoint ipe;
40 EXPECT_TRUE(ipe.FromSockAddr(
41 reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]),
42 sizeof *res._u._ext.nsaddrs[i]));
43 EXPECT_EQ(config.nameservers[i], ipe);
44 }
45#endif
46 for (size_t i = 0; i < nscount4; ++i) {
47 IPEndPoint ipe;
48 EXPECT_TRUE(ipe.FromSockAddr(
49 reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]),
50 sizeof res.nsaddr_list[i]));
51 EXPECT_EQ(config.nameservers[nscount6 + i], ipe);
52 }
53
54 ASSERT_TRUE(config.search.size() <= MAXDNSRCH);
55 EXPECT_TRUE(res.dnsrch[config.search.size()] == NULL);
56 for (size_t i = 0; i < config.search.size(); ++i) {
57 EXPECT_EQ(config.search[i], res.dnsrch[i]);
58 }
59}
60
61// Fills in |res| with sane configuration. Change |generation| to add diversity.
62void InitializeResState(res_state res, int generation) {
63 memset(res, 0, sizeof(*res));
64 res->options = RES_INIT | RES_ROTATE;
65 res->ndots = 2;
66 res->retrans = 8;
67 res->retry = 7;
68
69 const char kDnsrch[] = "chromium.org" "\0" "example.com";
70 memcpy(res->defdname, kDnsrch, sizeof(kDnsrch));
71 res->dnsrch[0] = res->defdname;
72 res->dnsrch[1] = res->defdname + sizeof("chromium.org");
73
74 const char* ip4addr[3] = {
75 "8.8.8.8",
76 "192.168.1.1",
77 "63.1.2.4",
78 };
79
80 for (int i = 0; i < 3; ++i) {
81 struct sockaddr_in sa;
82 sa.sin_family = AF_INET;
83 sa.sin_port = htons(NS_DEFAULTPORT + i - generation);
84 inet_pton(AF_INET, ip4addr[i], &sa.sin_addr);
85 res->nsaddr_list[i] = sa;
86 }
87 res->nscount = 3;
88
[email protected]5c5b7382011-08-18 18:02:3389#if defined(OS_LINUX)
[email protected]d84316a2011-08-18 04:41:2190 const char* ip6addr[2] = {
91 "2001:db8:0::42",
92 "::FFFF:129.144.52.38",
93 };
94
95 for (int i = 0; i < 2; ++i) {
96 // Must use malloc to mimick res_ninit.
97 struct sockaddr_in6 *sa6;
98 sa6 = (struct sockaddr_in6 *)malloc(sizeof(*sa6));
99 sa6->sin6_family = AF_INET6;
100 sa6->sin6_port = htons(NS_DEFAULTPORT - i);
101 inet_pton(AF_INET6, ip6addr[i], &sa6->sin6_addr);
102 res->_u._ext.nsaddrs[i] = sa6;
103 }
104 res->_u._ext.nscount6 = 2;
105#endif
106}
107
108void CloseResState(res_state res) {
[email protected]5c5b7382011-08-18 18:02:33109#if defined(OS_LINUX)
[email protected]d84316a2011-08-18 04:41:21110 for (int i = 0; i < res->_u._ext.nscount6; ++i) {
111 ASSERT_TRUE(res->_u._ext.nsaddrs[i] != NULL);
112 free(res->_u._ext.nsaddrs[i]);
113 }
114#endif
115}
116
117class DnsConfigServiceTest : public testing::Test,
118 public DnsConfigService::Observer {
119 public:
120 // Mocks
121
122 // DnsConfigService owns the instances of ResolverLib and
123 // FilePathWatcherFactory that it gets, so use simple proxies to call
124 // DnsConfigServiceTest.
125
126 // ResolverLib is owned by WatcherDelegate which is posted to WorkerPool so
127 // it must be canceled before the test is over.
128 class MockResolverLib : public DnsConfigServicePosix::ResolverLib {
129 public:
130 explicit MockResolverLib(DnsConfigServiceTest *test) : test_(test) {}
131 virtual ~MockResolverLib() {
132 base::AutoLock lock(lock_);
133 if (test_) {
134 EXPECT_TRUE(test_->IsComplete());
135 }
136 }
137 virtual int ninit(res_state res) OVERRIDE {
138 base::AutoLock lock(lock_);
139 if (test_)
140 return test_->OnNinit(res);
141 else
142 return -1;
143 }
144 virtual void nclose(res_state res) OVERRIDE {
145 CloseResState(res);
146 }
147 void Cancel() {
148 base::AutoLock lock(lock_);
149 test_ = NULL;
150 }
151 private:
152 base::Lock lock_;
153 DnsConfigServiceTest *test_;
154 };
155
156 class MockFilePathWatcherShim
157 : public DnsConfigServicePosix::FilePathWatcherShim {
158 public:
159 typedef base::files::FilePathWatcher::Delegate Delegate;
160
161 explicit MockFilePathWatcherShim(DnsConfigServiceTest* t) : test_(t) {}
162 virtual ~MockFilePathWatcherShim() {
163 test_->OnShimDestroyed(this);
164 }
165
166 // Enforce one-Watch-per-lifetime as the original FilePathWatcher
167 virtual bool Watch(const FilePath& path,
168 Delegate* delegate) OVERRIDE {
169 EXPECT_TRUE(path_.empty()) << "Only one-Watch-per-lifetime allowed.";
170 EXPECT_TRUE(!delegate_.get());
171 path_ = path;
172 delegate_ = delegate;
173 return test_->OnWatch();
174 }
175
176 void PathChanged() {
177 delegate_->OnFilePathChanged(path_);
178 }
179
180 void PathError() {
181 delegate_->OnFilePathError(path_);
182 }
183
184 private:
185 FilePath path_;
186 scoped_refptr<Delegate> delegate_;
187 DnsConfigServiceTest* test_;
188 };
189
190 class MockFilePathWatcherFactory
191 : public DnsConfigServicePosix::FilePathWatcherFactory {
192 public:
193 explicit MockFilePathWatcherFactory(DnsConfigServiceTest* t) : test(t) {}
194 virtual ~MockFilePathWatcherFactory() {
195 EXPECT_TRUE(test->IsComplete());
196 }
197 virtual DnsConfigServicePosix::FilePathWatcherShim*
198 CreateFilePathWatcher() OVERRIDE {
199 return test->CreateFilePathWatcher();
200 }
201 DnsConfigServiceTest* test;
202 };
203
204 // Helpers for mocks.
205
206 DnsConfigServicePosix::FilePathWatcherShim* CreateFilePathWatcher() {
207 watcher_shim_ = new MockFilePathWatcherShim(this);
208 return watcher_shim_;
209 }
210
211 void OnShimDestroyed(MockFilePathWatcherShim* destroyed_shim) {
212 // Precaution to avoid segfault.
213 if (watcher_shim_ == destroyed_shim)
214 watcher_shim_ = NULL;
215 }
216
217 // On each event, post QuitTask to allow use of MessageLoop::Run() to
218 // synchronize the threads.
219
220 bool OnWatch() {
221 EXPECT_TRUE(message_loop_ == MessageLoop::current());
222 watch_called_ = true;
223 BreakNow("OnWatch");
224 return !fail_on_watch_;
225 }
226
227 int OnNinit(res_state res) {
228 { // Check that res_ninit is executed serially.
229 base::AutoLock lock(ninit_lock_);
230 EXPECT_FALSE(ninit_running_) << "res_ninit is not called serially!";
231 ninit_running_ = true;
232 }
233 BreakNow("OnNinit");
234 ninit_allowed_.Wait();
235 // Calling from another thread is a bit dirty, but it's protected.
236 int rv = OnNinitNonThreadSafe(res);
237 // This lock might be destroyed after ninit_called_ is signalled.
238 {
239 base::AutoLock lock(ninit_lock_);
240 ninit_running_ = false;
241 }
242 ninit_called_.Signal();
243 return rv;
244 }
245
246 virtual void OnConfigChanged(const DnsConfig& new_config) OVERRIDE {
247 EXPECT_TRUE(message_loop_ == MessageLoop::current());
248 CompareConfig(res_, new_config);
249 EXPECT_FALSE(new_config.Equals(last_config_)) <<
250 "Config must be different from last call.";
251 last_config_ = new_config;
252 got_config_ = true;
253 BreakNow("OnConfigChanged");
254 }
255
256 bool IsComplete() {
257 return complete_;
258 }
259
260 protected:
261 friend class BreakTask;
262 class BreakTask : public Task {
263 public:
264 BreakTask(DnsConfigServiceTest* test, std::string breakpoint)
265 : test_(test), breakpoint_(breakpoint) {}
266 virtual ~BreakTask() {}
267 virtual void Run() OVERRIDE {
268 test_->breakpoint_ = breakpoint_;
269 MessageLoop::current()->QuitNow();
270 }
271 private:
272 DnsConfigServiceTest* test_;
273 std::string breakpoint_;
274 };
275
276 void BreakNow(std::string b) {
277 message_loop_->PostTask(FROM_HERE, new BreakTask(this, b));
278 }
279
280 void RunUntilBreak(std::string b) {
281 message_loop_->Run();
282 ASSERT_EQ(breakpoint_, b);
283 }
284
285 DnsConfigServiceTest()
286 : res_lib_(new MockResolverLib(this)),
287 watcher_shim_(NULL),
288 res_generation_(1),
289 watch_called_(false),
290 got_config_(false),
291 fail_on_watch_(false),
292 fail_on_ninit_(false),
293 complete_(false),
294 ninit_allowed_(false, false),
295 ninit_called_(false, false),
296 ninit_running_(false) {
297 }
298
299 // This is on WorkerPool, but protected by ninit_allowed_.
300 int OnNinitNonThreadSafe(res_state res) {
301 if (fail_on_ninit_)
302 return -1;
303 InitializeResState(res, res_generation_);
304 // Store a (deep) copy in the fixture to later verify correctness.
305 CloseResState(&res_);
306 InitializeResState(&res_, res_generation_);
307 return 0;
308 }
309
310 // Helpers for tests.
311
312 // Lets OnNinit run OnNinitNonThreadSafe and waits for it to complete.
313 // Might get OnConfigChanged scheduled on the loop but we have no certainty.
314 void WaitForNinit() {
315 RunUntilBreak("OnNinit");
316 ninit_allowed_.Signal();
317 ninit_called_.Wait();
318 }
319
320 // test::Test methods
321 virtual void SetUp() OVERRIDE {
322 message_loop_ = MessageLoop::current();
323 service_.reset(new DnsConfigServicePosix());
324 service_->set_resolver_lib(res_lib_);
325 service_->set_watcher_factory(new MockFilePathWatcherFactory(this));
326 memset(&res_, 0, sizeof(res_));
327 }
328
329 virtual void TearDown() OVERRIDE {
330 // res_lib_ could outlive the test, so make sure it doesn't call it.
331 res_lib_->Cancel();
332 CloseResState(&res_);
333 // Allow service_ to clean up ResolverLib and FilePathWatcherFactory.
334 complete_ = true;
335 }
336
337 MockResolverLib* res_lib_;
338 MockFilePathWatcherShim* watcher_shim_;
339 // Adds variety to the content of res_state.
340 int res_generation_;
341 struct __res_state res_;
342 DnsConfig last_config_;
343
344 bool watch_called_;
345 bool got_config_;
346 bool fail_on_watch_;
347 bool fail_on_ninit_;
348 bool complete_;
349
350 // Ninit is called on WorkerPool so we need to synchronize with it.
351 base::WaitableEvent ninit_allowed_;
352 base::WaitableEvent ninit_called_;
353
354 // Protected by ninit_lock_. Used to verify that Ninit calls are serialized.
355 bool ninit_running_;
356 base::Lock ninit_lock_;
357
358 // Loop for this thread.
359 MessageLoop* message_loop_;
360
361 // Service under test.
362 scoped_ptr<DnsConfigServicePosix> service_;
363
364 std::string breakpoint_;
365};
366
367TEST(DnsConfigTest, ResolverConfigConvertAndEquals) {
368 struct __res_state res[2];
369 DnsConfig config[2];
370 for (int i = 0; i < 2; ++i) {
371 InitializeResState(&res[i], i);
372 ASSERT_TRUE(ConvertResToConfig(res[i], &config[i]));
373 }
374 for (int i = 0; i < 2; ++i) {
375 CompareConfig(res[i], config[i]);
376 CloseResState(&res[i]);
377 }
378 EXPECT_TRUE(config[0].Equals(config[0]));
379 EXPECT_FALSE(config[0].Equals(config[1]));
380 EXPECT_FALSE(config[1].Equals(config[0]));
381}
382
383TEST_F(DnsConfigServiceTest, FilePathWatcherFailures) {
384 // For these tests, disable ninit.
385 res_lib_->Cancel();
386
387 fail_on_watch_ = true;
388 service_->Watch();
389 RunUntilBreak("OnWatch");
390 EXPECT_TRUE(watch_called_) << "Must call FilePathWatcher::Watch().";
391
392 fail_on_watch_ = false;
393 watch_called_ = false;
394 RunUntilBreak("OnWatch"); // Due to backoff this will take 100ms.
395 EXPECT_TRUE(watch_called_) <<
396 "Must restart on FilePathWatcher::Watch() failure.";
397
398 watch_called_ = false;
399 ASSERT_TRUE(watcher_shim_);
400 watcher_shim_->PathError();
401 RunUntilBreak("OnWatch");
402 EXPECT_TRUE(watch_called_) <<
403 "Must restart on FilePathWatcher::Delegate::OnFilePathError().";
404
405 // Worker thread could still be posting OnResultAvailable to the message loop
406}
407
408TEST_F(DnsConfigServiceTest, NotifyOnValidAndDistinctConfig) {
409 service_->AddObserver(this);
410 service_->Watch();
411 RunUntilBreak("OnWatch");
412 fail_on_ninit_ = true;
413 WaitForNinit();
414
415 // If OnNinit posts OnResultAvailable before the next call, then this test
416 // verifies that failure on ninit should not cause OnConfigChanged.
417 // Otherwise, this only verifies that ninit calls are serialized.
418
419 fail_on_ninit_ = false;
420 ASSERT_TRUE(watcher_shim_);
421 watcher_shim_->PathChanged();
422 WaitForNinit();
423
424 RunUntilBreak("OnConfigChanged");
425 EXPECT_TRUE(got_config_);
426
427 message_loop_->AssertIdle();
428
429 got_config_ = false;
430 // Forget about the config to test if we get it again on AddObserver.
431 last_config_ = DnsConfig();
432 service_->RemoveObserver(this);
433 service_->AddObserver(this);
434 RunUntilBreak("OnConfigChanged");
435 EXPECT_TRUE(got_config_) << "Did not get config after AddObserver.";
436
437 // Simulate spurious FilePathChanged.
438 ASSERT_TRUE(watcher_shim_);
439 watcher_shim_->PathChanged();
440 WaitForNinit();
441
442 // OnConfigChanged will catch that the config did not actually change.
443
444 got_config_ = false;
445 ++res_generation_;
446 ASSERT_TRUE(watcher_shim_);
447 watcher_shim_->PathChanged();
448 WaitForNinit();
449 RunUntilBreak("OnConfigChanged");
450 EXPECT_TRUE(got_config_) << "Did not get config after change";
451
452 message_loop_->AssertIdle();
453
454 // Schedule two calls. OnNinit checks if it is called serially.
455 ++res_generation_;
456 ASSERT_TRUE(watcher_shim_);
457 watcher_shim_->PathChanged();
458 // ninit is blocked, so this will have to induce read_pending
459 watcher_shim_->PathChanged();
460 WaitForNinit();
461 WaitForNinit();
462 RunUntilBreak("OnConfigChanged");
463 EXPECT_TRUE(got_config_) << "Did not get config after change";
464
465 // We should be done with all tasks.
466 message_loop_->AssertIdle();
467}
468
469} // namespace
470
471} // namespace net
472