diff options
author | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-19 21:21:29 +0000 |
---|---|---|
committer | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-19 21:21:29 +0000 |
commit | 539df6c7207426d0626ee1bc76cf6f9a2f3766c0 (patch) | |
tree | dca556cc0d34579feee1062540f1f5a9e4631e83 /net/dns | |
parent | e1c2607d73e9ce24e3fec9193d24739599e64241 (diff) | |
download | chromium_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.cc | 27 | ||||
-rw-r--r-- | net/dns/dns_config_service.h | 9 | ||||
-rw-r--r-- | net/dns/dns_config_service_posix.cc | 121 | ||||
-rw-r--r-- | net/dns/dns_config_service_posix.h | 15 | ||||
-rw-r--r-- | net/dns/dns_config_service_posix_unittest.cc | 14 | ||||
-rw-r--r-- | net/dns/dns_config_service_unittest.cc | 2 | ||||
-rw-r--r-- | net/dns/dns_config_service_win.cc | 324 | ||||
-rw-r--r-- | net/dns/dns_config_service_win.h | 18 | ||||
-rw-r--r-- | net/dns/dns_config_service_win_unittest.cc | 18 |
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); } } |