OLD | NEW |
---|---|
(Empty) | |
1 // 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 <resolv.h> | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/compiler_specific.h" | |
9 #include "base/file_path.h" | |
10 #include "base/files/file_path_watcher.h" | |
11 #include "base/memory/ref_counted.h" | |
12 #include "base/message_loop.h" | |
13 #include "base/message_loop_proxy.h" | |
14 #include "base/scoped_ptr.h" | |
15 #include "base/threading/worker_pool.h" | |
16 #include "net/base/ip_endpoint.h" | |
17 #include "net/base/net_util.h" | |
18 #include "net/dns/dns_config_service.h" | |
19 | |
20 namespace net { | |
21 | |
22 namespace { | |
23 | |
24 using base::files::FilePathWatcher; | |
25 | |
26 // fills in |dns_config| from |res| | |
27 void ConfigFromResolver(const struct __res_state& res, DnsConfig* dns_config) { | |
28 CHECK(dns_config != NULL); | |
29 DCHECK(res.options & RES_INIT); | |
30 dns_config->nameservers.clear(); | |
31 for (int i = 0; i < res.nscount; ++i) { | |
32 IPEndPoint ipe; | |
33 if (ipe.FromSockAddr( | |
cbentzel
2011/08/04 18:09:27
You may want to handle IPv6 DNS servers here - or
szym
2011/08/04 18:55:10
I'll look into this.
| |
34 reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]), | |
35 sizeof res.nsaddr_list[i])) { | |
36 dns_config->nameservers.push_back(ipe); | |
37 } else { | |
38 NOTREACHED() << "could not convert nsaddr"; | |
cbentzel
2011/08/04 18:09:27
I think this function should return a boolean, and
szym
2011/08/04 18:55:10
Right. I was swayed by IPEndPoint::FromSockAddr wh
| |
39 } | |
40 } | |
41 | |
42 dns_config->search.clear(); | |
43 for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) { | |
44 dns_config->search.push_back(std::string(res.dnsrch[i])); | |
45 } | |
46 | |
47 dns_config->domain = res.defdname; // use only if |search| is empty | |
cbentzel
2011/08/04 18:09:27
This should be conditional on dns_config->search.e
szym
2011/08/04 18:55:10
Even though defdname stores all the strings in dns
cbentzel
2011/08/04 20:01:21
That seems good.
| |
48 dns_config->ndots = res.ndots; | |
49 dns_config->timeout = res.retrans; | |
cbentzel
2011/08/04 18:09:27
I wish there was a way to distinguish whether thes
szym
2011/08/04 18:55:10
I can't think of a way to do that without parsing
cbentzel
2011/08/04 20:01:21
Yeah, agree.
| |
50 dns_config->attempts = res.retry; | |
51 dns_config->rotate = res.options & RES_ROTATE; | |
52 dns_config->edns0 = res.options & RES_USE_EDNS0; | |
cbentzel
2011/08/04 18:09:27
It's probably good to pull in all of these things.
szym
2011/08/04 18:55:10
I'm not sure I understand. Should I remove those o
cbentzel
2011/08/04 20:01:21
No, keep them. I was musing about what we need to
| |
53 } | |
54 | |
55 // fills in |dns_config| using res_ninit, returns true on success | |
56 bool ReadConfig(DnsConfig* dns_config) { | |
57 // res_ninit is available on Linux (glibc 2+), Mac OS X, and Android. | |
58 // However, it might not be thread-safe on OpenBSD. | |
cbentzel
2011/08/04 18:09:27
I think it would be good to limit to only one res_
szym
2011/08/04 18:55:10
Done.
| |
59 struct __res_state res; | |
60 if (res_ninit(&res) != 0) | |
61 return false; | |
62 | |
63 ConfigFromResolver(res, dns_config); | |
64 return true; | |
65 } | |
66 | |
67 class DnsConfigServicePosix : public DnsConfigService { | |
68 public: | |
69 DnsConfigServicePosix() {} | |
70 virtual ~DnsConfigServicePosix() { | |
71 // we must be destroyed on the same thread that called Watch | |
72 if (watcher_delegate_.get()) | |
73 watcher_delegate_->Cancel(); | |
74 } | |
75 virtual bool Watch(DnsConfigService::Delegate* delegate) OVERRIDE; | |
76 | |
77 private: | |
78 // FilePathWatcher::Delegate is refcounted, so we separate it from the Service | |
79 // We'll also use it on the WorkerPool | |
80 class WatcherDelegate : public FilePathWatcher::Delegate { | |
81 public: | |
82 explicit WatcherDelegate(DnsConfigService::Delegate* delegate) | |
83 : message_loop_(base::MessageLoopProxy::CreateForCurrentThread()), | |
84 delegate_(delegate) {} | |
85 | |
86 // FilePathWatcher::Delegate interface | |
87 virtual void OnFilePathChanged(const FilePath& path) OVERRIDE { | |
88 DCHECK(message_loop_->BelongsToCurrentThread()); | |
89 if (!delegate_) | |
90 return; | |
91 if (!base::WorkerPool::PostTask(FROM_HERE, base::Bind( | |
92 &WatcherDelegate::DoRead, this), false)) { | |
93 delegate_->OnConfigError(); | |
cbentzel
2011/08/04 18:09:27
Yeah, I don't think you need to escalate this to t
szym
2011/08/04 18:55:10
Done.
| |
94 } | |
95 } | |
96 | |
97 virtual void OnFilePathError(const FilePath& path) OVERRIDE { | |
98 DCHECK(message_loop_->BelongsToCurrentThread()); | |
99 if (!delegate_) | |
100 return; | |
101 delegate_->OnConfigError(); | |
102 } | |
103 | |
104 // called on the worker thread | |
105 // reads DnsConfig and posts OnResultAvailable to |message_loop_| | |
106 void DoRead() { | |
107 // on the WorkerPool | |
108 DnsConfig config; | |
109 bool success = ReadConfig(&config); | |
110 message_loop_->PostTask(FROM_HERE, base::Bind( | |
cbentzel
2011/08/04 18:09:27
If it wasn't successful, I think we should just sk
szym
2011/08/04 18:55:10
Can I have a stupid while(!the_thing_to_do); or sh
cbentzel
2011/08/04 20:01:21
Well, if someone made a typo in resolv.conf and th
| |
111 &WatcherDelegate::OnResultAvailable, this, config, success)); | |
112 } | |
113 | |
114 // called on the delegate's thread | |
115 // communicates result from DnsConfigReader to the delegate | |
116 void OnResultAvailable(const DnsConfig &config, bool success) { | |
117 DCHECK(message_loop_->BelongsToCurrentThread()); | |
118 if (!delegate_) | |
119 return; | |
120 if (!success) { | |
121 delegate_->OnConfigError(); | |
122 } else if (config != last_config_) { | |
123 delegate_->OnConfigChanged(config); | |
124 last_config_ = config; | |
125 } | |
126 } | |
127 | |
128 void Cancel() { | |
129 DCHECK(message_loop_->BelongsToCurrentThread()); | |
130 delegate_ = NULL; | |
131 } | |
132 | |
133 private: | |
134 virtual ~WatcherDelegate() {} | |
135 | |
136 // message loop for the thread on which Watch is called | |
137 // this should be an IO loop | |
138 scoped_refptr<base::MessageLoopProxy> message_loop_; | |
139 DnsConfigService::Delegate* delegate_; | |
140 DnsConfig last_config_; | |
141 }; | |
142 | |
143 scoped_ptr<FilePathWatcher> resolv_file_watcher_; | |
144 scoped_refptr<WatcherDelegate> watcher_delegate_; | |
145 }; | |
cbentzel
2011/08/04 18:09:27
DISALLOW_COPY_AND_ASSIGN
szym
2011/08/04 18:55:10
Done.
| |
146 | |
147 bool DnsConfigServicePosix::Watch(DnsConfigService::Delegate* delegate) { | |
148 DCHECK(!resolv_file_watcher_.get()); | |
149 DCHECK(!watcher_delegate_.get()); | |
150 DCHECK(MessageLoopForIO::current()); | |
151 | |
152 // start FilePathWatcher | |
153 FilePath path(FILE_PATH_LITERAL("/etc/resolv.conf")); | |
cbentzel
2011/08/04 18:24:56
Is _PATH_RESCONF defined correctly in the differen
szym
2011/08/04 18:55:10
I'll add a #ifndef _PATH_RESCONF #define _PATH_RES
| |
154 watcher_delegate_ = new WatcherDelegate(delegate); | |
155 resolv_file_watcher_.reset(new FilePathWatcher()); | |
156 bool result = resolv_file_watcher_->Watch(path, watcher_delegate_); | |
157 // make the initial read | |
158 watcher_delegate_->OnFilePathChanged(path); | |
159 return result; | |
160 } | |
161 | |
162 } // namespace | |
163 | |
164 DnsConfigService* DnsConfigService::CreateSystemService() { | |
165 return new DnsConfigServicePosix(); | |
166 } | |
cbentzel
2011/08/04 18:09:27
Nit: newline between here and // namespace net.
szym
2011/08/04 18:55:10
Done.
| |
167 } // namespace net | |
168 | |
OLD | NEW |