Chromium Code Reviews| 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 |
| index 5daaf69aac7b1c1357d08ce7b099a83eac75c56f..4428c97abfda226eb3400bfd7f57390b1e91b092 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,7 @@ |
| namespace net { |
| +#if !defined(OS_ANDROID) |
| namespace { |
| #ifndef _PATH_RESCONF // Normally defined in <resolv.h> |
| @@ -27,6 +30,24 @@ namespace { |
| const FilePath::CharType* kFilePathHosts = FILE_PATH_LITERAL("/etc/hosts"); |
| +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. |
| +}; |
| + |
| +static void RecordConfigParseResult(ConfigParsePosixResult result) { |
| + UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParsePosix", |
| + result, CONFIG_PARSE_POSIX_MAX); |
| +} |
| + |
| // A SerialWorker that uses libresolv to initialize res_state and converts |
| // it to DnsConfig. |
| class ConfigReader : public SerialWorker { |
| @@ -36,21 +57,25 @@ class ConfigReader : public SerialWorker { |
| : callback_(callback), success_(false) {} |
| void DoWork() OVERRIDE { |
| + base::TimeTicks start_time = base::TimeTicks::Now(); |
| success_ = false; |
| -#if defined(OS_ANDROID) |
| - NOTIMPLEMENTED(); |
| -#elif defined(OS_OPENBSD) |
| +#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) && (_res.options & RES_INIT)) |
| + if (res_init() == 0) { |
| success_ = internal::ConvertResStateToDnsConfig(_res, &dns_config_); |
| + } else { |
| + RecordConfigParseResult(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) && (res.options & RES_INIT)) |
| + if (res_ninit(&res) == 0) { |
| success_ = internal::ConvertResStateToDnsConfig(res, &dns_config_); |
| - |
| + } else { |
| + RecordConfigParseResult(CONFIG_PARSE_POSIX_RES_INIT_FAILED); |
| + } |
| // Prefer res_ndestroy where available. |
| #if defined(OS_MACOSX) || defined(OS_FREEBSD) |
| res_ndestroy(&res); |
| @@ -58,6 +83,9 @@ class ConfigReader : public SerialWorker { |
| res_nclose(&res); |
| #endif |
| #endif |
| + UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_); |
|
mmenke
2012/06/18 17:19:50
Do these give us anything that AsyncDNS.ConfigPars
szym
2012/06/18 20:53:08
They would share the name across platforms. Not su
mmenke
2012/06/18 20:55:56
They wouldn't, though, since the windows one is ca
szym
2012/06/18 20:59:55
That was my mistake. Notice that initially both re
|
| + UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration", |
| + base::TimeTicks::Now() - start_time); |
| } |
| void OnWorkFinished() OVERRIDE { |
| @@ -91,7 +119,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 { |
| @@ -150,11 +182,13 @@ void DnsConfigServicePosix::OnDNSChanged(unsigned detail) { |
| } |
| } |
| -#if !defined(OS_ANDROID) |
| bool ConvertResStateToDnsConfig(const struct __res_state& res, |
| DnsConfig* dns_config) { |
|
mmenke
2012/06/18 17:19:50
Optional: I'd suggest making this return a Config
|
| CHECK(dns_config != NULL); |
| - DCHECK(res.options & RES_INIT); |
| + if (!(res.options & RES_INIT)) { |
| + RecordConfigParseResult(CONFIG_PARSE_POSIX_RES_INIT_UNSET); |
| + return false; |
| + } |
| dns_config->nameservers.clear(); |
| @@ -168,6 +202,7 @@ bool ConvertResStateToDnsConfig(const struct __res_state& res, |
| if (!ipe.FromSockAddr( |
| reinterpret_cast<const struct sockaddr*>(&addresses[i]), |
| sizeof addresses[i])) { |
| + RecordConfigParseResult(CONFIG_PARSE_POSIX_BAD_ADDRESS); |
| return false; |
| } |
| dns_config->nameservers.push_back(ipe); |
| @@ -191,10 +226,13 @@ 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 { |
| + RecordConfigParseResult(CONFIG_PARSE_POSIX_BAD_EXT_STRUCT); |
| return false; |
| } |
| - if (!ipe.FromSockAddr(addr, addr_len)) |
| + if (!ipe.FromSockAddr(addr, addr_len)) { |
| + RecordConfigParseResult(CONFIG_PARSE_POSIX_BAD_ADDRESS); |
| return false; |
| + } |
| dns_config->nameservers.push_back(ipe); |
| } |
| #else // !(defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FREEBSD)) |
| @@ -204,6 +242,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])) { |
| + RecordConfigParseResult(CONFIG_PARSE_POSIX_BAD_ADDRESS); |
| return false; |
| } |
| dns_config->nameservers.push_back(ipe); |
| @@ -223,15 +262,37 @@ 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) { |
| + RecordConfigParseResult(CONFIG_PARSE_POSIX_MISSING_OPTIONS); |
| + return false; |
| + } |
| + |
| + unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC; |
| + if (res.options & kUnhandledOptions) { |
| + RecordConfigParseResult(CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS); |
| + return false; |
| + } |
| + |
| + if (dns_config->nameservers.empty()) { |
| + RecordConfigParseResult(CONFIG_PARSE_POSIX_NO_NAMESERVERS); |
| + return false; |
| + } |
| + |
| // 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) |
| + for (unsigned i = 0; i < dns_config->nameservers.size(); ++i) { |
| + if (dns_config->nameservers[i].address() == kEmptyAddress) { |
| + RecordConfigParseResult(CONFIG_PARSE_POSIX_NULL_ADDRESS); |
| return false; |
| + } |
| + } |
| + RecordConfigParseResult(CONFIG_PARSE_POSIX_OK); |
| return true; |
| } |
| -#endif // !defined(OS_ANDROID) |
| } // namespace internal |
| @@ -240,4 +301,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 |