Index: net/dns/dns_config_service_posix.cc |
diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc |
deleted file mode 100644 |
index 135f3fd68767a064c58a15652d8e549f7336d5da..0000000000000000000000000000000000000000 |
--- a/net/dns/dns_config_service_posix.cc |
+++ /dev/null |
@@ -1,505 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "net/dns/dns_config_service_posix.h" |
- |
-#include <string> |
- |
-#include "base/basictypes.h" |
-#include "base/bind.h" |
-#include "base/files/file_path.h" |
-#include "base/files/file_path_watcher.h" |
-#include "base/lazy_instance.h" |
-#include "base/memory/scoped_ptr.h" |
-#include "base/metrics/histogram.h" |
-#include "base/time/time.h" |
-#include "net/base/ip_endpoint.h" |
-#include "net/base/net_util.h" |
-#include "net/dns/dns_hosts.h" |
-#include "net/dns/dns_protocol.h" |
-#include "net/dns/notify_watcher_mac.h" |
-#include "net/dns/serial_worker.h" |
- |
-#if defined(OS_MACOSX) && !defined(OS_IOS) |
-#include "net/dns/dns_config_watcher_mac.h" |
-#endif |
- |
-#if defined(OS_ANDROID) |
-#include <sys/system_properties.h> |
-#include "net/base/network_change_notifier.h" |
-#endif |
- |
-namespace net { |
- |
-namespace internal { |
- |
-namespace { |
- |
-#if !defined(OS_ANDROID) |
-const base::FilePath::CharType* kFilePathHosts = |
- FILE_PATH_LITERAL("/etc/hosts"); |
-#else |
-const base::FilePath::CharType* kFilePathHosts = |
- FILE_PATH_LITERAL("/system/etc/hosts"); |
-#endif |
- |
-#if defined(OS_IOS) |
- |
-// There is no public API to watch the DNS configuration on iOS. |
-class DnsConfigWatcher { |
- public: |
- typedef base::Callback<void(bool succeeded)> CallbackType; |
- |
- bool Watch(const CallbackType& callback) { |
- return false; |
- } |
-}; |
- |
-#elif defined(OS_ANDROID) |
-// On Android, assume DNS config may have changed on every network change. |
-class DnsConfigWatcher : public NetworkChangeNotifier::NetworkChangeObserver { |
- public: |
- DnsConfigWatcher() { |
- NetworkChangeNotifier::AddNetworkChangeObserver(this); |
- } |
- |
- ~DnsConfigWatcher() override { |
- NetworkChangeNotifier::RemoveNetworkChangeObserver(this); |
- } |
- |
- bool Watch(const base::Callback<void(bool succeeded)>& callback) { |
- callback_ = callback; |
- return true; |
- } |
- |
- void OnNetworkChanged(NetworkChangeNotifier::ConnectionType type) override { |
- if (!callback_.is_null() && type != NetworkChangeNotifier::CONNECTION_NONE) |
- callback_.Run(true); |
- } |
- |
- private: |
- base::Callback<void(bool succeeded)> callback_; |
-}; |
-#elif !defined(OS_MACOSX) |
-// DnsConfigWatcher for OS_MACOSX is in dns_config_watcher_mac.{hh,cc}. |
- |
-#ifndef _PATH_RESCONF // Normally defined in <resolv.h> |
-#define _PATH_RESCONF "/etc/resolv.conf" |
-#endif |
- |
-static const base::FilePath::CharType* kFilePathConfig = |
- FILE_PATH_LITERAL(_PATH_RESCONF); |
- |
-class DnsConfigWatcher { |
- public: |
- typedef base::Callback<void(bool succeeded)> CallbackType; |
- |
- bool Watch(const CallbackType& callback) { |
- callback_ = callback; |
- return watcher_.Watch(base::FilePath(kFilePathConfig), false, |
- base::Bind(&DnsConfigWatcher::OnCallback, |
- base::Unretained(this))); |
- } |
- |
- private: |
- void OnCallback(const base::FilePath& path, bool error) { |
- callback_.Run(!error); |
- } |
- |
- base::FilePathWatcher watcher_; |
- CallbackType callback_; |
-}; |
-#endif |
- |
-#if !defined(OS_ANDROID) |
-ConfigParsePosixResult ReadDnsConfig(DnsConfig* config) { |
- ConfigParsePosixResult result; |
- config->unhandled_options = false; |
-#if defined(OS_OPENBSD) |
- // Note: res_ninit in glibc always returns 0 and sets RES_INIT. |
- // res_init behaves the same way. |
- memset(&_res, 0, sizeof(_res)); |
- if (res_init() == 0) { |
- result = ConvertResStateToDnsConfig(_res, config); |
- } else { |
- result = CONFIG_PARSE_POSIX_RES_INIT_FAILED; |
- } |
-#else // all other OS_POSIX |
- struct __res_state res; |
- memset(&res, 0, sizeof(res)); |
- if (res_ninit(&res) == 0) { |
- result = ConvertResStateToDnsConfig(res, config); |
- } else { |
- result = CONFIG_PARSE_POSIX_RES_INIT_FAILED; |
- } |
- // Prefer res_ndestroy where available. |
-#if defined(OS_MACOSX) || defined(OS_FREEBSD) |
- res_ndestroy(&res); |
-#else |
- res_nclose(&res); |
-#endif |
-#endif |
- |
-#if defined(OS_MACOSX) && !defined(OS_IOS) |
- ConfigParsePosixResult error = DnsConfigWatcher::CheckDnsConfig(); |
- switch (error) { |
- case CONFIG_PARSE_POSIX_OK: |
- break; |
- case CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS: |
- LOG(WARNING) << "dns_config has unhandled options!"; |
- config->unhandled_options = true; |
- default: |
- return error; |
- } |
-#endif // defined(OS_MACOSX) && !defined(OS_IOS) |
- // Override timeout value to match default setting on Windows. |
- config->timeout = base::TimeDelta::FromSeconds(kDnsTimeoutSeconds); |
- return result; |
-} |
-#else // defined(OS_ANDROID) |
-// Theoretically, this is bad. __system_property_get is not a supported API |
-// (but it's currently visible to anyone using Bionic), and the properties |
-// are implementation details that may disappear in future Android releases. |
-// Practically, libcutils provides property_get, which is a public API, and the |
-// DNS code (and its clients) are already robust against failing to get the DNS |
-// config for whatever reason, so the properties can disappear and the world |
-// won't end. |
-// TODO(ttuttle): Depend on libcutils, then switch this (and other uses of |
-// __system_property_get) to property_get. |
-ConfigParsePosixResult ReadDnsConfig(DnsConfig* dns_config) { |
- std::string dns1_string, dns2_string; |
- char property_value[PROP_VALUE_MAX]; |
- __system_property_get("net.dns1", property_value); |
- dns1_string = property_value; |
- __system_property_get("net.dns2", property_value); |
- dns2_string = property_value; |
- if (dns1_string.length() == 0 && dns2_string.length() == 0) |
- return CONFIG_PARSE_POSIX_NO_NAMESERVERS; |
- |
- IPAddressNumber dns1_number, dns2_number; |
- bool parsed1 = ParseIPLiteralToNumber(dns1_string, &dns1_number); |
- bool parsed2 = ParseIPLiteralToNumber(dns2_string, &dns2_number); |
- if (!parsed1 && !parsed2) |
- return CONFIG_PARSE_POSIX_BAD_ADDRESS; |
- |
- if (parsed1) { |
- IPEndPoint dns1(dns1_number, dns_protocol::kDefaultPort); |
- dns_config->nameservers.push_back(dns1); |
- } |
- if (parsed2) { |
- IPEndPoint dns2(dns2_number, dns_protocol::kDefaultPort); |
- dns_config->nameservers.push_back(dns2); |
- } |
- |
- return CONFIG_PARSE_POSIX_OK; |
-} |
-#endif |
- |
-} // namespace |
- |
-class DnsConfigServicePosix::Watcher { |
- public: |
- explicit Watcher(DnsConfigServicePosix* service) |
- : service_(service), |
- weak_factory_(this) {} |
- ~Watcher() {} |
- |
- bool Watch() { |
- bool success = true; |
- if (!config_watcher_.Watch(base::Bind(&Watcher::OnConfigChanged, |
- base::Unretained(this)))) { |
- LOG(ERROR) << "DNS config watch failed to start."; |
- success = false; |
- UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", |
- DNS_CONFIG_WATCH_FAILED_TO_START_CONFIG, |
- DNS_CONFIG_WATCH_MAX); |
- } |
- if (!hosts_watcher_.Watch(base::FilePath(kFilePathHosts), false, |
- base::Bind(&Watcher::OnHostsChanged, |
- base::Unretained(this)))) { |
- LOG(ERROR) << "DNS hosts watch failed to start."; |
- success = false; |
- UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", |
- DNS_CONFIG_WATCH_FAILED_TO_START_HOSTS, |
- DNS_CONFIG_WATCH_MAX); |
- } |
- return success; |
- } |
- |
- private: |
- void OnConfigChanged(bool succeeded) { |
- // Ignore transient flutter of resolv.conf by delaying the signal a bit. |
- const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(50); |
- base::MessageLoop::current()->PostDelayedTask( |
- FROM_HERE, |
- base::Bind(&Watcher::OnConfigChangedDelayed, |
- weak_factory_.GetWeakPtr(), |
- succeeded), |
- kDelay); |
- } |
- void OnConfigChangedDelayed(bool succeeded) { |
- service_->OnConfigChanged(succeeded); |
- } |
- void OnHostsChanged(const base::FilePath& path, bool error) { |
- service_->OnHostsChanged(!error); |
- } |
- |
- DnsConfigServicePosix* service_; |
- DnsConfigWatcher config_watcher_; |
- base::FilePathWatcher hosts_watcher_; |
- |
- base::WeakPtrFactory<Watcher> weak_factory_; |
- |
- DISALLOW_COPY_AND_ASSIGN(Watcher); |
-}; |
- |
-// A SerialWorker that uses libresolv to initialize res_state and converts |
-// it to DnsConfig (except on Android, where it reads system properties |
-// net.dns1 and net.dns2; see #if around ReadDnsConfig above.) |
-class DnsConfigServicePosix::ConfigReader : public SerialWorker { |
- public: |
- explicit ConfigReader(DnsConfigServicePosix* service) |
- : service_(service), success_(false) {} |
- |
- void DoWork() override { |
- base::TimeTicks start_time = base::TimeTicks::Now(); |
- ConfigParsePosixResult result = ReadDnsConfig(&dns_config_); |
- switch (result) { |
- case CONFIG_PARSE_POSIX_MISSING_OPTIONS: |
- case CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS: |
- DCHECK(dns_config_.unhandled_options); |
- // Fall through. |
- case CONFIG_PARSE_POSIX_OK: |
- success_ = true; |
- break; |
- default: |
- success_ = false; |
- break; |
- } |
- UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParsePosix", |
- result, CONFIG_PARSE_POSIX_MAX); |
- UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_); |
- UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration", |
- base::TimeTicks::Now() - start_time); |
- } |
- |
- void OnWorkFinished() override { |
- DCHECK(!IsCancelled()); |
- if (success_) { |
- service_->OnConfigRead(dns_config_); |
- } else { |
- LOG(WARNING) << "Failed to read DnsConfig."; |
- } |
- } |
- |
- private: |
- ~ConfigReader() override {} |
- |
- DnsConfigServicePosix* service_; |
- // Written in DoWork, read in OnWorkFinished, no locking necessary. |
- DnsConfig dns_config_; |
- bool success_; |
- |
- DISALLOW_COPY_AND_ASSIGN(ConfigReader); |
-}; |
- |
-// A SerialWorker that reads the HOSTS file and runs Callback. |
-class DnsConfigServicePosix::HostsReader : public SerialWorker { |
- public: |
- explicit HostsReader(DnsConfigServicePosix* service) |
- : service_(service), path_(kFilePathHosts), success_(false) {} |
- |
- private: |
- ~HostsReader() override {} |
- |
- void DoWork() override { |
- base::TimeTicks start_time = base::TimeTicks::Now(); |
- success_ = ParseHostsFile(path_, &hosts_); |
- UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostParseResult", success_); |
- UMA_HISTOGRAM_TIMES("AsyncDNS.HostsParseDuration", |
- base::TimeTicks::Now() - start_time); |
- } |
- |
- void OnWorkFinished() override { |
- if (success_) { |
- service_->OnHostsRead(hosts_); |
- } else { |
- LOG(WARNING) << "Failed to read DnsHosts."; |
- } |
- } |
- |
- DnsConfigServicePosix* service_; |
- const base::FilePath path_; |
- // Written in DoWork, read in OnWorkFinished, no locking necessary. |
- DnsHosts hosts_; |
- bool success_; |
- |
- DISALLOW_COPY_AND_ASSIGN(HostsReader); |
-}; |
- |
-DnsConfigServicePosix::DnsConfigServicePosix() |
- : config_reader_(new ConfigReader(this)), |
- hosts_reader_(new HostsReader(this)) {} |
- |
-DnsConfigServicePosix::~DnsConfigServicePosix() { |
- config_reader_->Cancel(); |
- hosts_reader_->Cancel(); |
-} |
- |
-void DnsConfigServicePosix::ReadNow() { |
- config_reader_->WorkNow(); |
- hosts_reader_->WorkNow(); |
-} |
- |
-bool DnsConfigServicePosix::StartWatching() { |
- // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139 |
- watcher_.reset(new Watcher(this)); |
- UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", DNS_CONFIG_WATCH_STARTED, |
- DNS_CONFIG_WATCH_MAX); |
- return watcher_->Watch(); |
-} |
- |
-void DnsConfigServicePosix::OnConfigChanged(bool succeeded) { |
- InvalidateConfig(); |
- if (succeeded) { |
- config_reader_->WorkNow(); |
- } else { |
- LOG(ERROR) << "DNS config watch failed."; |
- set_watch_failed(true); |
- UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", |
- DNS_CONFIG_WATCH_FAILED_CONFIG, |
- DNS_CONFIG_WATCH_MAX); |
- } |
-} |
- |
-void DnsConfigServicePosix::OnHostsChanged(bool succeeded) { |
- InvalidateHosts(); |
- if (succeeded) { |
- hosts_reader_->WorkNow(); |
- } else { |
- LOG(ERROR) << "DNS hosts watch failed."; |
- set_watch_failed(true); |
- UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", |
- DNS_CONFIG_WATCH_FAILED_HOSTS, |
- DNS_CONFIG_WATCH_MAX); |
- } |
-} |
- |
-#if !defined(OS_ANDROID) |
-ConfigParsePosixResult ConvertResStateToDnsConfig(const struct __res_state& res, |
- DnsConfig* dns_config) { |
- CHECK(dns_config != NULL); |
- if (!(res.options & RES_INIT)) |
- return CONFIG_PARSE_POSIX_RES_INIT_UNSET; |
- |
- dns_config->nameservers.clear(); |
- |
-#if defined(OS_MACOSX) || defined(OS_FREEBSD) |
- union res_sockaddr_union addresses[MAXNS]; |
- int nscount = res_getservers(const_cast<res_state>(&res), addresses, MAXNS); |
- DCHECK_GE(nscount, 0); |
- DCHECK_LE(nscount, MAXNS); |
- for (int i = 0; i < nscount; ++i) { |
- IPEndPoint ipe; |
- if (!ipe.FromSockAddr( |
- reinterpret_cast<const struct sockaddr*>(&addresses[i]), |
- sizeof addresses[i])) { |
- return CONFIG_PARSE_POSIX_BAD_ADDRESS; |
- } |
- dns_config->nameservers.push_back(ipe); |
- } |
-#elif defined(OS_LINUX) |
- static_assert(arraysize(res.nsaddr_list) >= MAXNS && |
- arraysize(res._u._ext.nsaddrs) >= MAXNS, |
- "incompatible libresolv res_state"); |
- DCHECK_LE(res.nscount, MAXNS); |
- // Initially, glibc stores IPv6 in |_ext.nsaddrs| and IPv4 in |nsaddr_list|. |
- // In res_send.c:res_nsend, it merges |nsaddr_list| into |nsaddrs|, |
- // but we have to combine the two arrays ourselves. |
- for (int i = 0; i < res.nscount; ++i) { |
- IPEndPoint ipe; |
- const struct sockaddr* addr = NULL; |
- size_t addr_len = 0; |
- if (res.nsaddr_list[i].sin_family) { // The indicator used by res_nsend. |
- addr = reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]); |
- addr_len = sizeof res.nsaddr_list[i]; |
- } else if (res._u._ext.nsaddrs[i] != NULL) { |
- addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]); |
- addr_len = sizeof *res._u._ext.nsaddrs[i]; |
- } else { |
- return CONFIG_PARSE_POSIX_BAD_EXT_STRUCT; |
- } |
- if (!ipe.FromSockAddr(addr, addr_len)) |
- return CONFIG_PARSE_POSIX_BAD_ADDRESS; |
- dns_config->nameservers.push_back(ipe); |
- } |
-#else // !(defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FREEBSD)) |
- DCHECK_LE(res.nscount, MAXNS); |
- for (int i = 0; i < res.nscount; ++i) { |
- IPEndPoint ipe; |
- if (!ipe.FromSockAddr( |
- reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]), |
- sizeof res.nsaddr_list[i])) { |
- return CONFIG_PARSE_POSIX_BAD_ADDRESS; |
- } |
- dns_config->nameservers.push_back(ipe); |
- } |
-#endif |
- |
- dns_config->search.clear(); |
- for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) { |
- dns_config->search.push_back(std::string(res.dnsrch[i])); |
- } |
- |
- dns_config->ndots = res.ndots; |
- dns_config->timeout = base::TimeDelta::FromSeconds(res.retrans); |
- dns_config->attempts = res.retry; |
-#if defined(RES_ROTATE) |
- dns_config->rotate = res.options & RES_ROTATE; |
-#endif |
-#if defined(RES_USE_EDNS0) |
- dns_config->edns0 = res.options & RES_USE_EDNS0; |
-#endif |
-#if !defined(RES_USE_DNSSEC) |
- // Some versions of libresolv don't have support for the DO bit. In this |
- // case, we proceed without it. |
- static const int RES_USE_DNSSEC = 0; |
-#endif |
- |
- // The current implementation assumes these options are set. They normally |
- // cannot be overwritten by /etc/resolv.conf |
- unsigned kRequiredOptions = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; |
- if ((res.options & kRequiredOptions) != kRequiredOptions) { |
- dns_config->unhandled_options = true; |
- return CONFIG_PARSE_POSIX_MISSING_OPTIONS; |
- } |
- |
- unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC; |
- if (res.options & kUnhandledOptions) { |
- dns_config->unhandled_options = true; |
- return CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS; |
- } |
- |
- if (dns_config->nameservers.empty()) |
- return CONFIG_PARSE_POSIX_NO_NAMESERVERS; |
- |
- // If any name server is 0.0.0.0, assume the configuration is invalid. |
- // TODO(szym): Measure how often this happens. http://crbug.com/125599 |
- const IPAddressNumber kEmptyAddress(kIPv4AddressSize); |
- for (unsigned i = 0; i < dns_config->nameservers.size(); ++i) { |
- if (dns_config->nameservers[i].address() == kEmptyAddress) |
- return CONFIG_PARSE_POSIX_NULL_ADDRESS; |
- } |
- return CONFIG_PARSE_POSIX_OK; |
-} |
-#endif // !defined(OS_ANDROID) |
- |
-} // namespace internal |
- |
-// static |
-scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { |
- return scoped_ptr<DnsConfigService>(new internal::DnsConfigServicePosix()); |
-} |
- |
-} // namespace net |