Roll src/third_party/skia 465283c:212aab0
[chromium-blink-merge.git] / net / dns / dns_config_service_posix.cc
blob4300dfc420a29afaa43ed5d9c8b591e8545ed938
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.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_path_watcher.h"
14 #include "base/lazy_instance.h"
15 #include "base/location.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/time/time.h"
21 #include "net/base/ip_endpoint.h"
22 #include "net/base/net_util.h"
23 #include "net/dns/dns_hosts.h"
24 #include "net/dns/dns_protocol.h"
25 #include "net/dns/notify_watcher_mac.h"
26 #include "net/dns/serial_worker.h"
28 #if defined(OS_MACOSX) && !defined(OS_IOS)
29 #include "net/dns/dns_config_watcher_mac.h"
30 #endif
32 #if defined(OS_ANDROID)
33 #include <sys/system_properties.h>
34 #include "net/base/network_change_notifier.h"
35 #endif
37 namespace net {
39 namespace internal {
41 namespace {
43 #if !defined(OS_ANDROID)
44 const base::FilePath::CharType* kFilePathHosts =
45 FILE_PATH_LITERAL("/etc/hosts");
46 #else
47 const base::FilePath::CharType* kFilePathHosts =
48 FILE_PATH_LITERAL("/system/etc/hosts");
49 #endif
51 #if defined(OS_IOS)
53 // There is no public API to watch the DNS configuration on iOS.
54 class DnsConfigWatcher {
55 public:
56 typedef base::Callback<void(bool succeeded)> CallbackType;
58 bool Watch(const CallbackType& callback) {
59 return false;
63 #elif defined(OS_ANDROID)
64 // On Android, assume DNS config may have changed on every network change.
65 class DnsConfigWatcher {
66 public:
67 bool Watch(const base::Callback<void(bool succeeded)>& callback) {
68 callback_ = callback;
69 return true;
72 void OnNetworkChanged(NetworkChangeNotifier::ConnectionType type) {
73 if (!callback_.is_null() && type != NetworkChangeNotifier::CONNECTION_NONE)
74 callback_.Run(true);
77 private:
78 base::Callback<void(bool succeeded)> callback_;
80 #elif !defined(OS_MACOSX)
81 // DnsConfigWatcher for OS_MACOSX is in dns_config_watcher_mac.{hh,cc}.
83 #ifndef _PATH_RESCONF // Normally defined in <resolv.h>
84 #define _PATH_RESCONF "/etc/resolv.conf"
85 #endif
87 static const base::FilePath::CharType* kFilePathConfig =
88 FILE_PATH_LITERAL(_PATH_RESCONF);
90 class DnsConfigWatcher {
91 public:
92 typedef base::Callback<void(bool succeeded)> CallbackType;
94 bool Watch(const CallbackType& callback) {
95 callback_ = callback;
96 return watcher_.Watch(base::FilePath(kFilePathConfig), false,
97 base::Bind(&DnsConfigWatcher::OnCallback,
98 base::Unretained(this)));
101 private:
102 void OnCallback(const base::FilePath& path, bool error) {
103 callback_.Run(!error);
106 base::FilePathWatcher watcher_;
107 CallbackType callback_;
109 #endif
111 #if !defined(OS_ANDROID)
112 ConfigParsePosixResult ReadDnsConfig(DnsConfig* config) {
113 ConfigParsePosixResult result;
114 config->unhandled_options = false;
115 #if defined(OS_OPENBSD)
116 // Note: res_ninit in glibc always returns 0 and sets RES_INIT.
117 // res_init behaves the same way.
118 memset(&_res, 0, sizeof(_res));
119 if (res_init() == 0) {
120 result = ConvertResStateToDnsConfig(_res, config);
121 } else {
122 result = CONFIG_PARSE_POSIX_RES_INIT_FAILED;
124 #else // all other OS_POSIX
125 struct __res_state res;
126 memset(&res, 0, sizeof(res));
127 if (res_ninit(&res) == 0) {
128 result = ConvertResStateToDnsConfig(res, config);
129 } else {
130 result = CONFIG_PARSE_POSIX_RES_INIT_FAILED;
132 // Prefer res_ndestroy where available.
133 #if defined(OS_MACOSX) || defined(OS_FREEBSD)
134 res_ndestroy(&res);
135 #else
136 res_nclose(&res);
137 #endif
138 #endif
140 #if defined(OS_MACOSX) && !defined(OS_IOS)
141 ConfigParsePosixResult error = DnsConfigWatcher::CheckDnsConfig();
142 switch (error) {
143 case CONFIG_PARSE_POSIX_OK:
144 break;
145 case CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS:
146 LOG(WARNING) << "dns_config has unhandled options!";
147 config->unhandled_options = true;
148 default:
149 return error;
151 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
152 // Override timeout value to match default setting on Windows.
153 config->timeout = base::TimeDelta::FromSeconds(kDnsTimeoutSeconds);
154 return result;
156 #else // defined(OS_ANDROID)
157 // Theoretically, this is bad. __system_property_get is not a supported API
158 // (but it's currently visible to anyone using Bionic), and the properties
159 // are implementation details that may disappear in future Android releases.
160 // Practically, libcutils provides property_get, which is a public API, and the
161 // DNS code (and its clients) are already robust against failing to get the DNS
162 // config for whatever reason, so the properties can disappear and the world
163 // won't end.
164 // TODO(ttuttle): Depend on libcutils, then switch this (and other uses of
165 // __system_property_get) to property_get.
166 ConfigParsePosixResult ReadDnsConfig(DnsConfig* dns_config) {
167 std::string dns1_string, dns2_string;
168 char property_value[PROP_VALUE_MAX];
169 __system_property_get("net.dns1", property_value);
170 dns1_string = property_value;
171 __system_property_get("net.dns2", property_value);
172 dns2_string = property_value;
173 if (dns1_string.length() == 0 && dns2_string.length() == 0)
174 return CONFIG_PARSE_POSIX_NO_NAMESERVERS;
176 IPAddressNumber dns1_number, dns2_number;
177 bool parsed1 = ParseIPLiteralToNumber(dns1_string, &dns1_number);
178 bool parsed2 = ParseIPLiteralToNumber(dns2_string, &dns2_number);
179 if (!parsed1 && !parsed2)
180 return CONFIG_PARSE_POSIX_BAD_ADDRESS;
182 if (parsed1) {
183 IPEndPoint dns1(dns1_number, dns_protocol::kDefaultPort);
184 dns_config->nameservers.push_back(dns1);
186 if (parsed2) {
187 IPEndPoint dns2(dns2_number, dns_protocol::kDefaultPort);
188 dns_config->nameservers.push_back(dns2);
191 return CONFIG_PARSE_POSIX_OK;
193 #endif
195 } // namespace
197 class DnsConfigServicePosix::Watcher {
198 public:
199 explicit Watcher(DnsConfigServicePosix* service)
200 : service_(service), weak_factory_(this) {}
201 ~Watcher() {}
203 bool Watch() {
204 bool success = true;
205 if (!config_watcher_.Watch(base::Bind(&Watcher::OnConfigChanged,
206 base::Unretained(this)))) {
207 LOG(ERROR) << "DNS config watch failed to start.";
208 success = false;
209 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
210 DNS_CONFIG_WATCH_FAILED_TO_START_CONFIG,
211 DNS_CONFIG_WATCH_MAX);
213 #if !defined(OS_IOS)
214 if (!hosts_watcher_.Watch(
215 base::FilePath(service_->file_path_hosts_), false,
216 base::Bind(&Watcher::OnHostsChanged, base::Unretained(this)))) {
217 LOG(ERROR) << "DNS hosts watch failed to start.";
218 success = false;
219 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
220 DNS_CONFIG_WATCH_FAILED_TO_START_HOSTS,
221 DNS_CONFIG_WATCH_MAX);
223 #endif
224 return success;
227 #if defined(OS_ANDROID)
228 void OnNetworkChanged(NetworkChangeNotifier::ConnectionType type) {
229 config_watcher_.OnNetworkChanged(type);
231 #endif // defined(OS_ANDROID)
233 private:
234 void OnConfigChanged(bool succeeded) {
235 #if defined(OS_ANDROID)
236 service_->seen_config_change_ = true;
237 #endif // defined(OS_ANDROID)
238 // Ignore transient flutter of resolv.conf by delaying the signal a bit.
239 const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(50);
240 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
241 FROM_HERE, base::Bind(&Watcher::OnConfigChangedDelayed,
242 weak_factory_.GetWeakPtr(), succeeded),
243 kDelay);
245 void OnConfigChangedDelayed(bool succeeded) {
246 service_->OnConfigChanged(succeeded);
248 void OnHostsChanged(const base::FilePath& path, bool error) {
249 service_->OnHostsChanged(!error);
252 DnsConfigServicePosix* service_;
253 DnsConfigWatcher config_watcher_;
254 #if !defined(OS_IOS)
255 base::FilePathWatcher hosts_watcher_;
256 #endif
258 base::WeakPtrFactory<Watcher> weak_factory_;
260 DISALLOW_COPY_AND_ASSIGN(Watcher);
263 // A SerialWorker that uses libresolv to initialize res_state and converts
264 // it to DnsConfig (except on Android, where it reads system properties
265 // net.dns1 and net.dns2; see #if around ReadDnsConfig above.)
266 class DnsConfigServicePosix::ConfigReader : public SerialWorker {
267 public:
268 explicit ConfigReader(DnsConfigServicePosix* service)
269 : service_(service), success_(false) {}
271 void DoWork() override {
272 base::TimeTicks start_time = base::TimeTicks::Now();
273 ConfigParsePosixResult result = ReadDnsConfig(&dns_config_);
274 if (service_->dns_config_for_testing_) {
275 dns_config_ = *service_->dns_config_for_testing_;
276 result = CONFIG_PARSE_POSIX_OK;
278 switch (result) {
279 case CONFIG_PARSE_POSIX_MISSING_OPTIONS:
280 case CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS:
281 DCHECK(dns_config_.unhandled_options);
282 // Fall through.
283 case CONFIG_PARSE_POSIX_OK:
284 success_ = true;
285 break;
286 default:
287 success_ = false;
288 break;
290 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParsePosix",
291 result, CONFIG_PARSE_POSIX_MAX);
292 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_);
293 UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration",
294 base::TimeTicks::Now() - start_time);
297 void OnWorkFinished() override {
298 DCHECK(!IsCancelled());
299 if (success_) {
300 service_->OnConfigRead(dns_config_);
301 } else {
302 LOG(WARNING) << "Failed to read DnsConfig.";
306 private:
307 ~ConfigReader() override {}
309 DnsConfigServicePosix* service_;
310 // Written in DoWork, read in OnWorkFinished, no locking necessary.
311 DnsConfig dns_config_;
312 bool success_;
314 DISALLOW_COPY_AND_ASSIGN(ConfigReader);
317 // A SerialWorker that reads the HOSTS file and runs Callback.
318 class DnsConfigServicePosix::HostsReader : public SerialWorker {
319 public:
320 explicit HostsReader(DnsConfigServicePosix* service)
321 : service_(service), success_(false) {}
323 private:
324 ~HostsReader() override {}
326 void DoWork() override {
327 base::TimeTicks start_time = base::TimeTicks::Now();
328 success_ =
329 ParseHostsFile(base::FilePath(service_->file_path_hosts_), &hosts_);
330 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostParseResult", success_);
331 UMA_HISTOGRAM_TIMES("AsyncDNS.HostsParseDuration",
332 base::TimeTicks::Now() - start_time);
335 void OnWorkFinished() override {
336 if (success_) {
337 service_->OnHostsRead(hosts_);
338 } else {
339 LOG(WARNING) << "Failed to read DnsHosts.";
343 DnsConfigServicePosix* service_;
344 // Written in DoWork, read in OnWorkFinished, no locking necessary.
345 DnsHosts hosts_;
346 bool success_;
348 DISALLOW_COPY_AND_ASSIGN(HostsReader);
351 DnsConfigServicePosix::DnsConfigServicePosix()
352 : file_path_hosts_(kFilePathHosts),
353 dns_config_for_testing_(nullptr),
354 config_reader_(new ConfigReader(this)),
355 hosts_reader_(new HostsReader(this))
356 #if defined(OS_ANDROID)
358 seen_config_change_(false)
359 #endif // defined(OS_ANDROID)
363 DnsConfigServicePosix::~DnsConfigServicePosix() {
364 config_reader_->Cancel();
365 hosts_reader_->Cancel();
368 void DnsConfigServicePosix::ReadNow() {
369 config_reader_->WorkNow();
370 hosts_reader_->WorkNow();
373 bool DnsConfigServicePosix::StartWatching() {
374 // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
375 watcher_.reset(new Watcher(this));
376 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", DNS_CONFIG_WATCH_STARTED,
377 DNS_CONFIG_WATCH_MAX);
378 return watcher_->Watch();
381 void DnsConfigServicePosix::OnConfigChanged(bool succeeded) {
382 InvalidateConfig();
383 if (succeeded) {
384 config_reader_->WorkNow();
385 } else {
386 LOG(ERROR) << "DNS config watch failed.";
387 set_watch_failed(true);
388 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
389 DNS_CONFIG_WATCH_FAILED_CONFIG,
390 DNS_CONFIG_WATCH_MAX);
394 void DnsConfigServicePosix::OnHostsChanged(bool succeeded) {
395 InvalidateHosts();
396 if (succeeded) {
397 hosts_reader_->WorkNow();
398 } else {
399 LOG(ERROR) << "DNS hosts watch failed.";
400 set_watch_failed(true);
401 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
402 DNS_CONFIG_WATCH_FAILED_HOSTS,
403 DNS_CONFIG_WATCH_MAX);
407 void DnsConfigServicePosix::SetDnsConfigForTesting(
408 const DnsConfig* dns_config) {
409 DCHECK(CalledOnValidThread());
410 dns_config_for_testing_ = dns_config;
413 #if !defined(OS_ANDROID)
414 ConfigParsePosixResult ConvertResStateToDnsConfig(const struct __res_state& res,
415 DnsConfig* dns_config) {
416 CHECK(dns_config != NULL);
417 if (!(res.options & RES_INIT))
418 return CONFIG_PARSE_POSIX_RES_INIT_UNSET;
420 dns_config->nameservers.clear();
422 #if defined(OS_MACOSX) || defined(OS_FREEBSD)
423 union res_sockaddr_union addresses[MAXNS];
424 int nscount = res_getservers(const_cast<res_state>(&res), addresses, MAXNS);
425 DCHECK_GE(nscount, 0);
426 DCHECK_LE(nscount, MAXNS);
427 for (int i = 0; i < nscount; ++i) {
428 IPEndPoint ipe;
429 if (!ipe.FromSockAddr(
430 reinterpret_cast<const struct sockaddr*>(&addresses[i]),
431 sizeof addresses[i])) {
432 return CONFIG_PARSE_POSIX_BAD_ADDRESS;
434 dns_config->nameservers.push_back(ipe);
436 #elif defined(OS_LINUX)
437 static_assert(arraysize(res.nsaddr_list) >= MAXNS &&
438 arraysize(res._u._ext.nsaddrs) >= MAXNS,
439 "incompatible libresolv res_state");
440 DCHECK_LE(res.nscount, MAXNS);
441 // Initially, glibc stores IPv6 in |_ext.nsaddrs| and IPv4 in |nsaddr_list|.
442 // In res_send.c:res_nsend, it merges |nsaddr_list| into |nsaddrs|,
443 // but we have to combine the two arrays ourselves.
444 for (int i = 0; i < res.nscount; ++i) {
445 IPEndPoint ipe;
446 const struct sockaddr* addr = NULL;
447 size_t addr_len = 0;
448 if (res.nsaddr_list[i].sin_family) { // The indicator used by res_nsend.
449 addr = reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]);
450 addr_len = sizeof res.nsaddr_list[i];
451 } else if (res._u._ext.nsaddrs[i] != NULL) {
452 addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]);
453 addr_len = sizeof *res._u._ext.nsaddrs[i];
454 } else {
455 return CONFIG_PARSE_POSIX_BAD_EXT_STRUCT;
457 if (!ipe.FromSockAddr(addr, addr_len))
458 return CONFIG_PARSE_POSIX_BAD_ADDRESS;
459 dns_config->nameservers.push_back(ipe);
461 #else // !(defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FREEBSD))
462 DCHECK_LE(res.nscount, MAXNS);
463 for (int i = 0; i < res.nscount; ++i) {
464 IPEndPoint ipe;
465 if (!ipe.FromSockAddr(
466 reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]),
467 sizeof res.nsaddr_list[i])) {
468 return CONFIG_PARSE_POSIX_BAD_ADDRESS;
470 dns_config->nameservers.push_back(ipe);
472 #endif
474 dns_config->search.clear();
475 for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) {
476 dns_config->search.push_back(std::string(res.dnsrch[i]));
479 dns_config->ndots = res.ndots;
480 dns_config->timeout = base::TimeDelta::FromSeconds(res.retrans);
481 dns_config->attempts = res.retry;
482 #if defined(RES_ROTATE)
483 dns_config->rotate = res.options & RES_ROTATE;
484 #endif
485 #if defined(RES_USE_EDNS0)
486 dns_config->edns0 = res.options & RES_USE_EDNS0;
487 #endif
488 #if !defined(RES_USE_DNSSEC)
489 // Some versions of libresolv don't have support for the DO bit. In this
490 // case, we proceed without it.
491 static const int RES_USE_DNSSEC = 0;
492 #endif
494 // The current implementation assumes these options are set. They normally
495 // cannot be overwritten by /etc/resolv.conf
496 unsigned kRequiredOptions = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
497 if ((res.options & kRequiredOptions) != kRequiredOptions) {
498 dns_config->unhandled_options = true;
499 return CONFIG_PARSE_POSIX_MISSING_OPTIONS;
502 unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC;
503 if (res.options & kUnhandledOptions) {
504 dns_config->unhandled_options = true;
505 return CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS;
508 if (dns_config->nameservers.empty())
509 return CONFIG_PARSE_POSIX_NO_NAMESERVERS;
511 // If any name server is 0.0.0.0, assume the configuration is invalid.
512 // TODO(szym): Measure how often this happens. http://crbug.com/125599
513 const IPAddressNumber kEmptyAddress(kIPv4AddressSize);
514 for (unsigned i = 0; i < dns_config->nameservers.size(); ++i) {
515 if (dns_config->nameservers[i].address() == kEmptyAddress)
516 return CONFIG_PARSE_POSIX_NULL_ADDRESS;
518 return CONFIG_PARSE_POSIX_OK;
521 #else // defined(OS_ANDROID)
523 bool DnsConfigServicePosix::SeenChangeSince(
524 const base::Time& since_time) const {
525 DCHECK(CalledOnValidThread());
526 if (seen_config_change_)
527 return true;
528 base::File hosts(base::FilePath(file_path_hosts_),
529 base::File::FLAG_OPEN | base::File::FLAG_READ);
530 base::File::Info hosts_info;
531 // File last modified times are not nearly as accurate as Time::Now() and are
532 // rounded down. This means a file modified at 1:23.456 might only
533 // be given a last modified time of 1:23.450. If we compared the last
534 // modified time directly to |since_time| we might miss changes to the hosts
535 // file because of this rounding down. To account for this the |since_time|
536 // is pushed back by 1s which should more than account for any rounding.
537 // In practice file modified times on Android are two orders of magnitude
538 // more accurate than this 1s. In practice the hosts file on Android always
539 // contains "127.0.0.1 localhost" and is never modified after Android is
540 // installed.
541 return !hosts.GetInfo(&hosts_info) ||
542 hosts_info.last_modified >=
543 (since_time - base::TimeDelta::FromSeconds(1));
546 void DnsConfigServicePosix::OnNetworkChanged(
547 NetworkChangeNotifier::ConnectionType type) {
548 DCHECK(CalledOnValidThread());
549 DCHECK(watcher_);
550 watcher_->OnNetworkChanged(type);
552 #endif // defined(OS_ANDROID)
554 } // namespace internal
556 // static
557 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
558 return scoped_ptr<DnsConfigService>(new internal::DnsConfigServicePosix());
561 } // namespace net