summaryrefslogtreecommitdiffstats
path: root/net/dns
diff options
context:
space:
mode:
authorszym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-19 21:21:29 +0000
committerszym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-19 21:21:29 +0000
commit539df6c7207426d0626ee1bc76cf6f9a2f3766c0 (patch)
treedca556cc0d34579feee1062540f1f5a9e4631e83 /net/dns
parente1c2607d73e9ce24e3fec9193d24739599e64241 (diff)
downloadchromium_src-539df6c7207426d0626ee1bc76cf6f9a2f3766c0.zip
chromium_src-539df6c7207426d0626ee1bc76cf6f9a2f3766c0.tar.gz
chromium_src-539df6c7207426d0626ee1bc76cf6f9a2f3766c0.tar.bz2
[net/dns] Instrument DnsConfigService to measure performance and failures.
BUG=125599 TEST=chrome://histograms Review URL: https://chromiumcodereview.appspot.com/10543168 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@143051 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/dns')
-rw-r--r--net/dns/dns_config_service.cc27
-rw-r--r--net/dns/dns_config_service.h9
-rw-r--r--net/dns/dns_config_service_posix.cc121
-rw-r--r--net/dns/dns_config_service_posix.h15
-rw-r--r--net/dns/dns_config_service_posix_unittest.cc14
-rw-r--r--net/dns/dns_config_service_unittest.cc2
-rw-r--r--net/dns/dns_config_service_win.cc324
-rw-r--r--net/dns/dns_config_service_win.h18
-rw-r--r--net/dns/dns_config_service_win_unittest.cc18
9 files changed, 346 insertions, 202 deletions
diff --git a/net/dns/dns_config_service.cc b/net/dns/dns_config_service.cc
index 43acf48..a520d26 100644
--- a/net/dns/dns_config_service.cc
+++ b/net/dns/dns_config_service.cc
@@ -152,6 +152,12 @@ void DnsConfigService::Watch(const CallbackType& callback) {
void DnsConfigService::InvalidateConfig() {
DCHECK(CalledOnValidThread());
+ base::TimeTicks now = base::TimeTicks::Now();
+ if (!last_invalidate_config_time_.is_null()) {
+ UMA_HISTOGRAM_LONG_TIMES("AsyncDNS.ConfigNotifyInterval",
+ now - last_invalidate_config_time_);
+ }
+ last_invalidate_config_time_ = now;
if (!have_config_)
return;
have_config_ = false;
@@ -160,6 +166,12 @@ void DnsConfigService::InvalidateConfig() {
void DnsConfigService::InvalidateHosts() {
DCHECK(CalledOnValidThread());
+ base::TimeTicks now = base::TimeTicks::Now();
+ if (!last_invalidate_hosts_time_.is_null()) {
+ UMA_HISTOGRAM_LONG_TIMES("AsyncDNS.HostsNotifyInterval",
+ now - last_invalidate_hosts_time_);
+ }
+ last_invalidate_hosts_time_ = now;
if (!have_hosts_)
return;
have_hosts_ = false;
@@ -170,10 +182,17 @@ void DnsConfigService::OnConfigRead(const DnsConfig& config) {
DCHECK(CalledOnValidThread());
DCHECK(config.IsValid());
+ bool changed = false;
if (!config.EqualsIgnoreHosts(dns_config_)) {
dns_config_.CopyIgnoreHosts(config);
need_update_ = true;
+ changed = true;
}
+ if (!changed && !last_sent_empty_time_.is_null()) {
+ UMA_HISTOGRAM_LONG_TIMES("AsyncDNS.UnchangedConfigInterval",
+ base::TimeTicks::Now() - last_sent_empty_time_);
+ }
+ UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigChange", changed);
have_config_ = true;
if (have_hosts_)
@@ -183,10 +202,17 @@ void DnsConfigService::OnConfigRead(const DnsConfig& config) {
void DnsConfigService::OnHostsRead(const DnsHosts& hosts) {
DCHECK(CalledOnValidThread());
+ bool changed = false;
if (hosts != dns_config_.hosts) {
dns_config_.hosts = hosts;
need_update_ = true;
+ changed = true;
+ }
+ if (!changed && !last_sent_empty_time_.is_null()) {
+ UMA_HISTOGRAM_LONG_TIMES("AsyncDNS.UnchangedHostsInterval",
+ base::TimeTicks::Now() - last_sent_empty_time_);
}
+ UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostsChange", changed);
have_hosts_ = true;
if (have_config_)
@@ -226,6 +252,7 @@ void DnsConfigService::OnTimeout() {
need_update_ = true;
// Empty config is considered invalid.
last_sent_empty_ = true;
+ last_sent_empty_time_ = base::TimeTicks::Now();
callback_.Run(DnsConfig());
}
diff --git a/net/dns/dns_config_service.h b/net/dns/dns_config_service.h
index da739b1..7d11832 100644
--- a/net/dns/dns_config_service.h
+++ b/net/dns/dns_config_service.h
@@ -103,8 +103,6 @@ class NET_EXPORT_PRIVATE DnsConfigService
virtual void Watch(const CallbackType& callback);
protected:
- friend class DnsHostsReader;
-
// Called when the current config (except hosts) has changed.
void InvalidateConfig();
// Called when the current hosts have changed.
@@ -139,8 +137,15 @@ class NET_EXPORT_PRIVATE DnsConfigService
// True if receiver needs to be updated when the config becomes complete.
bool need_update_;
// True if the last config sent was empty (instead of |dns_config_|).
+ // Set when |timer_| expires.
bool last_sent_empty_;
+ // Initialized and updated on Invalidate* call.
+ base::TimeTicks last_invalidate_config_time_;
+ base::TimeTicks last_invalidate_hosts_time_;
+ // Initialized and updated when |timer_| expires.
+ base::TimeTicks last_sent_empty_time_;
+
// Started in Invalidate*, cleared in On*Read.
base::OneShotTimer<DnsConfigService> timer_;
diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc
index 5daaf69..ea51915 100644
--- a/net/dns/dns_config_service_posix.cc
+++ b/net/dns/dns_config_service_posix.cc
@@ -11,6 +11,8 @@
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/time.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_util.h"
#include "net/dns/dns_hosts.h"
@@ -19,6 +21,9 @@
namespace net {
+#if !defined(OS_ANDROID)
+namespace internal {
+
namespace {
#ifndef _PATH_RESCONF // Normally defined in <resolv.h>
@@ -27,6 +32,35 @@ namespace {
const FilePath::CharType* kFilePathHosts = FILE_PATH_LITERAL("/etc/hosts");
+ConfigParsePosixResult ReadDnsConfig(DnsConfig* config) {
+ ConfigParsePosixResult result;
+#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
+ return result;
+}
+
// A SerialWorker that uses libresolv to initialize res_state and converts
// it to DnsConfig.
class ConfigReader : public SerialWorker {
@@ -36,28 +70,14 @@ class ConfigReader : public SerialWorker {
: callback_(callback), success_(false) {}
void DoWork() OVERRIDE {
- success_ = false;
-#if defined(OS_ANDROID)
- NOTIMPLEMENTED();
-#elif 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) && (_res.options & RES_INIT))
- success_ = internal::ConvertResStateToDnsConfig(_res, &dns_config_);
-#else // all other OS_POSIX
- struct __res_state res;
- memset(&res, 0, sizeof(res));
- if ((res_ninit(&res) == 0) && (res.options & RES_INIT))
- success_ = internal::ConvertResStateToDnsConfig(res, &dns_config_);
-
- // Prefer res_ndestroy where available.
-#if defined(OS_MACOSX) || defined(OS_FREEBSD)
- res_ndestroy(&res);
-#else
- res_nclose(&res);
-#endif
-#endif
+ base::TimeTicks start_time = base::TimeTicks::Now();
+ ConfigParsePosixResult result = ReadDnsConfig(&dns_config_);
+ success_ = (result == CONFIG_PARSE_POSIX_OK);
+ 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 {
@@ -91,7 +111,11 @@ class HostsReader : public SerialWorker {
virtual ~HostsReader() {}
virtual 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);
}
virtual void OnWorkFinished() OVERRIDE {
@@ -113,8 +137,6 @@ class HostsReader : public SerialWorker {
} // namespace
-namespace internal {
-
DnsConfigServicePosix::DnsConfigServicePosix() {
config_reader_ = new ConfigReader(
base::Bind(&DnsConfigServicePosix::OnConfigRead,
@@ -150,11 +172,11 @@ void DnsConfigServicePosix::OnDNSChanged(unsigned detail) {
}
}
-#if !defined(OS_ANDROID)
-bool ConvertResStateToDnsConfig(const struct __res_state& res,
- DnsConfig* dns_config) {
+ConfigParsePosixResult ConvertResStateToDnsConfig(const struct __res_state& res,
+ DnsConfig* dns_config) {
CHECK(dns_config != NULL);
- DCHECK(res.options & RES_INIT);
+ if (!(res.options & RES_INIT))
+ return CONFIG_PARSE_POSIX_RES_INIT_UNSET;
dns_config->nameservers.clear();
@@ -168,7 +190,7 @@ bool ConvertResStateToDnsConfig(const struct __res_state& res,
if (!ipe.FromSockAddr(
reinterpret_cast<const struct sockaddr*>(&addresses[i]),
sizeof addresses[i])) {
- return false;
+ return CONFIG_PARSE_POSIX_BAD_ADDRESS;
}
dns_config->nameservers.push_back(ipe);
}
@@ -191,10 +213,10 @@ bool ConvertResStateToDnsConfig(const struct __res_state& res,
addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]);
addr_len = sizeof *res._u._ext.nsaddrs[i];
} else {
- return false;
+ return CONFIG_PARSE_POSIX_BAD_EXT_STRUCT;
}
if (!ipe.FromSockAddr(addr, addr_len))
- return false;
+ return CONFIG_PARSE_POSIX_BAD_ADDRESS;
dns_config->nameservers.push_back(ipe);
}
#else // !(defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FREEBSD))
@@ -204,7 +226,7 @@ bool ConvertResStateToDnsConfig(const struct __res_state& res,
if (!ipe.FromSockAddr(
reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]),
sizeof res.nsaddr_list[i])) {
- return false;
+ return CONFIG_PARSE_POSIX_BAD_ADDRESS;
}
dns_config->nameservers.push_back(ipe);
}
@@ -223,15 +245,28 @@ bool ConvertResStateToDnsConfig(const struct __res_state& res,
#endif
dns_config->edns0 = res.options & RES_USE_EDNS0;
+ // 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)
+ return CONFIG_PARSE_POSIX_MISSING_OPTIONS;
+
+ unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC;
+ if (res.options & kUnhandledOptions)
+ 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)
+ for (unsigned i = 0; i < dns_config->nameservers.size(); ++i) {
if (dns_config->nameservers[i].address() == kEmptyAddress)
- return false;
- return true;
+ return CONFIG_PARSE_POSIX_NULL_ADDRESS;
+ }
+ return CONFIG_PARSE_POSIX_OK;
}
-#endif // !defined(OS_ANDROID)
} // namespace internal
@@ -240,4 +275,18 @@ scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
return scoped_ptr<DnsConfigService>(new internal::DnsConfigServicePosix());
}
+#else // defined(OS_ANDROID)
+// Android NDK provides only a stub <resolv.h> header.
+class StubDnsConfigService : public DnsConfigService {
+ public:
+ StubDnsConfigService() {}
+ virtual ~StubDnsConfigService() {}
+ virtual void OnDNSChanged(unsigned detail) OVERRIDE {}
+};
+// static
+scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
+ return scoped_ptr<DnsConfigService>(new StubDnsConfigService());
+}
+#endif
+
} // namespace net
diff --git a/net/dns/dns_config_service_posix.h b/net/dns/dns_config_service_posix.h
index d66002a..6c1bc08 100644
--- a/net/dns/dns_config_service_posix.h
+++ b/net/dns/dns_config_service_posix.h
@@ -36,8 +36,21 @@ class NET_EXPORT_PRIVATE DnsConfigServicePosix : public DnsConfigService {
DISALLOW_COPY_AND_ASSIGN(DnsConfigServicePosix);
};
+enum ConfigParsePosixResult {
+ CONFIG_PARSE_POSIX_OK = 0,
+ CONFIG_PARSE_POSIX_RES_INIT_FAILED,
+ CONFIG_PARSE_POSIX_RES_INIT_UNSET,
+ CONFIG_PARSE_POSIX_BAD_ADDRESS,
+ CONFIG_PARSE_POSIX_BAD_EXT_STRUCT,
+ CONFIG_PARSE_POSIX_NULL_ADDRESS,
+ CONFIG_PARSE_POSIX_NO_NAMESERVERS,
+ CONFIG_PARSE_POSIX_MISSING_OPTIONS,
+ CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS,
+ CONFIG_PARSE_POSIX_MAX // Bounding values for enumeration.
+};
+
// Fills in |dns_config| from |res|.
-bool NET_EXPORT_PRIVATE ConvertResStateToDnsConfig(
+ConfigParsePosixResult NET_EXPORT_PRIVATE ConvertResStateToDnsConfig(
const struct __res_state& res, DnsConfig* dns_config);
} // namespace internal
diff --git a/net/dns/dns_config_service_posix_unittest.cc b/net/dns/dns_config_service_posix_unittest.cc
index e1e2338..1c7dd36 100644
--- a/net/dns/dns_config_service_posix_unittest.cc
+++ b/net/dns/dns_config_service_posix_unittest.cc
@@ -30,7 +30,8 @@ const char* kNameserversIPv6[] = {
// Fills in |res| with sane configuration.
void InitializeResState(res_state res) {
memset(res, 0, sizeof(*res));
- res->options = RES_INIT | RES_ROTATE;
+ res->options = RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH |
+ RES_ROTATE;
res->ndots = 2;
res->retrans = 4;
res->retry = 7;
@@ -112,7 +113,8 @@ TEST(DnsConfigServicePosixTest, ConvertResStateToDnsConfig) {
DnsConfig config;
EXPECT_FALSE(config.IsValid());
InitializeResState(&res);
- ASSERT_TRUE(internal::ConvertResStateToDnsConfig(res, &config));
+ ASSERT_EQ(internal::CONFIG_PARSE_POSIX_OK,
+ internal::ConvertResStateToDnsConfig(res, &config));
CloseResState(&res);
EXPECT_TRUE(config.IsValid());
@@ -124,7 +126,7 @@ TEST(DnsConfigServicePosixTest, ConvertResStateToDnsConfig) {
TEST(DnsConfigServicePosixTest, RejectEmptyNameserver) {
struct __res_state res = {};
- res.options = RES_INIT;
+ res.options = RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
const char kDnsrch[] = "chromium.org";
memcpy(res.defdname, kDnsrch, sizeof(kDnsrch));
res.dnsrch[0] = res.defdname;
@@ -139,11 +141,13 @@ TEST(DnsConfigServicePosixTest, RejectEmptyNameserver) {
res.nscount = 2;
DnsConfig config;
- EXPECT_FALSE(internal::ConvertResStateToDnsConfig(res, &config));
+ EXPECT_EQ(internal::CONFIG_PARSE_POSIX_NULL_ADDRESS,
+ internal::ConvertResStateToDnsConfig(res, &config));
sa.sin_addr.s_addr = 0xDEADBEEF;
res.nsaddr_list[0] = sa;
- EXPECT_TRUE(internal::ConvertResStateToDnsConfig(res, &config));
+ EXPECT_EQ(internal::CONFIG_PARSE_POSIX_OK,
+ internal::ConvertResStateToDnsConfig(res, &config));
}
} // namespace
diff --git a/net/dns/dns_config_service_unittest.cc b/net/dns/dns_config_service_unittest.cc
index 462eadb..2533809 100644
--- a/net/dns/dns_config_service_unittest.cc
+++ b/net/dns/dns_config_service_unittest.cc
@@ -189,7 +189,7 @@ TEST_F(DnsConfigServiceTest, DifferentConfig) {
EXPECT_TRUE(last_config_.Equals(config3));
}
-#if defined(OS_POSIX) || defined(OS_WIN)
+#if (defined(OS_POSIX) && !defined(OS_ANDROID)) || defined(OS_WIN)
// TODO(szym): This is really an integration test and can time out if HOSTS is
// huge. http://crbug.com/107810
TEST_F(DnsConfigServiceTest, FLAKY_GetSystemConfig) {
diff --git a/net/dns/dns_config_service_win.cc b/net/dns/dns_config_service_win.cc
index 0c2e2c1..a6d503d 100644
--- a/net/dns/dns_config_service_win.cc
+++ b/net/dns/dns_config_service_win.cc
@@ -13,11 +13,13 @@
#include "base/file_path.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/synchronization/lock.h"
#include "base/threading/non_thread_safe.h"
#include "base/threading/thread_restrictions.h"
+#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/win/registry.h"
#include "base/win/windows_version.h"
@@ -39,6 +41,15 @@ namespace {
const wchar_t* const kPrimaryDnsSuffixPath =
L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient";
+enum HostsParseWinResult {
+ HOSTS_PARSE_WIN_OK = 0,
+ HOSTS_PARSE_WIN_UNREADABLE_HOSTS_FILE,
+ HOSTS_PARSE_WIN_COMPUTER_NAME_FAILED,
+ HOSTS_PARSE_WIN_IPHELPER_FAILED,
+ HOSTS_PARSE_WIN_BAD_ADDRESS,
+ HOSTS_PARSE_WIN_MAX // Bounding values for enumeration.
+};
+
// Convenience for reading values using RegKey.
class RegistryReader : public base::NonThreadSafe {
public:
@@ -85,7 +96,7 @@ class RegistryReader : public base::NonThreadSafe {
DISALLOW_COPY_AND_ASSIGN(RegistryReader);
};
-// Returns NULL if failed.
+// Wrapper for GetAdaptersAddresses. Returns NULL if failed.
scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> ReadIpHelper(ULONG flags) {
base::ThreadRestrictions::AssertIOAllowed();
@@ -132,6 +143,132 @@ bool ParseDomainASCII(const string16& widestr, std::string* domain) {
return success && !domain->empty();
}
+bool ReadDevolutionSetting(const RegistryReader& reader,
+ DnsSystemSettings::DevolutionSetting* setting) {
+ return reader.ReadDword(L"UseDomainNameDevolution", &setting->enabled) &&
+ reader.ReadDword(L"DomainNameDevolutionLevel", &setting->level);
+}
+
+// Reads DnsSystemSettings from IpHelper and registry.
+ConfigParseWinResult ReadSystemSettings(DnsSystemSettings* settings) {
+ settings->addresses = ReadIpHelper(GAA_FLAG_SKIP_ANYCAST |
+ GAA_FLAG_SKIP_UNICAST |
+ GAA_FLAG_SKIP_MULTICAST |
+ GAA_FLAG_SKIP_FRIENDLY_NAME);
+ if (!settings->addresses.get())
+ return CONFIG_PARSE_WIN_READ_IPHELPER;
+
+ RegistryReader tcpip_reader(kTcpipPath);
+ RegistryReader tcpip6_reader(kTcpip6Path);
+ RegistryReader dnscache_reader(kDnscachePath);
+ RegistryReader policy_reader(kPolicyPath);
+ RegistryReader primary_dns_suffix_reader(kPrimaryDnsSuffixPath);
+
+ if (!policy_reader.ReadString(L"SearchList",
+ &settings->policy_search_list)) {
+ return CONFIG_PARSE_WIN_READ_POLICY_SEARCHLIST;
+ }
+
+ if (!tcpip_reader.ReadString(L"SearchList", &settings->tcpip_search_list))
+ return CONFIG_PARSE_WIN_READ_TCPIP_SEARCHLIST;
+
+ if (!tcpip_reader.ReadString(L"Domain", &settings->tcpip_domain))
+ return CONFIG_PARSE_WIN_READ_DOMAIN;
+
+ if (!ReadDevolutionSetting(policy_reader, &settings->policy_devolution))
+ return CONFIG_PARSE_WIN_READ_POLICY_DEVOLUTION;
+
+ if (!ReadDevolutionSetting(dnscache_reader, &settings->dnscache_devolution))
+ return CONFIG_PARSE_WIN_READ_DNSCACHE_DEVOLUTION;
+
+ if (!ReadDevolutionSetting(tcpip_reader, &settings->tcpip_devolution))
+ return CONFIG_PARSE_WIN_READ_TCPIP_DEVOLUTION;
+
+ if (!policy_reader.ReadDword(L"AppendToMultiLabelName",
+ &settings->append_to_multi_label_name)) {
+ return CONFIG_PARSE_WIN_READ_APPEND_MULTILABEL;
+ }
+
+ if (!primary_dns_suffix_reader.ReadString(L"PrimaryDnsSuffix",
+ &settings->primary_dns_suffix)) {
+ return CONFIG_PARSE_WIN_READ_PRIMARY_SUFFIX;
+ }
+ return CONFIG_PARSE_WIN_OK;
+}
+
+// Default address of "localhost" and local computer name can be overridden
+// by the HOSTS file, but if it's not there, then we need to fill it in.
+HostsParseWinResult AddLocalhostEntries(DnsHosts* hosts) {
+ const unsigned char kIPv4Localhost[] = { 127, 0, 0, 1 };
+ const unsigned char kIPv6Localhost[] = { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1 };
+ IPAddressNumber loopback_ipv4(kIPv4Localhost,
+ kIPv4Localhost + arraysize(kIPv4Localhost));
+ IPAddressNumber loopback_ipv6(kIPv6Localhost,
+ kIPv6Localhost + arraysize(kIPv6Localhost));
+
+ // This does not override any pre-existing entries from the HOSTS file.
+ hosts->insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4),
+ loopback_ipv4));
+ hosts->insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV6),
+ loopback_ipv6));
+
+ WCHAR buffer[MAX_PATH];
+ DWORD size = MAX_PATH;
+ std::string localname;
+ if (!GetComputerNameExW(ComputerNameDnsHostname, buffer, &size) ||
+ !ParseDomainASCII(buffer, &localname)) {
+ return HOSTS_PARSE_WIN_COMPUTER_NAME_FAILED;
+ }
+ StringToLowerASCII(&localname);
+
+ bool have_ipv4 =
+ hosts->count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)) > 0;
+ bool have_ipv6 =
+ hosts->count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)) > 0;
+
+ if (have_ipv4 && have_ipv6)
+ return HOSTS_PARSE_WIN_OK;
+
+ scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> addresses =
+ ReadIpHelper(GAA_FLAG_SKIP_ANYCAST |
+ GAA_FLAG_SKIP_DNS_SERVER |
+ GAA_FLAG_SKIP_MULTICAST |
+ GAA_FLAG_SKIP_FRIENDLY_NAME);
+ if (!addresses.get())
+ return HOSTS_PARSE_WIN_IPHELPER_FAILED;
+
+ // The order of adapters is the network binding order, so stick to the
+ // first good adapter for each family.
+ for (const IP_ADAPTER_ADDRESSES* adapter = addresses.get();
+ adapter != NULL && (!have_ipv4 || !have_ipv6);
+ adapter = adapter->Next) {
+ if (adapter->OperStatus != IfOperStatusUp)
+ continue;
+ if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
+ continue;
+
+ for (const IP_ADAPTER_UNICAST_ADDRESS* address =
+ adapter->FirstUnicastAddress;
+ address != NULL;
+ address = address->Next) {
+ IPEndPoint ipe;
+ if (!ipe.FromSockAddr(address->Address.lpSockaddr,
+ address->Address.iSockaddrLength)) {
+ return HOSTS_PARSE_WIN_BAD_ADDRESS;
+ }
+ if (!have_ipv4 && (ipe.GetFamily() == AF_INET)) {
+ have_ipv4 = true;
+ (*hosts)[DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)] = ipe.address();
+ } else if (!have_ipv6 && (ipe.GetFamily() == AF_INET6)) {
+ have_ipv6 = true;
+ (*hosts)[DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)] = ipe.address();
+ }
+ }
+ }
+ return HOSTS_PARSE_WIN_OK;
+}
+
} // namespace
FilePath GetHostsPath() {
@@ -166,8 +303,9 @@ bool ParseSearchList(const string16& value, std::vector<std::string>* output) {
return !output->empty();
}
-bool ConvertSettingsToDnsConfig(const DnsSystemSettings& settings,
- DnsConfig* config) {
+ConfigParseWinResult ConvertSettingsToDnsConfig(
+ const DnsSystemSettings& settings,
+ DnsConfig* config) {
*config = DnsConfig();
// Use GetAdapterAddresses to get effective DNS server order and
@@ -194,7 +332,7 @@ bool ConvertSettingsToDnsConfig(const DnsSystemSettings& settings,
ipe = IPEndPoint(ipe.address(), dns_protocol::kDefaultPort);
config->nameservers.push_back(ipe);
} else {
- return false;
+ return CONFIG_PARSE_WIN_BAD_ADDRESS;
}
}
@@ -209,7 +347,7 @@ bool ConvertSettingsToDnsConfig(const DnsSystemSettings& settings,
}
if (config->nameservers.empty())
- return false; // No point continuing.
+ return CONFIG_PARSE_WIN_NO_NAMESERVERS; // No point continuing.
// Windows always tries a multi-label name "as is" before using suffixes.
config->ndots = 1;
@@ -231,14 +369,14 @@ bool ConvertSettingsToDnsConfig(const DnsSystemSettings& settings,
std::vector<std::string> search;
if (ParseSearchList(settings.policy_search_list.value, &search)) {
config->search.swap(search);
- return true;
+ return CONFIG_PARSE_WIN_OK;
}
// Even if invalid, the policy disables the user-specified setting below.
} else if (settings.tcpip_search_list.set) {
std::vector<std::string> search;
if (ParseSearchList(settings.tcpip_search_list.value, &search)) {
config->search.swap(search);
- return true;
+ return CONFIG_PARSE_WIN_OK;
}
}
@@ -261,7 +399,7 @@ bool ConvertSettingsToDnsConfig(const DnsSystemSettings& settings,
// Primary suffix goes in front.
config->search.insert(config->search.begin(), primary_suffix);
} else {
- return true; // No primary suffix, hence no devolution.
+ return CONFIG_PARSE_WIN_OK; // No primary suffix, hence no devolution.
}
// Devolution is determined by precedence: policy > dnscache > tcpip.
@@ -274,7 +412,7 @@ bool ConvertSettingsToDnsConfig(const DnsSystemSettings& settings,
if (!devolution.enabled.set)
devolution.enabled = settings.tcpip_devolution.enabled;
if (devolution.enabled.set && (devolution.enabled.value == 0))
- return true; // Devolution disabled.
+ return CONFIG_PARSE_WIN_OK; // Devolution disabled.
// By default devolution is enabled.
@@ -293,7 +431,7 @@ bool ConvertSettingsToDnsConfig(const DnsSystemSettings& settings,
//
// If the level is explicitly set below 2, devolution is disabled.
if (!devolution.level.set || devolution.level.value < 2)
- return true; // Devolution disabled.
+ return CONFIG_PARSE_WIN_OK; // Devolution disabled.
// Devolve the primary suffix. This naive logic matches the observed
// behavior (see also ParseSearchList). If a suffix is not valid, it will be
@@ -306,13 +444,10 @@ bool ConvertSettingsToDnsConfig(const DnsSystemSettings& settings,
offset = primary_suffix.find('.', offset + 1);
config->search.push_back(primary_suffix.substr(offset + 1));
}
-
- return true;
+ return CONFIG_PARSE_WIN_OK;
}
-// Watches registry for changes and reads config from registry and IP helper.
-// Reading and opening of reg keys is always performed on WorkerPool. Setting
-// up watches requires IO loop.
+// Reads config from registry and IpHelper. All work performed on WorkerPool.
class DnsConfigServiceWin::ConfigReader : public SerialWorker {
public:
explicit ConfigReader(DnsConfigServiceWin* service)
@@ -320,59 +455,21 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker {
success_(false) {}
private:
- bool ReadDevolutionSetting(const RegistryReader& reader,
- DnsSystemSettings::DevolutionSetting& setting) {
- return reader.ReadDword(L"UseDomainNameDevolution", &setting.enabled) &&
- reader.ReadDword(L"DomainNameDevolutionLevel", &setting.level);
- }
+ virtual ~ConfigReader() {}
virtual void DoWork() OVERRIDE {
// Should be called on WorkerPool.
- success_ = false;
-
+ base::TimeTicks start_time = base::TimeTicks::Now();
DnsSystemSettings settings = {};
- settings.addresses = ReadIpHelper(GAA_FLAG_SKIP_ANYCAST |
- GAA_FLAG_SKIP_UNICAST |
- GAA_FLAG_SKIP_MULTICAST |
- GAA_FLAG_SKIP_FRIENDLY_NAME);
- if (!settings.addresses.get())
- return; // no point reading the rest
-
- RegistryReader tcpip_reader(kTcpipPath);
- RegistryReader tcpip6_reader(kTcpip6Path);
- RegistryReader dnscache_reader(kDnscachePath);
- RegistryReader policy_reader(kPolicyPath);
- RegistryReader primary_dns_suffix_reader(kPrimaryDnsSuffixPath);
-
- if (!policy_reader.ReadString(L"SearchList",
- &settings.policy_search_list))
- return;
-
- if (!tcpip_reader.ReadString(L"SearchList", &settings.tcpip_search_list))
- return;
-
- if (!tcpip_reader.ReadString(L"Domain", &settings.tcpip_domain))
- return;
-
- if (!ReadDevolutionSetting(policy_reader, settings.policy_devolution))
- return;
-
- if (!ReadDevolutionSetting(dnscache_reader,
- settings.dnscache_devolution))
- return;
-
- if (!ReadDevolutionSetting(tcpip_reader, settings.tcpip_devolution))
- return;
-
- if (!policy_reader.ReadDword(L"AppendToMultiLabelName",
- &settings.append_to_multi_label_name))
- return;
-
- if (!primary_dns_suffix_reader.ReadString(L"PrimaryDnsSuffix",
- &settings.primary_dns_suffix))
- return;
-
- success_ = ConvertSettingsToDnsConfig(settings, &dns_config_);
+ ConfigParseWinResult result = ReadSystemSettings(&settings);
+ if (result == CONFIG_PARSE_WIN_OK)
+ result = ConvertSettingsToDnsConfig(settings, &dns_config_);
+ success_ = (result == CONFIG_PARSE_WIN_OK);
+ UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParseWin",
+ result, CONFIG_PARSE_WIN_MAX);
+ UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_);
+ UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration",
+ base::TimeTicks::Now() - start_time);
}
virtual void OnWorkFinished() OVERRIDE {
@@ -391,9 +488,8 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker {
bool success_;
};
-// An extension for DnsHostsReader which also watches the HOSTS file,
-// reads local name from GetComputerNameEx, local IP from GetAdaptersAddresses,
-// and observes changes to local IP address.
+// Reads hosts from HOSTS file and fills in localhost and local computer name if
+// necessary. All work performed on WorkerPool.
class DnsConfigServiceWin::HostsReader : public SerialWorker {
public:
explicit HostsReader(DnsConfigServiceWin* service)
@@ -401,88 +497,19 @@ class DnsConfigServiceWin::HostsReader : public SerialWorker {
}
private:
- virtual void DoWork() OVERRIDE {
- success_ = ParseHostsFile(path_, &hosts_);
-
- if (!success_)
- return;
-
- success_ = false;
-
- // Default address of "localhost" and local computer name can be overridden
- // by the HOSTS file, but if it's not there, then we need to fill it in.
-
- const unsigned char kIPv4Localhost[] = { 127, 0, 0, 1 };
- const unsigned char kIPv6Localhost[] = { 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 1 };
- IPAddressNumber loopback_ipv4(kIPv4Localhost,
- kIPv4Localhost + arraysize(kIPv4Localhost));
- IPAddressNumber loopback_ipv6(kIPv6Localhost,
- kIPv6Localhost + arraysize(kIPv6Localhost));
-
- // This does not override any pre-existing entries from the HOSTS file.
- hosts_.insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4),
- loopback_ipv4));
- hosts_.insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV6),
- loopback_ipv6));
-
- WCHAR buffer[MAX_PATH];
- DWORD size = MAX_PATH;
- std::string localname;
- if (!GetComputerNameExW(ComputerNameDnsHostname, buffer, &size) ||
- !ParseDomainASCII(buffer, &localname)) {
- LOG(ERROR) << "Failed to read local computer name";
- return;
- }
- StringToLowerASCII(&localname);
-
- bool have_ipv4 =
- hosts_.count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)) > 0;
- bool have_ipv6 =
- hosts_.count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)) > 0;
-
- if (have_ipv4 && have_ipv6) {
- success_ = true;
- return;
- }
+ virtual ~HostsReader() {}
- scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> addresses =
- ReadIpHelper(GAA_FLAG_SKIP_ANYCAST |
- GAA_FLAG_SKIP_DNS_SERVER |
- GAA_FLAG_SKIP_MULTICAST |
- GAA_FLAG_SKIP_FRIENDLY_NAME);
- if (!addresses.get())
- return;
-
- // The order of adapters is the network binding order, so stick to the
- // first good adapter for each family.
- for (const IP_ADAPTER_ADDRESSES* adapter = addresses.get();
- adapter != NULL && (!have_ipv4 || !have_ipv6);
- adapter = adapter->Next) {
- if (adapter->OperStatus != IfOperStatusUp)
- continue;
- if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
- continue;
-
- for (const IP_ADAPTER_UNICAST_ADDRESS* address =
- adapter->FirstUnicastAddress;
- address != NULL;
- address = address->Next) {
- IPEndPoint ipe;
- if (!ipe.FromSockAddr(address->Address.lpSockaddr,
- address->Address.iSockaddrLength)) {
- return;
- }
- if (!have_ipv4 && (ipe.GetFamily() == AF_INET)) {
- have_ipv4 = true;
- hosts_[DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)] = ipe.address();
- } else if (!have_ipv6 && (ipe.GetFamily() == AF_INET6)) {
- have_ipv6 = true;
- hosts_[DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)] = ipe.address();
- }
- }
- }
- success_ = true;
+ virtual void DoWork() OVERRIDE {
+ base::TimeTicks start_time = base::TimeTicks::Now();
+ HostsParseWinResult result = HOSTS_PARSE_WIN_UNREADABLE_HOSTS_FILE;
+ if (ParseHostsFile(path_, &hosts_))
+ result = AddLocalhostEntries(&hosts_);
+ success_ = (result == HOSTS_PARSE_WIN_OK);
+ UMA_HISTOGRAM_ENUMERATION("AsyncDNS.HostsParseWin",
+ result, HOSTS_PARSE_WIN_MAX);
+ UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostParseResult", success_);
+ UMA_HISTOGRAM_TIMES("AsyncDNS.HostsParseDuration",
+ base::TimeTicks::Now() - start_time);
}
virtual void OnWorkFinished() OVERRIDE {
@@ -508,7 +535,6 @@ DnsConfigServiceWin::DnsConfigServiceWin()
hosts_reader_(new HostsReader(this)) {}
DnsConfigServiceWin::~DnsConfigServiceWin() {
- DCHECK(CalledOnValidThread());
config_reader_->Cancel();
hosts_reader_->Cancel();
NetworkChangeNotifier::RemoveIPAddressObserver(this);
diff --git a/net/dns/dns_config_service_win.h b/net/dns/dns_config_service_win.h
index a502e8b..830b181 100644
--- a/net/dns/dns_config_service_win.h
+++ b/net/dns/dns_config_service_win.h
@@ -101,8 +101,24 @@ struct NET_EXPORT_PRIVATE DnsSystemSettings {
RegDword append_to_multi_label_name;
};
+enum ConfigParseWinResult {
+ CONFIG_PARSE_WIN_OK = 0,
+ CONFIG_PARSE_WIN_READ_IPHELPER,
+ CONFIG_PARSE_WIN_READ_POLICY_SEARCHLIST,
+ CONFIG_PARSE_WIN_READ_TCPIP_SEARCHLIST,
+ CONFIG_PARSE_WIN_READ_DOMAIN,
+ CONFIG_PARSE_WIN_READ_POLICY_DEVOLUTION,
+ CONFIG_PARSE_WIN_READ_DNSCACHE_DEVOLUTION,
+ CONFIG_PARSE_WIN_READ_TCPIP_DEVOLUTION,
+ CONFIG_PARSE_WIN_READ_APPEND_MULTILABEL,
+ CONFIG_PARSE_WIN_READ_PRIMARY_SUFFIX,
+ CONFIG_PARSE_WIN_BAD_ADDRESS,
+ CONFIG_PARSE_WIN_NO_NAMESERVERS,
+ CONFIG_PARSE_WIN_MAX // Bounding values for enumeration.
+};
+
// Fills in |dns_config| from |settings|. Exposed for tests.
-bool NET_EXPORT_PRIVATE ConvertSettingsToDnsConfig(
+ConfigParseWinResult NET_EXPORT_PRIVATE ConvertSettingsToDnsConfig(
const DnsSystemSettings& settings,
DnsConfig* dns_config);
diff --git a/net/dns/dns_config_service_win_unittest.cc b/net/dns/dns_config_service_win_unittest.cc
index 63e4268..c3cce9b 100644
--- a/net/dns/dns_config_service_win_unittest.cc
+++ b/net/dns/dns_config_service_win_unittest.cc
@@ -186,11 +186,14 @@ TEST(DnsConfigServiceWinTest, ConvertAdapterAddresses) {
}
DnsConfig config;
- bool result = internal::ConvertSettingsToDnsConfig(settings, &config);
- bool expected_result = !expected_nameservers.empty();
- ASSERT_EQ(expected_result, result);
+ internal::ConfigParseWinResult result =
+ internal::ConvertSettingsToDnsConfig(settings, &config);
+ internal::ConfigParseWinResult expected_result =
+ expected_nameservers.empty() ? internal::CONFIG_PARSE_WIN_NO_NAMESERVERS
+ : internal::CONFIG_PARSE_WIN_OK;
+ EXPECT_EQ(expected_result, result);
EXPECT_EQ(expected_nameservers, config.nameservers);
- if (result) {
+ if (result == internal::CONFIG_PARSE_WIN_OK) {
ASSERT_EQ(1u, config.search.size());
EXPECT_EQ(t.expected_suffix, config.search[0]);
}
@@ -375,8 +378,8 @@ TEST(DnsConfigServiceWinTest, ConvertSuffixSearch) {
for (size_t i = 0; i < arraysize(cases); ++i) {
const TestCase& t = cases[i];
DnsConfig config;
- ASSERT_TRUE(internal::ConvertSettingsToDnsConfig(t.input_settings,
- &config));
+ EXPECT_EQ(internal::CONFIG_PARSE_WIN_OK,
+ internal::ConvertSettingsToDnsConfig(t.input_settings, &config));
std::vector<std::string> expected_search;
for (size_t j = 0; !t.expected_search[j].empty(); ++j) {
expected_search.push_back(t.expected_search[j]);
@@ -414,7 +417,8 @@ TEST(DnsConfigServiceWinTest, AppendToMultiLabelName) {
t.input,
};
DnsConfig config;
- ASSERT_TRUE(internal::ConvertSettingsToDnsConfig(settings, &config));
+ EXPECT_EQ(internal::CONFIG_PARSE_WIN_OK,
+ internal::ConvertSettingsToDnsConfig(settings, &config));
EXPECT_EQ(config.append_to_multi_label_name, t.expected_output);
}
}