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/observer_list.h" | |
15 #include "base/scoped_ptr.h" | |
16 #include "base/threading/worker_pool.h" | |
17 #include "net/base/ip_endpoint.h" | |
18 #include "net/base/net_util.h" | |
19 #include "net/dns/dns_config_service.h" | |
20 | |
21 #ifndef _PATH_RESCONF | |
22 #define _PATH_RESCONF "/etc/resolv.conf" | |
23 #endif | |
24 | |
25 namespace net { | |
26 | |
27 namespace { | |
28 | |
29 using base::files::FilePathWatcher; | |
30 | |
31 // Fills in |dns_config| from |res|. | |
32 bool ConfigFromResolver(const struct __res_state& res, DnsConfig* dns_config) { | |
33 CHECK(dns_config != NULL); | |
34 DCHECK(res.options & RES_INIT); | |
35 | |
36 dns_config->nameservers.clear(); | |
37 for (int i = 0; i < res.nscount; ++i) { | |
38 IPEndPoint ipe; | |
39 if (ipe.FromSockAddr( | |
40 reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]), | |
41 sizeof res.nsaddr_list[i])) { | |
42 dns_config->nameservers.push_back(ipe); | |
43 } else { | |
44 return false; | |
45 } | |
46 } | |
47 | |
48 // TODO(szym): this might not compile on mac. | |
cbentzel
2011/08/05 19:11:39
Can you run try jobs to confirm?
May also want t
szym
2011/08/15 14:27:42
glibc uses funny code to determine the ordering of
| |
49 for (int i = 0; i < res._u._ext.nscount6; ++i) { | |
50 IPEndPoint ipe; | |
51 if (ipe.FromSockAddr( | |
52 reinterpret_cast<const struct sockaddr*>(&res._u._ext.nsaddrs[i]), | |
53 sizeof res._u._ext.nsaddrs[i])) { | |
54 dns_config->nameservers.push_back(ipe); | |
55 } else { | |
56 return false; | |
57 } | |
58 } | |
59 | |
60 dns_config->search.clear(); | |
61 for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) { | |
62 dns_config->search.push_back(std::string(res.dnsrch[i])); | |
63 } | |
64 | |
65 dns_config->ndots = res.ndots; | |
66 dns_config->timeout = res.retrans; | |
67 dns_config->attempts = res.retry; | |
68 dns_config->rotate = res.options & RES_ROTATE; | |
69 dns_config->edns0 = res.options & RES_USE_EDNS0; | |
70 | |
71 return true; | |
72 } | |
73 | |
74 // Fills in |dns_config| using res_ninit, returns true on success. | |
75 bool ReadConfig(DnsConfig* dns_config) { | |
76 // res_ninit is available on Linux (glibc 2+), Mac OS X, and Android. | |
77 // It might be thread-safe, but to ensure ordering we run only one at a time. | |
78 struct __res_state res; | |
79 if (res_ninit(&res) != 0) | |
80 return false; | |
81 | |
82 return ConfigFromResolver(res, dns_config); | |
83 } | |
84 | |
85 class DnsConfigServicePosix : public DnsConfigService { | |
86 public: | |
87 DnsConfigServicePosix(); | |
88 virtual ~DnsConfigServicePosix() { | |
89 // The watcher must be destroyed on the same thread that called Watch. | |
90 if (watcher_delegate_.get()) | |
91 watcher_delegate_->Cancel(); | |
92 } | |
93 | |
94 virtual void AddObserver(Observer* observer) { | |
95 observers_.AddObserver(observer); | |
96 } | |
97 | |
98 virtual void RemoveObserver(Observer* observer) { | |
99 observers_.RemoveObserver(observer); | |
100 } | |
101 | |
102 private: | |
103 // FilePathWatcher::Delegate is refcounted, so we separate it from the Service | |
104 // We'll also use it on the WorkerPool. | |
105 class WatcherDelegate : public FilePathWatcher::Delegate { | |
cbentzel
2011/08/05 19:11:39
Might be clearer to make this a non-nested class -
szym
2011/08/15 14:27:42
Done.
| |
106 public: | |
107 explicit WatcherDelegate(DnsConfigServicePosix* service) | |
108 : message_loop_(base::MessageLoopProxy::CreateForCurrentThread()), | |
109 service_(service) {} | |
cbentzel
2011/08/05 19:11:39
You need to initialize reading_(false) and read_pe
szym
2011/08/15 14:27:42
Done. Good catch. I wrongly assumed bool() == fals
| |
110 | |
111 // FilePathWatcher::Delegate interface | |
112 virtual void OnFilePathChanged(const FilePath& path) OVERRIDE { | |
113 DCHECK(message_loop_->BelongsToCurrentThread()); | |
114 if (!service_) | |
115 return; | |
116 ScheduleRead(); | |
117 } | |
118 | |
119 virtual void OnFilePathError(const FilePath& path) OVERRIDE { | |
120 DCHECK(message_loop_->BelongsToCurrentThread()); | |
121 StartWatch(); | |
122 } | |
123 | |
124 void Cancel() { | |
125 DCHECK(message_loop_->BelongsToCurrentThread()); | |
126 service_ = NULL; | |
127 } | |
128 | |
129 void ScheduleWatch() { | |
130 // Retry Watch in 100ms or more. | |
131 message_loop_->PostDelayedTask( | |
132 FROM_HERE, base::Bind(&WatcherDelegate::StartWatch, this), 100); | |
133 } | |
134 | |
135 private: | |
136 virtual ~WatcherDelegate() {} | |
137 | |
138 void ScheduleRead() { | |
139 if (reading_) { | |
140 read_pending_ = true; | |
141 } else { | |
142 // This can't fail on Linux. | |
143 base::WorkerPool::PostTask(FROM_HERE, base::Bind( | |
144 &WatcherDelegate::DoRead, this), false); | |
145 reading_ = true; | |
146 read_pending_ = false; | |
147 } | |
148 } | |
149 | |
150 // Reads DnsConfig and posts OnResultAvailable to |message_loop_|. | |
151 // Must be called on the worker thread. | |
152 void DoRead() { | |
153 DnsConfig config; | |
154 bool success = ReadConfig(&config); | |
155 // If this fails, the loop is gone, so there is no point retrying. | |
156 message_loop_->PostTask(FROM_HERE, base::Bind( | |
157 &WatcherDelegate::OnResultAvailable, this, config, success)); | |
158 } | |
159 | |
160 // Communicates result to the service. Must be called on the the same thread | |
161 // that constructed WatcherDelegate. | |
162 void OnResultAvailable(const DnsConfig &config, bool success) { | |
163 DCHECK(message_loop_->BelongsToCurrentThread()); | |
164 if (!service_) | |
165 return; | |
166 reading_ = false; | |
167 if (read_pending_) { | |
168 // Discard this result and re-schedule. | |
169 ScheduleRead(); | |
170 return; | |
171 } | |
172 if (!success) { | |
173 VLOG(1) << "Failed to read DnsConfig"; | |
174 } else { | |
175 service_->OnConfigRead(config); | |
176 } | |
177 } | |
178 | |
179 void StartWatch() { | |
180 if (!service_) | |
181 return; | |
182 service_->StartWatch(); | |
183 } | |
184 | |
185 // Message loop for the thread on which Watch is called (of TYPE_IO). | |
186 scoped_refptr<base::MessageLoopProxy> message_loop_; | |
187 DnsConfigServicePosix* service_; | |
188 // True after DoRead before OnResultsAvailable | |
189 bool reading_; | |
190 // True after OnFilePathChanged fires while |reading_| is true. | |
191 bool read_pending_; | |
192 }; | |
193 | |
194 void OnConfigRead(const DnsConfig& config) { | |
195 if (!config.Equals(dns_config_)) { | |
196 dns_config_ = config; | |
197 FOR_EACH_OBSERVER(Observer, observers_, OnConfigChanged(config)); | |
198 } | |
199 } | |
200 | |
201 // Configure a FilePathWatcher and install watcher_delegate_. | |
202 void StartWatch(); | |
203 | |
204 scoped_ptr<FilePathWatcher> resolv_file_watcher_; | |
205 scoped_refptr<WatcherDelegate> watcher_delegate_; | |
206 ObserverList<Observer> observers_; | |
207 | |
208 DISALLOW_COPY_AND_ASSIGN(DnsConfigServicePosix); | |
209 }; | |
210 | |
211 DnsConfigServicePosix::DnsConfigServicePosix() { | |
212 DCHECK(!resolv_file_watcher_.get()); | |
213 DCHECK(!watcher_delegate_.get()); | |
214 DCHECK(MessageLoopForIO::current()); | |
215 | |
216 watcher_delegate_ = new WatcherDelegate(this); | |
217 StartWatch(); | |
218 } | |
219 | |
220 void DnsConfigServicePosix::StartWatch() { | |
221 DCHECK(watcher_delegate_.get()); | |
222 | |
223 FilePath path(FILE_PATH_LITERAL(_PATH_RESCONF)); | |
224 | |
225 // FilePathWatcher allows only one Watch per lifetime, so we need a new one. | |
226 resolv_file_watcher_.reset(new FilePathWatcher()); | |
227 if (!resolv_file_watcher_->Watch(path, watcher_delegate_)) { | |
228 watcher_delegate_->ScheduleWatch(); | |
229 } else { | |
230 // Make the initial read after watch is installed. | |
231 watcher_delegate_->OnFilePathChanged(path); | |
232 } | |
233 } | |
234 | |
235 } // namespace | |
236 | |
237 DnsConfigService* DnsConfigService::CreateSystemService() { | |
238 return new DnsConfigServicePosix(); | |
239 } | |
240 | |
241 } // namespace net | |
242 | |
OLD | NEW |