Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / net / dns / dns_config_service_posix.cc
blob2c298ddedbb8c98ce46a950929eba2616322baa5
1 // Copyright (c) 2012 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.
5 #include "net/dns/dns_config_service_posix.h"
7 #include <string>
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_path_watcher.h"
13 #include "base/lazy_instance.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/metrics/histogram.h"
16 #include "base/time/time.h"
17 #include "net/base/ip_endpoint.h"
18 #include "net/base/net_util.h"
19 #include "net/dns/dns_hosts.h"
20 #include "net/dns/dns_protocol.h"
21 #include "net/dns/notify_watcher_mac.h"
22 #include "net/dns/serial_worker.h"
24 #if defined(OS_MACOSX) && !defined(OS_IOS)
25 #include "net/dns/dns_config_watcher_mac.h"
26 #endif
28 #if defined(OS_ANDROID)
29 #include <sys/system_properties.h>
30 #include "net/base/network_change_notifier.h"
31 #endif
33 namespace net {
35 namespace internal {
37 namespace {
39 #if !defined(OS_ANDROID)
40 const base::FilePath::CharType* kFilePathHosts =
41 FILE_PATH_LITERAL("/etc/hosts");
42 #else
43 const base::FilePath::CharType* kFilePathHosts =
44 FILE_PATH_LITERAL("/system/etc/hosts");
45 #endif
47 #if defined(OS_IOS)
49 // There is no public API to watch the DNS configuration on iOS.
50 class DnsConfigWatcher {
51 public:
52 typedef base::Callback<void(bool succeeded)> CallbackType;
54 bool Watch(const CallbackType& callback) {
55 return false;
59 #elif defined(OS_ANDROID)
60 // On Android, assume DNS config may have changed on every network change.
61 class DnsConfigWatcher : public NetworkChangeNotifier::NetworkChangeObserver {
62 public:
63 DnsConfigWatcher() {
64 NetworkChangeNotifier::AddNetworkChangeObserver(this);
67 virtual ~DnsConfigWatcher() {
68 NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
71 bool Watch(const base::Callback<void(bool succeeded)>& callback) {
72 callback_ = callback;
73 return true;
76 virtual void OnNetworkChanged(NetworkChangeNotifier::ConnectionType type)
77 OVERRIDE {
78 if (!callback_.is_null() && type != NetworkChangeNotifier::CONNECTION_NONE)
79 callback_.Run(true);
82 private:
83 base::Callback<void(bool succeeded)> callback_;
85 #elif !defined(OS_MACOSX)
86 // DnsConfigWatcher for OS_MACOSX is in dns_config_watcher_mac.{hh,cc}.
88 #ifndef _PATH_RESCONF // Normally defined in <resolv.h>
89 #define _PATH_RESCONF "/etc/resolv.conf"
90 #endif
92 static const base::FilePath::CharType* kFilePathConfig =
93 FILE_PATH_LITERAL(_PATH_RESCONF);
95 class DnsConfigWatcher {
96 public:
97 typedef base::Callback<void(bool succeeded)> CallbackType;
99 bool Watch(const CallbackType& callback) {
100 callback_ = callback;
101 return watcher_.Watch(base::FilePath(kFilePathConfig), false,
102 base::Bind(&DnsConfigWatcher::OnCallback,
103 base::Unretained(this)));
106 private:
107 void OnCallback(const base::FilePath& path, bool error) {
108 callback_.Run(!error);
111 base::FilePathWatcher watcher_;
112 CallbackType callback_;
114 #endif
116 #if !defined(OS_ANDROID)
117 ConfigParsePosixResult ReadDnsConfig(DnsConfig* config) {
118 ConfigParsePosixResult result;
119 config->unhandled_options = false;
120 #if defined(OS_OPENBSD)
121 // Note: res_ninit in glibc always returns 0 and sets RES_INIT.
122 // res_init behaves the same way.
123 memset(&_res, 0, sizeof(_res));
124 if (res_init() == 0) {
125 result = ConvertResStateToDnsConfig(_res, config);
126 } else {
127 result = CONFIG_PARSE_POSIX_RES_INIT_FAILED;
129 #else // all other OS_POSIX
130 struct __res_state res;
131 memset(&res, 0, sizeof(res));
132 if (res_ninit(&res) == 0) {
133 result = ConvertResStateToDnsConfig(res, config);
134 } else {
135 result = CONFIG_PARSE_POSIX_RES_INIT_FAILED;
137 // Prefer res_ndestroy where available.
138 #if defined(OS_MACOSX) || defined(OS_FREEBSD)
139 res_ndestroy(&res);
140 #else
141 res_nclose(&res);
142 #endif
143 #endif
145 #if defined(OS_MACOSX) && !defined(OS_IOS)
146 ConfigParsePosixResult error = DnsConfigWatcher::CheckDnsConfig();
147 switch (error) {
148 case CONFIG_PARSE_POSIX_OK:
149 break;
150 case CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS:
151 LOG(WARNING) << "dns_config has unhandled options!";
152 config->unhandled_options = true;
153 default:
154 return error;
156 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
157 // Override timeout value to match default setting on Windows.
158 config->timeout = base::TimeDelta::FromSeconds(kDnsTimeoutSeconds);
159 return result;
161 #else // defined(OS_ANDROID)
162 // Theoretically, this is bad. __system_property_get is not a supported API
163 // (but it's currently visible to anyone using Bionic), and the properties
164 // are implementation details that may disappear in future Android releases.
165 // Practically, libcutils provides property_get, which is a public API, and the
166 // DNS code (and its clients) are already robust against failing to get the DNS
167 // config for whatever reason, so the properties can disappear and the world
168 // won't end.
169 // TODO(ttuttle): Depend on libcutils, then switch this (and other uses of
170 // __system_property_get) to property_get.
171 ConfigParsePosixResult ReadDnsConfig(DnsConfig* dns_config) {
172 std::string dns1_string, dns2_string;
173 char property_value[PROP_VALUE_MAX];
174 __system_property_get("net.dns1", property_value);
175 dns1_string = property_value;
176 __system_property_get("net.dns2", property_value);
177 dns2_string = property_value;
178 if (dns1_string.length() == 0 && dns2_string.length() == 0)
179 return CONFIG_PARSE_POSIX_NO_NAMESERVERS;
181 IPAddressNumber dns1_number, dns2_number;
182 bool parsed1 = ParseIPLiteralToNumber(dns1_string, &dns1_number);
183 bool parsed2 = ParseIPLiteralToNumber(dns2_string, &dns2_number);
184 if (!parsed1 && !parsed2)
185 return CONFIG_PARSE_POSIX_BAD_ADDRESS;
187 if (parsed1) {
188 IPEndPoint dns1(dns1_number, dns_protocol::kDefaultPort);
189 dns_config->nameservers.push_back(dns1);
191 if (parsed2) {
192 IPEndPoint dns2(dns2_number, dns_protocol::kDefaultPort);
193 dns_config->nameservers.push_back(dns2);
196 return CONFIG_PARSE_POSIX_OK;
198 #endif
200 } // namespace
202 class DnsConfigServicePosix::Watcher {
203 public:
204 explicit Watcher(DnsConfigServicePosix* service)
205 : service_(service),
206 weak_factory_(this) {}
207 ~Watcher() {}
209 bool Watch() {
210 bool success = true;
211 if (!config_watcher_.Watch(base::Bind(&Watcher::OnConfigChanged,
212 base::Unretained(this)))) {
213 LOG(ERROR) << "DNS config watch failed to start.";
214 success = false;
215 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
216 DNS_CONFIG_WATCH_FAILED_TO_START_CONFIG,
217 DNS_CONFIG_WATCH_MAX);
219 if (!hosts_watcher_.Watch(base::FilePath(kFilePathHosts), false,
220 base::Bind(&Watcher::OnHostsChanged,
221 base::Unretained(this)))) {
222 LOG(ERROR) << "DNS hosts watch failed to start.";
223 success = false;
224 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
225 DNS_CONFIG_WATCH_FAILED_TO_START_HOSTS,
226 DNS_CONFIG_WATCH_MAX);
228 return success;
231 private:
232 void OnConfigChanged(bool succeeded) {
233 // Ignore transient flutter of resolv.conf by delaying the signal a bit.
234 const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(50);
235 base::MessageLoop::current()->PostDelayedTask(
236 FROM_HERE,
237 base::Bind(&Watcher::OnConfigChangedDelayed,
238 weak_factory_.GetWeakPtr(),
239 succeeded),
240 kDelay);
242 void OnConfigChangedDelayed(bool succeeded) {
243 service_->OnConfigChanged(succeeded);
245 void OnHostsChanged(const base::FilePath& path, bool error) {
246 service_->OnHostsChanged(!error);
249 DnsConfigServicePosix* service_;
250 DnsConfigWatcher config_watcher_;
251 base::FilePathWatcher hosts_watcher_;
253 base::WeakPtrFactory<Watcher> weak_factory_;
255 DISALLOW_COPY_AND_ASSIGN(Watcher);
258 // A SerialWorker that uses libresolv to initialize res_state and converts
259 // it to DnsConfig (except on Android, where it reads system properties
260 // net.dns1 and net.dns2; see #if around ReadDnsConfig above.)
261 class DnsConfigServicePosix::ConfigReader : public SerialWorker {
262 public:
263 explicit ConfigReader(DnsConfigServicePosix* service)
264 : service_(service), success_(false) {}
266 virtual void DoWork() OVERRIDE {
267 base::TimeTicks start_time = base::TimeTicks::Now();
268 ConfigParsePosixResult result = ReadDnsConfig(&dns_config_);
269 switch (result) {
270 case CONFIG_PARSE_POSIX_MISSING_OPTIONS:
271 case CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS:
272 DCHECK(dns_config_.unhandled_options);
273 // Fall through.
274 case CONFIG_PARSE_POSIX_OK:
275 success_ = true;
276 break;
277 default:
278 success_ = false;
279 break;
281 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParsePosix",
282 result, CONFIG_PARSE_POSIX_MAX);
283 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_);
284 UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration",
285 base::TimeTicks::Now() - start_time);
288 virtual void OnWorkFinished() OVERRIDE {
289 DCHECK(!IsCancelled());
290 if (success_) {
291 service_->OnConfigRead(dns_config_);
292 } else {
293 LOG(WARNING) << "Failed to read DnsConfig.";
297 private:
298 virtual ~ConfigReader() {}
300 DnsConfigServicePosix* service_;
301 // Written in DoWork, read in OnWorkFinished, no locking necessary.
302 DnsConfig dns_config_;
303 bool success_;
305 DISALLOW_COPY_AND_ASSIGN(ConfigReader);
308 // A SerialWorker that reads the HOSTS file and runs Callback.
309 class DnsConfigServicePosix::HostsReader : public SerialWorker {
310 public:
311 explicit HostsReader(DnsConfigServicePosix* service)
312 : service_(service), path_(kFilePathHosts), success_(false) {}
314 private:
315 virtual ~HostsReader() {}
317 virtual void DoWork() OVERRIDE {
318 base::TimeTicks start_time = base::TimeTicks::Now();
319 success_ = ParseHostsFile(path_, &hosts_);
320 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostParseResult", success_);
321 UMA_HISTOGRAM_TIMES("AsyncDNS.HostsParseDuration",
322 base::TimeTicks::Now() - start_time);
325 virtual void OnWorkFinished() OVERRIDE {
326 if (success_) {
327 service_->OnHostsRead(hosts_);
328 } else {
329 LOG(WARNING) << "Failed to read DnsHosts.";
333 DnsConfigServicePosix* service_;
334 const base::FilePath path_;
335 // Written in DoWork, read in OnWorkFinished, no locking necessary.
336 DnsHosts hosts_;
337 bool success_;
339 DISALLOW_COPY_AND_ASSIGN(HostsReader);
342 DnsConfigServicePosix::DnsConfigServicePosix()
343 : config_reader_(new ConfigReader(this)),
344 hosts_reader_(new HostsReader(this)) {}
346 DnsConfigServicePosix::~DnsConfigServicePosix() {
347 config_reader_->Cancel();
348 hosts_reader_->Cancel();
351 void DnsConfigServicePosix::ReadNow() {
352 config_reader_->WorkNow();
353 hosts_reader_->WorkNow();
356 bool DnsConfigServicePosix::StartWatching() {
357 // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
358 watcher_.reset(new Watcher(this));
359 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", DNS_CONFIG_WATCH_STARTED,
360 DNS_CONFIG_WATCH_MAX);
361 return watcher_->Watch();
364 void DnsConfigServicePosix::OnConfigChanged(bool succeeded) {
365 InvalidateConfig();
366 if (succeeded) {
367 config_reader_->WorkNow();
368 } else {
369 LOG(ERROR) << "DNS config watch failed.";
370 set_watch_failed(true);
371 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
372 DNS_CONFIG_WATCH_FAILED_CONFIG,
373 DNS_CONFIG_WATCH_MAX);
377 void DnsConfigServicePosix::OnHostsChanged(bool succeeded) {
378 InvalidateHosts();
379 if (succeeded) {
380 hosts_reader_->WorkNow();
381 } else {
382 LOG(ERROR) << "DNS hosts watch failed.";
383 set_watch_failed(true);
384 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
385 DNS_CONFIG_WATCH_FAILED_HOSTS,
386 DNS_CONFIG_WATCH_MAX);
390 #if !defined(OS_ANDROID)
391 ConfigParsePosixResult ConvertResStateToDnsConfig(const struct __res_state& res,
392 DnsConfig* dns_config) {
393 CHECK(dns_config != NULL);
394 if (!(res.options & RES_INIT))
395 return CONFIG_PARSE_POSIX_RES_INIT_UNSET;
397 dns_config->nameservers.clear();
399 #if defined(OS_MACOSX) || defined(OS_FREEBSD)
400 union res_sockaddr_union addresses[MAXNS];
401 int nscount = res_getservers(const_cast<res_state>(&res), addresses, MAXNS);
402 DCHECK_GE(nscount, 0);
403 DCHECK_LE(nscount, MAXNS);
404 for (int i = 0; i < nscount; ++i) {
405 IPEndPoint ipe;
406 if (!ipe.FromSockAddr(
407 reinterpret_cast<const struct sockaddr*>(&addresses[i]),
408 sizeof addresses[i])) {
409 return CONFIG_PARSE_POSIX_BAD_ADDRESS;
411 dns_config->nameservers.push_back(ipe);
413 #elif defined(OS_LINUX)
414 COMPILE_ASSERT(arraysize(res.nsaddr_list) >= MAXNS &&
415 arraysize(res._u._ext.nsaddrs) >= MAXNS,
416 incompatible_libresolv_res_state);
417 DCHECK_LE(res.nscount, MAXNS);
418 // Initially, glibc stores IPv6 in |_ext.nsaddrs| and IPv4 in |nsaddr_list|.
419 // In res_send.c:res_nsend, it merges |nsaddr_list| into |nsaddrs|,
420 // but we have to combine the two arrays ourselves.
421 for (int i = 0; i < res.nscount; ++i) {
422 IPEndPoint ipe;
423 const struct sockaddr* addr = NULL;
424 size_t addr_len = 0;
425 if (res.nsaddr_list[i].sin_family) { // The indicator used by res_nsend.
426 addr = reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]);
427 addr_len = sizeof res.nsaddr_list[i];
428 } else if (res._u._ext.nsaddrs[i] != NULL) {
429 addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]);
430 addr_len = sizeof *res._u._ext.nsaddrs[i];
431 } else {
432 return CONFIG_PARSE_POSIX_BAD_EXT_STRUCT;
434 if (!ipe.FromSockAddr(addr, addr_len))
435 return CONFIG_PARSE_POSIX_BAD_ADDRESS;
436 dns_config->nameservers.push_back(ipe);
438 #else // !(defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FREEBSD))
439 DCHECK_LE(res.nscount, MAXNS);
440 for (int i = 0; i < res.nscount; ++i) {
441 IPEndPoint ipe;
442 if (!ipe.FromSockAddr(
443 reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]),
444 sizeof res.nsaddr_list[i])) {
445 return CONFIG_PARSE_POSIX_BAD_ADDRESS;
447 dns_config->nameservers.push_back(ipe);
449 #endif
451 dns_config->search.clear();
452 for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) {
453 dns_config->search.push_back(std::string(res.dnsrch[i]));
456 dns_config->ndots = res.ndots;
457 dns_config->timeout = base::TimeDelta::FromSeconds(res.retrans);
458 dns_config->attempts = res.retry;
459 #if defined(RES_ROTATE)
460 dns_config->rotate = res.options & RES_ROTATE;
461 #endif
462 dns_config->edns0 = res.options & RES_USE_EDNS0;
464 // The current implementation assumes these options are set. They normally
465 // cannot be overwritten by /etc/resolv.conf
466 unsigned kRequiredOptions = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
467 if ((res.options & kRequiredOptions) != kRequiredOptions) {
468 dns_config->unhandled_options = true;
469 return CONFIG_PARSE_POSIX_MISSING_OPTIONS;
472 unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC;
473 if (res.options & kUnhandledOptions) {
474 dns_config->unhandled_options = true;
475 return CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS;
478 if (dns_config->nameservers.empty())
479 return CONFIG_PARSE_POSIX_NO_NAMESERVERS;
481 // If any name server is 0.0.0.0, assume the configuration is invalid.
482 // TODO(szym): Measure how often this happens. http://crbug.com/125599
483 const IPAddressNumber kEmptyAddress(kIPv4AddressSize);
484 for (unsigned i = 0; i < dns_config->nameservers.size(); ++i) {
485 if (dns_config->nameservers[i].address() == kEmptyAddress)
486 return CONFIG_PARSE_POSIX_NULL_ADDRESS;
488 return CONFIG_PARSE_POSIX_OK;
490 #endif // !defined(OS_ANDROID)
492 } // namespace internal
494 // static
495 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
496 return scoped_ptr<DnsConfigService>(new internal::DnsConfigServicePosix());
499 } // namespace net