diff options
Diffstat (limited to 'chromium/net/dns')
34 files changed, 1448 insertions, 563 deletions
diff --git a/chromium/net/dns/address_sorter_posix_unittest.cc b/chromium/net/dns/address_sorter_posix_unittest.cc index 96cbfc6fcb0..c4517379957 100644 --- a/chromium/net/dns/address_sorter_posix_unittest.cc +++ b/chromium/net/dns/address_sorter_posix_unittest.cc @@ -10,6 +10,8 @@ #include "net/base/net_util.h" #include "net/base/test_completion_callback.h" #include "net/socket/client_socket_factory.h" +#include "net/socket/ssl_client_socket.h" +#include "net/socket/stream_socket.h" #include "net/udp/datagram_client_socket.h" #include "testing/gtest/include/gtest/gtest.h" @@ -90,27 +92,27 @@ class TestSocketFactory : public ClientSocketFactory { TestSocketFactory() {} virtual ~TestSocketFactory() {} - virtual DatagramClientSocket* CreateDatagramClientSocket( + virtual scoped_ptr<DatagramClientSocket> CreateDatagramClientSocket( DatagramSocket::BindType, const RandIntCallback&, NetLog*, const NetLog::Source&) OVERRIDE { - return new TestUDPClientSocket(&mapping_); + return scoped_ptr<DatagramClientSocket>(new TestUDPClientSocket(&mapping_)); } - virtual StreamSocket* CreateTransportClientSocket( + virtual scoped_ptr<StreamSocket> CreateTransportClientSocket( const AddressList&, NetLog*, const NetLog::Source&) OVERRIDE { NOTIMPLEMENTED(); - return NULL; + return scoped_ptr<StreamSocket>(); } - virtual SSLClientSocket* CreateSSLClientSocket( - ClientSocketHandle*, + virtual scoped_ptr<SSLClientSocket> CreateSSLClientSocket( + scoped_ptr<ClientSocketHandle>, const HostPortPair&, const SSLConfig&, const SSLClientSocketContext&) OVERRIDE { NOTIMPLEMENTED(); - return NULL; + return scoped_ptr<SSLClientSocket>(); } virtual void ClearSSLSessionCache() OVERRIDE { NOTIMPLEMENTED(); diff --git a/chromium/net/dns/dns_client.cc b/chromium/net/dns/dns_client.cc index 976f1533905..9e29ca4265d 100644 --- a/chromium/net/dns/dns_client.cc +++ b/chromium/net/dns/dns_client.cc @@ -27,7 +27,7 @@ class DnsClientImpl : public DnsClient { virtual void SetConfig(const DnsConfig& config) OVERRIDE { factory_.reset(); session_ = NULL; - if (config.IsValid()) { + if (config.IsValid() && !config.unhandled_options) { ClientSocketFactory* factory = ClientSocketFactory::GetDefaultFactory(); scoped_ptr<DnsSocketPool> socket_pool( config.randomize_ports ? DnsSocketPool::CreateDefault(factory) diff --git a/chromium/net/dns/dns_client.h b/chromium/net/dns/dns_client.h index 650c7d0d416..0484d44c0c6 100644 --- a/chromium/net/dns/dns_client.h +++ b/chromium/net/dns/dns_client.h @@ -22,7 +22,8 @@ class NET_EXPORT DnsClient { public: virtual ~DnsClient() {} - // Creates a new DnsTransactionFactory according to the new |config|. + // Destroys the current DnsTransactionFactory and creates a new one + // according to |config|, unless it is invalid or has |unhandled_options|. virtual void SetConfig(const DnsConfig& config) = 0; // Returns NULL if the current config is not valid. diff --git a/chromium/net/dns/dns_config_service.cc b/chromium/net/dns/dns_config_service.cc index ea8a3421cd2..66131825571 100644 --- a/chromium/net/dns/dns_config_service.cc +++ b/chromium/net/dns/dns_config_service.cc @@ -14,13 +14,15 @@ namespace net { // Default values are taken from glibc resolv.h except timeout which is set to // |kDnsTimeoutSeconds|. DnsConfig::DnsConfig() - : append_to_multi_label_name(true), + : unhandled_options(false), + append_to_multi_label_name(true), randomize_ports(false), ndots(1), timeout(base::TimeDelta::FromSeconds(kDnsTimeoutSeconds)), attempts(2), rotate(false), - edns0(false) {} + edns0(false), + use_local_ipv6(false) {} DnsConfig::~DnsConfig() {} @@ -31,23 +33,27 @@ bool DnsConfig::Equals(const DnsConfig& d) const { bool DnsConfig::EqualsIgnoreHosts(const DnsConfig& d) const { return (nameservers == d.nameservers) && (search == d.search) && + (unhandled_options == d.unhandled_options) && (append_to_multi_label_name == d.append_to_multi_label_name) && (ndots == d.ndots) && (timeout == d.timeout) && (attempts == d.attempts) && (rotate == d.rotate) && - (edns0 == d.edns0); + (edns0 == d.edns0) && + (use_local_ipv6 == d.use_local_ipv6); } void DnsConfig::CopyIgnoreHosts(const DnsConfig& d) { nameservers = d.nameservers; search = d.search; + unhandled_options = d.unhandled_options; append_to_multi_label_name = d.append_to_multi_label_name; ndots = d.ndots; timeout = d.timeout; attempts = d.attempts; rotate = d.rotate; edns0 = d.edns0; + use_local_ipv6 = d.use_local_ipv6; } base::Value* DnsConfig::ToValue() const { @@ -63,12 +69,14 @@ base::Value* DnsConfig::ToValue() const { list->Append(new base::StringValue(search[i])); dict->Set("search", list); + dict->SetBoolean("unhandled_options", unhandled_options); dict->SetBoolean("append_to_multi_label_name", append_to_multi_label_name); dict->SetInteger("ndots", ndots); dict->SetDouble("timeout", timeout.InSecondsF()); dict->SetInteger("attempts", attempts); dict->SetBoolean("rotate", rotate); dict->SetBoolean("edns0", edns0); + dict->SetBoolean("use_local_ipv6", use_local_ipv6); dict->SetInteger("num_hosts", hosts.size()); return dict; diff --git a/chromium/net/dns/dns_config_service.h b/chromium/net/dns/dns_config_service.h index 4babb9e7d37..7386e601790 100644 --- a/chromium/net/dns/dns_config_service.h +++ b/chromium/net/dns/dns_config_service.h @@ -59,6 +59,10 @@ struct NET_EXPORT_PRIVATE DnsConfig { DnsHosts hosts; + // True if there are options set in the system configuration that are not yet + // supported by DnsClient. + bool unhandled_options; + // AppendToMultiLabelName: is suffix search performed for multi-label names? // True, except on Windows where it can be configured. bool append_to_multi_label_name; @@ -79,6 +83,11 @@ struct NET_EXPORT_PRIVATE DnsConfig { bool rotate; // Enable EDNS0 extensions. bool edns0; + + // Indicates system configuration uses local IPv6 connectivity, e.g., + // DirectAccess. This is exposed for HostResolver to skip IPv6 probes, + // as it may cause them to return incorrect results. + bool use_local_ipv6; }; diff --git a/chromium/net/dns/dns_config_service_posix.cc b/chromium/net/dns/dns_config_service_posix.cc index ff2295e704a..baf917284c7 100644 --- a/chromium/net/dns/dns_config_service_posix.cc +++ b/chromium/net/dns/dns_config_service_posix.cc @@ -10,6 +10,7 @@ #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" @@ -20,6 +21,69 @@ #include "net/dns/notify_watcher_mac.h" #include "net/dns/serial_worker.h" +#if defined(OS_MACOSX) +#include <dlfcn.h> + +#include "third_party/apple_apsl/dnsinfo.h" + +namespace { + +// dnsinfo symbols are available via libSystem.dylib, but can also be present in +// SystemConfiguration.framework. To avoid confusion, load them explicitly from +// libSystem.dylib. +class DnsInfoApi { + public: + typedef const char* (*dns_configuration_notify_key_t)(); + typedef dns_config_t* (*dns_configuration_copy_t)(); + typedef void (*dns_configuration_free_t)(dns_config_t*); + + DnsInfoApi() + : dns_configuration_notify_key(NULL), + dns_configuration_copy(NULL), + dns_configuration_free(NULL) { + handle_ = dlopen("/usr/lib/libSystem.dylib", + RTLD_LAZY | RTLD_NOLOAD); + if (!handle_) + return; + dns_configuration_notify_key = + reinterpret_cast<dns_configuration_notify_key_t>( + dlsym(handle_, "dns_configuration_notify_key")); + dns_configuration_copy = + reinterpret_cast<dns_configuration_copy_t>( + dlsym(handle_, "dns_configuration_copy")); + dns_configuration_free = + reinterpret_cast<dns_configuration_free_t>( + dlsym(handle_, "dns_configuration_free")); + } + + ~DnsInfoApi() { + if (handle_) + dlclose(handle_); + } + + dns_configuration_notify_key_t dns_configuration_notify_key; + dns_configuration_copy_t dns_configuration_copy; + dns_configuration_free_t dns_configuration_free; + + private: + void* handle_; +}; + +const DnsInfoApi& GetDnsInfoApi() { + static base::LazyInstance<DnsInfoApi>::Leaky api = LAZY_INSTANCE_INITIALIZER; + return api.Get(); +} + +struct DnsConfigTDeleter { + inline void operator()(dns_config_t* ptr) const { + if (GetDnsInfoApi().dns_configuration_free) + GetDnsInfoApi().dns_configuration_free(ptr); + } +}; + +} // namespace +#endif // defined(OS_MACOSX) + namespace net { #if !defined(OS_ANDROID) @@ -31,14 +95,13 @@ const base::FilePath::CharType* kFilePathHosts = FILE_PATH_LITERAL("/etc/hosts"); #if defined(OS_MACOSX) -// From 10.7.3 configd-395.10/dnsinfo/dnsinfo.h -static const char* kDnsNotifyKey = - "com.apple.system.SystemConfiguration.dns_configuration"; - class ConfigWatcher { public: bool Watch(const base::Callback<void(bool succeeded)>& callback) { - return watcher_.Watch(kDnsNotifyKey, callback); + if (!GetDnsInfoApi().dns_configuration_notify_key) + return false; + return watcher_.Watch(GetDnsInfoApi().dns_configuration_notify_key(), + callback); } private: @@ -76,6 +139,7 @@ class ConfigWatcher { 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. @@ -100,6 +164,32 @@ ConfigParsePosixResult ReadDnsConfig(DnsConfig* config) { res_nclose(&res); #endif #endif + +#if defined(OS_MACOSX) + if (!GetDnsInfoApi().dns_configuration_copy) + return CONFIG_PARSE_POSIX_NO_DNSINFO; + scoped_ptr<dns_config_t, DnsConfigTDeleter> dns_config( + GetDnsInfoApi().dns_configuration_copy()); + if (!dns_config) + return CONFIG_PARSE_POSIX_NO_DNSINFO; + + // TODO(szym): Parse dns_config_t for resolvers rather than res_state. + // DnsClient can't handle domain-specific unscoped resolvers. + unsigned num_resolvers = 0; + for (int i = 0; i < dns_config->n_resolver; ++i) { + dns_resolver_t* resolver = dns_config->resolver[i]; + if (!resolver->n_nameserver) + continue; + if (resolver->options && !strcmp(resolver->options, "mdns")) + continue; + ++num_resolvers; + } + if (num_resolvers > 1) { + LOG(WARNING) << "dns_config has unhandled options!"; + config->unhandled_options = true; + return CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS; + } +#endif // defined(OS_MACOSX) // Override timeout value to match default setting on Windows. config->timeout = base::TimeDelta::FromSeconds(kDnsTimeoutSeconds); return result; @@ -172,7 +262,18 @@ class DnsConfigServicePosix::ConfigReader : public SerialWorker { virtual void DoWork() OVERRIDE { base::TimeTicks start_time = base::TimeTicks::Now(); ConfigParsePosixResult result = ReadDnsConfig(&dns_config_); - success_ = (result == CONFIG_PARSE_POSIX_OK); + 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_); @@ -358,12 +459,16 @@ ConfigParsePosixResult ConvertResStateToDnsConfig(const struct __res_state& res, // 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) + 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) + 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; diff --git a/chromium/net/dns/dns_config_service_posix.h b/chromium/net/dns/dns_config_service_posix.h index 95a4377d932..be19ab90b08 100644 --- a/chromium/net/dns/dns_config_service_posix.h +++ b/chromium/net/dns/dns_config_service_posix.h @@ -53,6 +53,7 @@ enum ConfigParsePosixResult { CONFIG_PARSE_POSIX_NO_NAMESERVERS, CONFIG_PARSE_POSIX_MISSING_OPTIONS, CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS, + CONFIG_PARSE_POSIX_NO_DNSINFO, CONFIG_PARSE_POSIX_MAX // Bounding values for enumeration. }; diff --git a/chromium/net/dns/dns_config_service_win.cc b/chromium/net/dns/dns_config_service_win.cc index 6d12155d8cc..fe97b74f73e 100644 --- a/chromium/net/dns/dns_config_service_win.cc +++ b/chromium/net/dns/dns_config_service_win.cc @@ -43,8 +43,19 @@ namespace { // Interval between retries to parse config. Used only until parsing succeeds. const int kRetryIntervalSeconds = 5; +// Registry key paths. +const wchar_t* const kTcpipPath = + L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"; +const wchar_t* const kTcpip6Path = + L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters"; +const wchar_t* const kDnscachePath = + L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters"; +const wchar_t* const kPolicyPath = + L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient"; const wchar_t* const kPrimaryDnsSuffixPath = L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient"; +const wchar_t* const kNRPTPath = + L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig"; enum HostsParseWinResult { HOSTS_PARSE_WIN_OK = 0, @@ -198,6 +209,10 @@ ConfigParseWinResult ReadSystemSettings(DnsSystemSettings* settings) { &settings->primary_dns_suffix)) { return CONFIG_PARSE_WIN_READ_PRIMARY_SUFFIX; } + + base::win::RegistryKeyIterator nrpt_rules(HKEY_LOCAL_MACHINE, kNRPTPath); + settings->have_name_resolution_policy = (nrpt_rules.SubkeyCount() > 0); + return CONFIG_PARSE_WIN_OK; } @@ -330,8 +345,7 @@ bool IsStatelessDiscoveryAddress(const IPAddressNumber& address) { address.begin()) && (address.back() < 4); } -} // namespace - +// Returns the path to the HOSTS file. base::FilePath GetHostsPath() { TCHAR buffer[MAX_PATH]; UINT rc = GetSystemDirectory(buffer, MAX_PATH); @@ -340,6 +354,92 @@ base::FilePath GetHostsPath() { FILE_PATH_LITERAL("drivers\\etc\\hosts")); } +void ConfigureSuffixSearch(const DnsSystemSettings& settings, + DnsConfig* config) { + // SearchList takes precedence, so check it first. + if (settings.policy_search_list.set) { + std::vector<std::string> search; + if (ParseSearchList(settings.policy_search_list.value, &search)) { + config->search.swap(search); + return; + } + // 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; + } + } + + // In absence of explicit search list, suffix search is: + // [primary suffix, connection-specific suffix, devolution of primary suffix]. + // Primary suffix can be set by policy (primary_dns_suffix) or + // user setting (tcpip_domain). + // + // The policy (primary_dns_suffix) can be edited via Group Policy Editor + // (gpedit.msc) at Local Computer Policy => Computer Configuration + // => Administrative Template => Network => DNS Client => Primary DNS Suffix. + // + // The user setting (tcpip_domain) can be configurred at Computer Name in + // System Settings + std::string primary_suffix; + if ((settings.primary_dns_suffix.set && + ParseDomainASCII(settings.primary_dns_suffix.value, &primary_suffix)) || + (settings.tcpip_domain.set && + ParseDomainASCII(settings.tcpip_domain.value, &primary_suffix))) { + // Primary suffix goes in front. + config->search.insert(config->search.begin(), primary_suffix); + } else { + return; // No primary suffix, hence no devolution. + } + + // Devolution is determined by precedence: policy > dnscache > tcpip. + // |enabled|: UseDomainNameDevolution and |level|: DomainNameDevolutionLevel + // are overridden independently. + DnsSystemSettings::DevolutionSetting devolution = settings.policy_devolution; + + if (!devolution.enabled.set) + devolution.enabled = settings.dnscache_devolution.enabled; + if (!devolution.enabled.set) + devolution.enabled = settings.tcpip_devolution.enabled; + if (devolution.enabled.set && (devolution.enabled.value == 0)) + return; // Devolution disabled. + + // By default devolution is enabled. + + if (!devolution.level.set) + devolution.level = settings.dnscache_devolution.level; + if (!devolution.level.set) + devolution.level = settings.tcpip_devolution.level; + + // After the recent update, Windows will try to determine a safe default + // value by comparing the forest root domain (FRD) to the primary suffix. + // See http://support.microsoft.com/kb/957579 for details. + // For now, if the level is not set, we disable devolution, assuming that + // we will fallback to the system getaddrinfo anyway. This might cause + // performance loss for resolutions which depend on the system default + // devolution setting. + // + // If the level is explicitly set below 2, devolution is disabled. + if (!devolution.level.set || devolution.level.value < 2) + return; // 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 + // discarded when the fully-qualified name is converted to DNS format. + + unsigned num_dots = std::count(primary_suffix.begin(), + primary_suffix.end(), '.'); + + for (size_t offset = 0; num_dots >= devolution.level.value; --num_dots) { + offset = primary_suffix.find('.', offset + 1); + config->search.push_back(primary_suffix.substr(offset + 1)); + } +} + +} // namespace + bool ParseSearchList(const base::string16& value, std::vector<std::string>* output) { DCHECK(output); @@ -429,87 +529,16 @@ ConfigParseWinResult ConvertSettingsToDnsConfig( (settings.append_to_multi_label_name.value != 0); } - // SearchList takes precedence, so check it first. - if (settings.policy_search_list.set) { - std::vector<std::string> search; - if (ParseSearchList(settings.policy_search_list.value, &search)) { - config->search.swap(search); - 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 CONFIG_PARSE_WIN_OK; - } + ConfigParseWinResult result = CONFIG_PARSE_WIN_OK; + if (settings.have_name_resolution_policy) { + config->unhandled_options = true; + // TODO(szym): only set this to true if NRPT has DirectAccess rules. + config->use_local_ipv6 = true; + result = CONFIG_PARSE_WIN_UNHANDLED_OPTIONS; } - // In absence of explicit search list, suffix search is: - // [primary suffix, connection-specific suffix, devolution of primary suffix]. - // Primary suffix can be set by policy (primary_dns_suffix) or - // user setting (tcpip_domain). - // - // The policy (primary_dns_suffix) can be edited via Group Policy Editor - // (gpedit.msc) at Local Computer Policy => Computer Configuration - // => Administrative Template => Network => DNS Client => Primary DNS Suffix. - // - // The user setting (tcpip_domain) can be configurred at Computer Name in - // System Settings - std::string primary_suffix; - if ((settings.primary_dns_suffix.set && - ParseDomainASCII(settings.primary_dns_suffix.value, &primary_suffix)) || - (settings.tcpip_domain.set && - ParseDomainASCII(settings.tcpip_domain.value, &primary_suffix))) { - // Primary suffix goes in front. - config->search.insert(config->search.begin(), primary_suffix); - } else { - return CONFIG_PARSE_WIN_OK; // No primary suffix, hence no devolution. - } - - // Devolution is determined by precedence: policy > dnscache > tcpip. - // |enabled|: UseDomainNameDevolution and |level|: DomainNameDevolutionLevel - // are overridden independently. - DnsSystemSettings::DevolutionSetting devolution = settings.policy_devolution; - - if (!devolution.enabled.set) - devolution.enabled = settings.dnscache_devolution.enabled; - if (!devolution.enabled.set) - devolution.enabled = settings.tcpip_devolution.enabled; - if (devolution.enabled.set && (devolution.enabled.value == 0)) - return CONFIG_PARSE_WIN_OK; // Devolution disabled. - - // By default devolution is enabled. - - if (!devolution.level.set) - devolution.level = settings.dnscache_devolution.level; - if (!devolution.level.set) - devolution.level = settings.tcpip_devolution.level; - - // After the recent update, Windows will try to determine a safe default - // value by comparing the forest root domain (FRD) to the primary suffix. - // See http://support.microsoft.com/kb/957579 for details. - // For now, if the level is not set, we disable devolution, assuming that - // we will fallback to the system getaddrinfo anyway. This might cause - // performance loss for resolutions which depend on the system default - // devolution setting. - // - // If the level is explicitly set below 2, devolution is disabled. - if (!devolution.level.set || devolution.level.value < 2) - 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 - // discarded when the fully-qualified name is converted to DNS format. - - unsigned num_dots = std::count(primary_suffix.begin(), - primary_suffix.end(), '.'); - - for (size_t offset = 0; num_dots >= devolution.level.value; --num_dots) { - offset = primary_suffix.find('.', offset + 1); - config->search.push_back(primary_suffix.substr(offset + 1)); - } - return CONFIG_PARSE_WIN_OK; + ConfigureSuffixSearch(settings, config); + return result; } // Watches registry and HOSTS file for changes. Must live on a thread which @@ -606,7 +635,8 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker { ConfigParseWinResult result = ReadSystemSettings(&settings); if (result == CONFIG_PARSE_WIN_OK) result = ConvertSettingsToDnsConfig(settings, &dns_config_); - success_ = (result == CONFIG_PARSE_WIN_OK); + success_ = (result == CONFIG_PARSE_WIN_OK || + result == CONFIG_PARSE_WIN_UNHANDLED_OPTIONS); UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParseWin", result, CONFIG_PARSE_WIN_MAX); UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_); diff --git a/chromium/net/dns/dns_config_service_win.h b/chromium/net/dns/dns_config_service_win.h index 06fc0d9663b..9503dc8593f 100644 --- a/chromium/net/dns/dns_config_service_win.h +++ b/chromium/net/dns/dns_config_service_win.h @@ -34,19 +34,6 @@ namespace net { namespace internal { -// Registry key paths. -const wchar_t* const kTcpipPath = - L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"; -const wchar_t* const kTcpip6Path = - L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters"; -const wchar_t* const kDnscachePath = - L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters"; -const wchar_t* const kPolicyPath = - L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient"; - -// Returns the path to the HOSTS file. -base::FilePath GetHostsPath(); - // Parses |value| as search list (comma-delimited list of domain names) from // a registry key and stores it in |out|. Returns true on success. Empty // entries (e.g., "chromium.org,,org") terminate the list. Non-ascii hostnames @@ -98,6 +85,10 @@ struct NET_EXPORT_PRIVATE DnsSystemSettings { // SOFTWARE\Policies\Microsoft\Windows NT\DNSClient\AppendToMultiLabelName RegDword append_to_multi_label_name; + + // True when the Name Resolution Policy Table (NRPT) has at least one rule: + // SOFTWARE\Policies\Microsoft\Windows NT\DNSClient\DnsPolicyConfig\Rule* + bool have_name_resolution_policy; }; enum ConfigParseWinResult { @@ -113,6 +104,7 @@ enum ConfigParseWinResult { CONFIG_PARSE_WIN_READ_PRIMARY_SUFFIX, CONFIG_PARSE_WIN_BAD_ADDRESS, CONFIG_PARSE_WIN_NO_NAMESERVERS, + CONFIG_PARSE_WIN_UNHANDLED_OPTIONS, CONFIG_PARSE_WIN_MAX // Bounding values for enumeration. }; diff --git a/chromium/net/dns/dns_config_service_win_unittest.cc b/chromium/net/dns/dns_config_service_win_unittest.cc index b28b8e9554a..3f3e4ed1e37 100644 --- a/chromium/net/dns/dns_config_service_win_unittest.cc +++ b/chromium/net/dns/dns_config_service_win_unittest.cc @@ -4,6 +4,7 @@ #include "net/dns/dns_config_service_win.h" +#include "base/basictypes.h" #include "base/logging.h" #include "base/win/windows_version.h" #include "net/dns/dns_protocol.h" @@ -420,10 +421,46 @@ TEST(DnsConfigServiceWinTest, AppendToMultiLabelName) { DnsConfig config; EXPECT_EQ(internal::CONFIG_PARSE_WIN_OK, internal::ConvertSettingsToDnsConfig(settings, &config)); - EXPECT_EQ(config.append_to_multi_label_name, t.expected_output); + EXPECT_EQ(t.expected_output, config.append_to_multi_label_name); } } +// Setting have_name_resolution_policy_table should set unhandled_options. +TEST(DnsConfigServiceWinTest, HaveNRPT) { + AdapterInfo infos[2] = { + { IF_TYPE_USB, IfOperStatusUp, L"connection.suffix", { "1.0.0.1" } }, + { 0 }, + }; + + const struct TestCase { + bool have_nrpt; + bool unhandled_options; + internal::ConfigParseWinResult result; + } cases[] = { + { false, false, internal::CONFIG_PARSE_WIN_OK }, + { true, true, internal::CONFIG_PARSE_WIN_UNHANDLED_OPTIONS }, + }; + + for (size_t i = 0; i < arraysize(cases); ++i) { + const TestCase& t = cases[i]; + internal::DnsSystemSettings settings = { + CreateAdapterAddresses(infos), + { false }, { false }, { false }, { false }, + { { false }, { false } }, + { { false }, { false } }, + { { false }, { false } }, + { false }, + t.have_nrpt, + }; + DnsConfig config; + EXPECT_EQ(t.result, + internal::ConvertSettingsToDnsConfig(settings, &config)); + EXPECT_EQ(t.unhandled_options, config.unhandled_options); + EXPECT_EQ(t.have_nrpt, config.use_local_ipv6); + } +} + + } // namespace } // namespace net diff --git a/chromium/net/dns/dns_hosts.cc b/chromium/net/dns/dns_hosts.cc index 852d35c8bb4..3edea2a7abc 100644 --- a/chromium/net/dns/dns_hosts.cc +++ b/chromium/net/dns/dns_hosts.cc @@ -158,7 +158,7 @@ bool ParseHostsFile(const base::FilePath& path, DnsHosts* dns_hosts) { return false; std::string contents; - if (!file_util::ReadFileToString(path, &contents)) + if (!base::ReadFileToString(path, &contents)) return false; ParseHosts(contents, dns_hosts); diff --git a/chromium/net/dns/dns_session_unittest.cc b/chromium/net/dns/dns_session_unittest.cc index 46627069f66..ed726f23234 100644 --- a/chromium/net/dns/dns_session_unittest.cc +++ b/chromium/net/dns/dns_session_unittest.cc @@ -14,6 +14,8 @@ #include "net/dns/dns_protocol.h" #include "net/dns/dns_socket_pool.h" #include "net/socket/socket_test_util.h" +#include "net/socket/ssl_client_socket.h" +#include "net/socket/stream_socket.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -24,26 +26,26 @@ class TestClientSocketFactory : public ClientSocketFactory { public: virtual ~TestClientSocketFactory(); - virtual DatagramClientSocket* CreateDatagramClientSocket( + virtual scoped_ptr<DatagramClientSocket> CreateDatagramClientSocket( DatagramSocket::BindType bind_type, const RandIntCallback& rand_int_cb, net::NetLog* net_log, const net::NetLog::Source& source) OVERRIDE; - virtual StreamSocket* CreateTransportClientSocket( + virtual scoped_ptr<StreamSocket> CreateTransportClientSocket( const AddressList& addresses, NetLog*, const NetLog::Source&) OVERRIDE { NOTIMPLEMENTED(); - return NULL; + return scoped_ptr<StreamSocket>(); } - virtual SSLClientSocket* CreateSSLClientSocket( - ClientSocketHandle* transport_socket, + virtual scoped_ptr<SSLClientSocket> CreateSSLClientSocket( + scoped_ptr<ClientSocketHandle> transport_socket, const HostPortPair& host_and_port, const SSLConfig& ssl_config, const SSLClientSocketContext& context) OVERRIDE { NOTIMPLEMENTED(); - return NULL; + return scoped_ptr<SSLClientSocket>(); } virtual void ClearSSLSessionCache() OVERRIDE { @@ -179,7 +181,8 @@ bool DnsSessionTest::ExpectEvent(const PoolEvent& expected) { return true; } -DatagramClientSocket* TestClientSocketFactory::CreateDatagramClientSocket( +scoped_ptr<DatagramClientSocket> +TestClientSocketFactory::CreateDatagramClientSocket( DatagramSocket::BindType bind_type, const RandIntCallback& rand_int_cb, net::NetLog* net_log, @@ -188,9 +191,10 @@ DatagramClientSocket* TestClientSocketFactory::CreateDatagramClientSocket( // simplest SocketDataProvider with no data supplied. SocketDataProvider* data_provider = new StaticSocketDataProvider(); data_providers_.push_back(data_provider); - MockUDPClientSocket* socket = new MockUDPClientSocket(data_provider, net_log); - data_provider->set_socket(socket); - return socket; + scoped_ptr<MockUDPClientSocket> socket( + new MockUDPClientSocket(data_provider, net_log)); + data_provider->set_socket(socket.get()); + return socket.PassAs<DatagramClientSocket>(); } TestClientSocketFactory::~TestClientSocketFactory() { diff --git a/chromium/net/dns/dns_socket_pool.cc b/chromium/net/dns/dns_socket_pool.cc index 64570fca8fc..7a7ecd6ee8f 100644 --- a/chromium/net/dns/dns_socket_pool.cc +++ b/chromium/net/dns/dns_socket_pool.cc @@ -76,8 +76,8 @@ scoped_ptr<DatagramClientSocket> DnsSocketPool::CreateConnectedSocket( scoped_ptr<DatagramClientSocket> socket; NetLog::Source no_source; - socket.reset(socket_factory_->CreateDatagramClientSocket( - kBindType, base::Bind(&base::RandInt), net_log_, no_source)); + socket = socket_factory_->CreateDatagramClientSocket( + kBindType, base::Bind(&base::RandInt), net_log_, no_source); if (socket.get()) { int rv = socket->Connect((*nameservers_)[server_index]); diff --git a/chromium/net/dns/dns_test_util.cc b/chromium/net/dns/dns_test_util.cc index 37bf855dd10..63014213faa 100644 --- a/chromium/net/dns/dns_test_util.cc +++ b/chromium/net/dns/dns_test_util.cc @@ -15,9 +15,6 @@ #include "net/base/io_buffer.h" #include "net/base/net_errors.h" #include "net/dns/address_sorter.h" -#include "net/dns/dns_client.h" -#include "net/dns/dns_config_service.h" -#include "net/dns/dns_protocol.h" #include "net/dns/dns_query.h" #include "net/dns/dns_response.h" #include "net/dns/dns_transaction.h" @@ -26,6 +23,16 @@ namespace net { namespace { +class MockAddressSorter : public AddressSorter { + public: + virtual ~MockAddressSorter() {} + virtual void Sort(const AddressList& list, + const CallbackType& callback) const OVERRIDE { + // Do nothing. + callback.Run(true, list); + } +}; + // A DnsTransaction which uses MockDnsClientRuleList to determine the response. class MockTransaction : public DnsTransaction, public base::SupportsWeakPtr<MockTransaction> { @@ -38,7 +45,8 @@ class MockTransaction : public DnsTransaction, hostname_(hostname), qtype_(qtype), callback_(callback), - started_(false) { + started_(false), + delayed_(false) { // Find the relevant rule which matches |qtype| and prefix of |hostname|. for (size_t i = 0; i < rules.size(); ++i) { const std::string& prefix = rules[i].prefix; @@ -46,6 +54,7 @@ class MockTransaction : public DnsTransaction, (hostname.size() >= prefix.size()) && (hostname.compare(0, prefix.size(), prefix) == 0)) { result_ = rules[i].result; + delayed_ = rules[i].delay; break; } } @@ -62,11 +71,21 @@ class MockTransaction : public DnsTransaction, virtual void Start() OVERRIDE { EXPECT_FALSE(started_); started_ = true; + if (delayed_) + return; // Using WeakPtr to cleanly cancel when transaction is destroyed. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&MockTransaction::Finish, AsWeakPtr())); } + void FinishDelayedTransaction() { + EXPECT_TRUE(delayed_); + delayed_ = false; + Finish(); + } + + bool delayed() const { return delayed_; } + private: void Finish() { switch (result_) { @@ -136,14 +155,17 @@ class MockTransaction : public DnsTransaction, const uint16 qtype_; DnsTransactionFactory::CallbackType callback_; bool started_; + bool delayed_; }; +} // namespace // A DnsTransactionFactory which creates MockTransaction. class MockTransactionFactory : public DnsTransactionFactory { public: explicit MockTransactionFactory(const MockDnsClientRuleList& rules) : rules_(rules) {} + virtual ~MockTransactionFactory() {} virtual scoped_ptr<DnsTransaction> CreateTransaction( @@ -151,60 +173,57 @@ class MockTransactionFactory : public DnsTransactionFactory { uint16 qtype, const DnsTransactionFactory::CallbackType& callback, const BoundNetLog&) OVERRIDE { - return scoped_ptr<DnsTransaction>( - new MockTransaction(rules_, hostname, qtype, callback)); + MockTransaction* transaction = + new MockTransaction(rules_, hostname, qtype, callback); + if (transaction->delayed()) + delayed_transactions_.push_back(transaction->AsWeakPtr()); + return scoped_ptr<DnsTransaction>(transaction); + } + + void CompleteDelayedTransactions() { + DelayedTransactionList old_delayed_transactions; + old_delayed_transactions.swap(delayed_transactions_); + for (DelayedTransactionList::iterator it = old_delayed_transactions.begin(); + it != old_delayed_transactions.end(); ++it) { + if (it->get()) + (*it)->FinishDelayedTransaction(); + } } private: - MockDnsClientRuleList rules_; -}; + typedef std::vector<base::WeakPtr<MockTransaction> > DelayedTransactionList; -class MockAddressSorter : public AddressSorter { - public: - virtual ~MockAddressSorter() {} - virtual void Sort(const AddressList& list, - const CallbackType& callback) const OVERRIDE { - // Do nothing. - callback.Run(true, list); - } + MockDnsClientRuleList rules_; + DelayedTransactionList delayed_transactions_; }; -// MockDnsClient provides MockTransactionFactory. -class MockDnsClient : public DnsClient { - public: - MockDnsClient(const DnsConfig& config, - const MockDnsClientRuleList& rules) - : config_(config), factory_(rules) {} - virtual ~MockDnsClient() {} +MockDnsClient::MockDnsClient(const DnsConfig& config, + const MockDnsClientRuleList& rules) + : config_(config), + factory_(new MockTransactionFactory(rules)), + address_sorter_(new MockAddressSorter()) { +} - virtual void SetConfig(const DnsConfig& config) OVERRIDE { - config_ = config; - } +MockDnsClient::~MockDnsClient() {} - virtual const DnsConfig* GetConfig() const OVERRIDE { - return config_.IsValid() ? &config_ : NULL; - } - - virtual DnsTransactionFactory* GetTransactionFactory() OVERRIDE { - return config_.IsValid() ? &factory_ : NULL; - } +void MockDnsClient::SetConfig(const DnsConfig& config) { + config_ = config; +} - virtual AddressSorter* GetAddressSorter() OVERRIDE { - return &address_sorter_; - } +const DnsConfig* MockDnsClient::GetConfig() const { + return config_.IsValid() ? &config_ : NULL; +} - private: - DnsConfig config_; - MockTransactionFactory factory_; - MockAddressSorter address_sorter_; -}; +DnsTransactionFactory* MockDnsClient::GetTransactionFactory() { + return config_.IsValid() ? factory_.get() : NULL; +} -} // namespace +AddressSorter* MockDnsClient::GetAddressSorter() { + return address_sorter_.get(); +} -// static -scoped_ptr<DnsClient> CreateMockDnsClient(const DnsConfig& config, - const MockDnsClientRuleList& rules) { - return scoped_ptr<DnsClient>(new MockDnsClient(config, rules)); +void MockDnsClient::CompleteDelayedTransactions() { + factory_->CompleteDelayedTransactions(); } } // namespace net diff --git a/chromium/net/dns/dns_test_util.h b/chromium/net/dns/dns_test_util.h index d447b299c86..d0b8e81b7ed 100644 --- a/chromium/net/dns/dns_test_util.h +++ b/chromium/net/dns/dns_test_util.h @@ -10,6 +10,7 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" +#include "net/dns/dns_client.h" #include "net/dns/dns_config_service.h" #include "net/dns/dns_protocol.h" @@ -174,7 +175,9 @@ static const int kT3TTL = 0x00000015; // +2 for the CNAME records, +1 for TXT record. static const unsigned kT3RecordCount = arraysize(kT3IpAddresses) + 3; +class AddressSorter; class DnsClient; +class MockTransactionFactory; struct MockDnsClientRule { enum Result { @@ -184,21 +187,43 @@ struct MockDnsClientRule { OK, // Return a response with loopback address. }; + // If |delay| is true, matching transactions will be delayed until triggered + // by the consumer. MockDnsClientRule(const std::string& prefix_arg, uint16 qtype_arg, - Result result_arg) - : result(result_arg), prefix(prefix_arg), qtype(qtype_arg) { } + Result result_arg, + bool delay) + : result(result_arg), prefix(prefix_arg), qtype(qtype_arg), + delay(delay) {} Result result; std::string prefix; uint16 qtype; + bool delay; }; typedef std::vector<MockDnsClientRule> MockDnsClientRuleList; -// Creates mock DnsClient for testing HostResolverImpl. -scoped_ptr<DnsClient> CreateMockDnsClient(const DnsConfig& config, - const MockDnsClientRuleList& rules); +// MockDnsClient provides MockTransactionFactory. +class MockDnsClient : public DnsClient { + public: + MockDnsClient(const DnsConfig& config, const MockDnsClientRuleList& rules); + virtual ~MockDnsClient(); + + // DnsClient interface: + virtual void SetConfig(const DnsConfig& config) OVERRIDE; + virtual const DnsConfig* GetConfig() const OVERRIDE; + virtual DnsTransactionFactory* GetTransactionFactory() OVERRIDE; + virtual AddressSorter* GetAddressSorter() OVERRIDE; + + // Completes all DnsTransactions that were delayed by a rule. + void CompleteDelayedTransactions(); + + private: + DnsConfig config_; + scoped_ptr<MockTransactionFactory> factory_; + scoped_ptr<AddressSorter> address_sorter_; +}; } // namespace net diff --git a/chromium/net/dns/dns_transaction_unittest.cc b/chromium/net/dns/dns_transaction_unittest.cc index f9667eed5f4..7040e44be16 100644 --- a/chromium/net/dns/dns_transaction_unittest.cc +++ b/chromium/net/dns/dns_transaction_unittest.cc @@ -180,21 +180,21 @@ class TestSocketFactory : public MockClientSocketFactory { TestSocketFactory() : fail_next_socket_(false) {} virtual ~TestSocketFactory() {} - virtual DatagramClientSocket* CreateDatagramClientSocket( + virtual scoped_ptr<DatagramClientSocket> CreateDatagramClientSocket( DatagramSocket::BindType bind_type, const RandIntCallback& rand_int_cb, net::NetLog* net_log, const net::NetLog::Source& source) OVERRIDE { if (fail_next_socket_) { fail_next_socket_ = false; - return new FailingUDPClientSocket(&empty_data_, net_log); + return scoped_ptr<DatagramClientSocket>( + new FailingUDPClientSocket(&empty_data_, net_log)); } SocketDataProvider* data_provider = mock_data().GetNext(); - TestUDPClientSocket* socket = new TestUDPClientSocket(this, - data_provider, - net_log); - data_provider->set_socket(socket); - return socket; + scoped_ptr<TestUDPClientSocket> socket( + new TestUDPClientSocket(this, data_provider, net_log)); + data_provider->set_socket(socket.get()); + return socket.PassAs<DatagramClientSocket>(); } void OnConnect(const IPEndPoint& endpoint) { diff --git a/chromium/net/dns/host_resolver.cc b/chromium/net/dns/host_resolver.cc index d74be91beb0..9b1e3314acd 100644 --- a/chromium/net/dns/host_resolver.cc +++ b/chromium/net/dns/host_resolver.cc @@ -98,9 +98,7 @@ HostResolver::RequestInfo::RequestInfo(const HostPortPair& host_port_pair) address_family_(ADDRESS_FAMILY_UNSPECIFIED), host_resolver_flags_(0), allow_cached_response_(true), - is_speculative_(false), - priority_(MEDIUM) { -} + is_speculative_(false) {} HostResolver::~HostResolver() { } diff --git a/chromium/net/dns/host_resolver.h b/chromium/net/dns/host_resolver.h index 558a1ddc4b2..2964fe600df 100644 --- a/chromium/net/dns/host_resolver.h +++ b/chromium/net/dns/host_resolver.h @@ -53,8 +53,8 @@ class NET_EXPORT HostResolver { bool enable_caching; }; - // The parameters for doing a Resolve(). A hostname and port are required, - // the rest are optional (and have reasonable defaults). + // The parameters for doing a Resolve(). A hostname and port are + // required; the rest are optional (and have reasonable defaults). class NET_EXPORT RequestInfo { public: explicit RequestInfo(const HostPortPair& host_port_pair); @@ -85,9 +85,6 @@ class NET_EXPORT HostResolver { bool is_speculative() const { return is_speculative_; } void set_is_speculative(bool b) { is_speculative_ = b; } - RequestPriority priority() const { return priority_; } - void set_priority(RequestPriority priority) { priority_ = priority; } - private: // The hostname to resolve, and the port to use in resulting sockaddrs. HostPortPair host_port_pair_; @@ -103,9 +100,6 @@ class NET_EXPORT HostResolver { // Whether this request was started by the DNS prefetcher. bool is_speculative_; - - // The priority for the request. - RequestPriority priority_; }; // Opaque type used to cancel a request. @@ -144,6 +138,7 @@ class NET_EXPORT HostResolver { // // Profiling information for the request is saved to |net_log| if non-NULL. virtual int Resolve(const RequestInfo& info, + RequestPriority priority, AddressList* addresses, const CompletionCallback& callback, RequestHandle* out_req, diff --git a/chromium/net/dns/host_resolver_impl.cc b/chromium/net/dns/host_resolver_impl.cc index e656f03955f..e4098d003be 100644 --- a/chromium/net/dns/host_resolver_impl.cc +++ b/chromium/net/dns/host_resolver_impl.cc @@ -69,10 +69,6 @@ const unsigned kNegativeCacheEntryTTLSeconds = 0; // Minimum TTL for successful resolutions with DnsTask. const unsigned kMinimumTTLSeconds = kCacheEntryTTLSeconds; -// Number of consecutive failures of DnsTask (with successful fallback) before -// the DnsClient is disabled until the next DNS change. -const unsigned kMaximumDnsFailures = 16; - // We use a separate histogram name for each platform to facilitate the // display of error codes by their symbolic name (since each platform has // different mappings). @@ -343,7 +339,6 @@ base::Value* NetLogRequestInfoCallback(const NetLog::Source& source, static_cast<int>(info->address_family())); dict->SetBoolean("allow_cached_response", info->allow_cached_response()); dict->SetBoolean("is_speculative", info->is_speculative()); - dict->SetInteger("priority", info->priority()); return dict; } @@ -457,6 +452,8 @@ class PriorityTracker { //----------------------------------------------------------------------------- +const unsigned HostResolverImpl::kMaximumDnsFailures = 16; + // Holds the data for a request that could not be completed synchronously. // It is owned by a Job. Canceled Requests are only marked as canceled rather // than removed from the Job's |requests_| list. @@ -465,16 +462,17 @@ class HostResolverImpl::Request { Request(const BoundNetLog& source_net_log, const BoundNetLog& request_net_log, const RequestInfo& info, + RequestPriority priority, const CompletionCallback& callback, AddressList* addresses) : source_net_log_(source_net_log), request_net_log_(request_net_log), info_(info), + priority_(priority), job_(NULL), callback_(callback), addresses_(addresses), - request_time_(base::TimeTicks::Now()) { - } + request_time_(base::TimeTicks::Now()) {} // Mark the request as canceled. void MarkAsCanceled() { @@ -521,16 +519,19 @@ class HostResolverImpl::Request { return info_; } - base::TimeTicks request_time() const { - return request_time_; - } + RequestPriority priority() const { return priority_; } + + base::TimeTicks request_time() const { return request_time_; } private: BoundNetLog source_net_log_; BoundNetLog request_net_log_; // The request info that started the request. - RequestInfo info_; + const RequestInfo info_; + + // TODO(akalin): Support reprioritization. + const RequestPriority priority_; // The resolve job that this request is dependent on. Job* job_; @@ -967,54 +968,98 @@ class HostResolverImpl::LoopbackProbeJob { // TODO(szym): This could be moved to separate source file as well. class HostResolverImpl::DnsTask : public base::SupportsWeakPtr<DnsTask> { public: - typedef base::Callback<void(int net_error, - const AddressList& addr_list, - base::TimeDelta ttl)> Callback; + class Delegate { + public: + virtual void OnDnsTaskComplete(base::TimeTicks start_time, + int net_error, + const AddressList& addr_list, + base::TimeDelta ttl) = 0; + + // Called when the first of two jobs succeeds. If the first completed + // transaction fails, this is not called. Also not called when the DnsTask + // only needs to run one transaction. + virtual void OnFirstDnsTransactionComplete() = 0; + + protected: + Delegate() {} + virtual ~Delegate() {} + }; DnsTask(DnsClient* client, const Key& key, - const Callback& callback, + Delegate* delegate, const BoundNetLog& job_net_log) : client_(client), - family_(key.address_family), - callback_(callback), - net_log_(job_net_log) { + key_(key), + delegate_(delegate), + net_log_(job_net_log), + num_completed_transactions_(0), + task_start_time_(base::TimeTicks::Now()) { DCHECK(client); - DCHECK(!callback.is_null()); - - // If unspecified, do IPv4 first, because suffix search will be faster. - uint16 qtype = (family_ == ADDRESS_FAMILY_IPV6) ? - dns_protocol::kTypeAAAA : - dns_protocol::kTypeA; - transaction_ = client_->GetTransactionFactory()->CreateTransaction( - key.hostname, - qtype, - base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this), - true /* first_query */, base::TimeTicks::Now()), - net_log_); + DCHECK(delegate_); } - void Start() { + bool needs_two_transactions() const { + return key_.address_family == ADDRESS_FAMILY_UNSPECIFIED; + } + + bool needs_another_transaction() const { + return needs_two_transactions() && !transaction_aaaa_; + } + + void StartFirstTransaction() { + DCHECK_EQ(0u, num_completed_transactions_); net_log_.BeginEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK); - transaction_->Start(); + if (key_.address_family == ADDRESS_FAMILY_IPV6) { + StartAAAA(); + } else { + StartA(); + } + } + + void StartSecondTransaction() { + DCHECK(needs_two_transactions()); + StartAAAA(); } private: - void OnTransactionComplete(bool first_query, - const base::TimeTicks& start_time, + void StartA() { + DCHECK(!transaction_a_); + DCHECK_NE(ADDRESS_FAMILY_IPV6, key_.address_family); + transaction_a_ = CreateTransaction(ADDRESS_FAMILY_IPV4); + transaction_a_->Start(); + } + + void StartAAAA() { + DCHECK(!transaction_aaaa_); + DCHECK_NE(ADDRESS_FAMILY_IPV4, key_.address_family); + transaction_aaaa_ = CreateTransaction(ADDRESS_FAMILY_IPV6); + transaction_aaaa_->Start(); + } + + scoped_ptr<DnsTransaction> CreateTransaction(AddressFamily family) { + DCHECK_NE(ADDRESS_FAMILY_UNSPECIFIED, family); + return client_->GetTransactionFactory()->CreateTransaction( + key_.hostname, + family == ADDRESS_FAMILY_IPV6 ? dns_protocol::kTypeAAAA : + dns_protocol::kTypeA, + base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this), + base::TimeTicks::Now()), + net_log_); + } + + void OnTransactionComplete(const base::TimeTicks& start_time, DnsTransaction* transaction, int net_error, const DnsResponse* response) { DCHECK(transaction); base::TimeDelta duration = base::TimeTicks::Now() - start_time; - // Run |callback_| last since the owning Job will then delete this DnsTask. if (net_error != OK) { DNS_HISTOGRAM("AsyncDNS.TransactionFailure", duration); OnFailure(net_error, DnsResponse::DNS_PARSE_OK); return; } - CHECK(response); DNS_HISTOGRAM("AsyncDNS.TransactionSuccess", duration); switch (transaction->GetType()) { case dns_protocol::kTypeA: @@ -1024,6 +1069,7 @@ class HostResolverImpl::DnsTask : public base::SupportsWeakPtr<DnsTask> { DNS_HISTOGRAM("AsyncDNS.TransactionSuccess_AAAA", duration); break; } + AddressList addr_list; base::TimeDelta ttl; DnsResponse::Result result = response->ParseToAddressList(&addr_list, &ttl); @@ -1036,58 +1082,53 @@ class HostResolverImpl::DnsTask : public base::SupportsWeakPtr<DnsTask> { return; } - bool needs_sort = false; - if (first_query) { - DCHECK(client_->GetConfig()) << - "Transaction should have been aborted when config changed!"; - if (family_ == ADDRESS_FAMILY_IPV6) { - needs_sort = (addr_list.size() > 1); - } else if (family_ == ADDRESS_FAMILY_UNSPECIFIED) { - first_addr_list_ = addr_list; - first_ttl_ = ttl; - // Use fully-qualified domain name to avoid search. - transaction_ = client_->GetTransactionFactory()->CreateTransaction( - response->GetDottedName() + ".", - dns_protocol::kTypeAAAA, - base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this), - false /* first_query */, base::TimeTicks::Now()), - net_log_); - transaction_->Start(); - return; - } + ++num_completed_transactions_; + if (num_completed_transactions_ == 1) { + ttl_ = ttl; } else { - DCHECK_EQ(ADDRESS_FAMILY_UNSPECIFIED, family_); - bool has_ipv6_addresses = !addr_list.empty(); - if (!first_addr_list_.empty()) { - ttl = std::min(ttl, first_ttl_); - // Place IPv4 addresses after IPv6. - addr_list.insert(addr_list.end(), first_addr_list_.begin(), - first_addr_list_.end()); - } - needs_sort = (has_ipv6_addresses && addr_list.size() > 1); + ttl_ = std::min(ttl_, ttl); } - if (addr_list.empty()) { + if (transaction->GetType() == dns_protocol::kTypeA) { + DCHECK_EQ(transaction_a_.get(), transaction); + // Place IPv4 addresses after IPv6. + addr_list_.insert(addr_list_.end(), addr_list.begin(), addr_list.end()); + } else { + DCHECK_EQ(transaction_aaaa_.get(), transaction); + // Place IPv6 addresses before IPv4. + addr_list_.insert(addr_list_.begin(), addr_list.begin(), addr_list.end()); + } + + if (needs_two_transactions() && num_completed_transactions_ == 1) { + // No need to repeat the suffix search. + key_.hostname = transaction->GetHostname(); + delegate_->OnFirstDnsTransactionComplete(); + return; + } + + if (addr_list_.empty()) { // TODO(szym): Don't fallback to ProcTask in this case. OnFailure(ERR_NAME_NOT_RESOLVED, DnsResponse::DNS_PARSE_OK); return; } - if (needs_sort) { - // Sort could complete synchronously. + // If there are multiple addresses, and at least one is IPv6, need to sort + // them. Note that IPv6 addresses are always put before IPv4 ones, so it's + // sufficient to just check the family of the first address. + if (addr_list_.size() > 1 && + addr_list_[0].GetFamily() == ADDRESS_FAMILY_IPV6) { + // Sort addresses if needed. Sort could complete synchronously. client_->GetAddressSorter()->Sort( - addr_list, + addr_list_, base::Bind(&DnsTask::OnSortComplete, AsWeakPtr(), - base::TimeTicks::Now(), - ttl)); + base::TimeTicks::Now())); } else { - OnSuccess(addr_list, ttl); + OnSuccess(addr_list_); } } void OnSortComplete(base::TimeTicks start_time, - base::TimeDelta ttl, bool success, const AddressList& addr_list) { if (!success) { @@ -1107,7 +1148,7 @@ class HostResolverImpl::DnsTask : public base::SupportsWeakPtr<DnsTask> { return; } - OnSuccess(addr_list, ttl); + OnSuccess(addr_list); } void OnFailure(int net_error, DnsResponse::Result result) { @@ -1115,26 +1156,34 @@ class HostResolverImpl::DnsTask : public base::SupportsWeakPtr<DnsTask> { net_log_.EndEvent( NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK, base::Bind(&NetLogDnsTaskFailedCallback, net_error, result)); - callback_.Run(net_error, AddressList(), base::TimeDelta()); + delegate_->OnDnsTaskComplete(task_start_time_, net_error, AddressList(), + base::TimeDelta()); } - void OnSuccess(const AddressList& addr_list, base::TimeDelta ttl) { + void OnSuccess(const AddressList& addr_list) { net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK, addr_list.CreateNetLogCallback()); - callback_.Run(OK, addr_list, ttl); + delegate_->OnDnsTaskComplete(task_start_time_, OK, addr_list, ttl_); } DnsClient* client_; - AddressFamily family_; + Key key_; + // The listener to the results of this DnsTask. - Callback callback_; + Delegate* delegate_; const BoundNetLog net_log_; - scoped_ptr<DnsTransaction> transaction_; + scoped_ptr<DnsTransaction> transaction_a_; + scoped_ptr<DnsTransaction> transaction_aaaa_; + + unsigned num_completed_transactions_; - // Results from the first transaction. Used only if |family_| is unspecified. - AddressList first_addr_list_; - base::TimeDelta first_ttl_; + // These are updated as each transaction completes. + base::TimeDelta ttl_; + // IPv6 addresses must appear first in the list. + AddressList addr_list_; + + base::TimeTicks task_start_time_; DISALLOW_COPY_AND_ASSIGN(DnsTask); }; @@ -1142,7 +1191,8 @@ class HostResolverImpl::DnsTask : public base::SupportsWeakPtr<DnsTask> { //----------------------------------------------------------------------------- // Aggregates all Requests for the same Key. Dispatched via PriorityDispatch. -class HostResolverImpl::Job : public PrioritizedDispatcher::Job { +class HostResolverImpl::Job : public PrioritizedDispatcher::Job, + public HostResolverImpl::DnsTask::Delegate { public: // Creates new job for |key| where |request_net_log| is bound to the // request that spawned it. @@ -1155,6 +1205,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { priority_tracker_(priority), had_non_speculative_request_(false), had_dns_config_(false), + num_occupied_job_slots_(0), dns_task_error_(OK), creation_time_(base::TimeTicks::Now()), priority_change_time_(creation_time_), @@ -1178,7 +1229,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { proc_task_ = NULL; } // Clean up now for nice NetLog. - dns_task_.reset(NULL); + KillDnsTask(); net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, ERR_ABORTED); } else if (is_queued()) { @@ -1201,16 +1252,30 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { } } - // Add this job to the dispatcher. - void Schedule() { - handle_ = resolver_->dispatcher_.Add(this, priority()); + // Add this job to the dispatcher. If "at_head" is true, adds at the front + // of the queue. + void Schedule(bool at_head) { + DCHECK(!is_queued()); + PrioritizedDispatcher::Handle handle; + if (!at_head) { + handle = resolver_->dispatcher_.Add(this, priority()); + } else { + handle = resolver_->dispatcher_.AddAtHead(this, priority()); + } + // The dispatcher could have started |this| in the above call to Add, which + // could have called Schedule again. In that case |handle| will be null, + // but |handle_| may have been set by the other nested call to Schedule. + if (!handle.is_null()) { + DCHECK(handle_.is_null()); + handle_ = handle; + } } void AddRequest(scoped_ptr<Request> req) { DCHECK_EQ(key_.hostname, req->info().hostname()); req->set_job(this); - priority_tracker_.Add(req->info().priority()); + priority_tracker_.Add(req->priority()); req->request_net_log().AddEvent( NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_ATTACH, @@ -1245,12 +1310,11 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { LogCancelRequest(req->source_net_log(), req->request_net_log(), req->info()); - priority_tracker_.Remove(req->info().priority()); - net_log_.AddEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_DETACH, - base::Bind(&NetLogJobAttachCallback, - req->request_net_log().source(), - priority())); + priority_tracker_.Remove(req->priority()); + net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_DETACH, + base::Bind(&NetLogJobAttachCallback, + req->request_net_log().source(), + priority())); if (num_active_requests() > 0) { UpdatePriority(); @@ -1272,7 +1336,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { // If DnsTask present, abort it and fall back to ProcTask. void AbortDnsTask() { if (dns_task_) { - dns_task_.reset(); + KillDnsTask(); dns_task_error_ = OK; StartProcTask(); } @@ -1321,6 +1385,29 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { } private: + void KillDnsTask() { + if (dns_task_) { + ReduceToOneJobSlot(); + dns_task_.reset(); + } + } + + // Reduce the number of job slots occupied and queued in the dispatcher + // to one. If the second Job slot is queued in the dispatcher, cancels the + // queued job. Otherwise, the second Job has been started by the + // PrioritizedDispatcher, so signals it is complete. + void ReduceToOneJobSlot() { + DCHECK_GE(num_occupied_job_slots_, 1u); + if (is_queued()) { + resolver_->dispatcher_.Cancel(handle_); + handle_.Reset(); + } else if (num_occupied_job_slots_ > 1) { + resolver_->dispatcher_.OnJobFinished(); + --num_occupied_job_slots_; + } + DCHECK_EQ(1u, num_occupied_job_slots_); + } + void UpdatePriority() { if (is_queued()) { if (priority() != static_cast<RequestPriority>(handle_.priority())) @@ -1337,8 +1424,17 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { // PriorityDispatch::Job: virtual void Start() OVERRIDE { - DCHECK(!is_running()); + DCHECK_LE(num_occupied_job_slots_, 1u); + handle_.Reset(); + ++num_occupied_job_slots_; + + if (num_occupied_job_slots_ == 2) { + StartSecondDnsTransaction(); + return; + } + + DCHECK(!is_running()); net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_STARTED); @@ -1359,8 +1455,12 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { queue_time_after_change); } + bool system_only = + (key_.host_resolver_flags & HOST_RESOLVER_SYSTEM_ONLY) != 0; + // Caution: Job::Start must not complete synchronously. - if (had_dns_config_ && !ResemblesMulticastDNSName(key_.hostname)) { + if (!system_only && had_dns_config_ && + !ResemblesMulticastDNSName(key_.hostname)) { StartDnsTask(); } else { StartProcTask(); @@ -1442,14 +1542,18 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { void StartDnsTask() { DCHECK(resolver_->HaveDnsConfig()); - base::TimeTicks start_time = base::TimeTicks::Now(); - dns_task_.reset(new DnsTask( - resolver_->dns_client_.get(), - key_, - base::Bind(&Job::OnDnsTaskComplete, base::Unretained(this), start_time), - net_log_)); + dns_task_.reset(new DnsTask(resolver_->dns_client_.get(), key_, this, + net_log_)); - dns_task_->Start(); + dns_task_->StartFirstTransaction(); + // Schedule a second transaction, if needed. + if (dns_task_->needs_two_transactions()) + Schedule(true); + } + + void StartSecondDnsTransaction() { + DCHECK(dns_task_->needs_two_transactions()); + dns_task_->StartSecondTransaction(); } // Called if DnsTask fails. It is posted from StartDnsTask, so Job may be @@ -1471,7 +1575,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { // TODO(szym): Some net errors indicate lack of connectivity. Starting // ProcTask in that case is a waste of time. if (resolver_->fallback_to_proctask_) { - dns_task_.reset(); + KillDnsTask(); StartProcTask(); } else { UmaAsyncDnsResolveStatus(RESOLVE_STATUS_FAIL); @@ -1479,11 +1583,13 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { } } - // Called by DnsTask when it completes. - void OnDnsTaskComplete(base::TimeTicks start_time, - int net_error, - const AddressList& addr_list, - base::TimeDelta ttl) { + + // HostResolverImpl::DnsTask::Delegate implementation: + + virtual void OnDnsTaskComplete(base::TimeTicks start_time, + int net_error, + const AddressList& addr_list, + base::TimeDelta ttl) OVERRIDE { DCHECK(is_dns_running()); base::TimeDelta duration = base::TimeTicks::Now() - start_time; @@ -1518,6 +1624,19 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { bounded_ttl); } + virtual void OnFirstDnsTransactionComplete() OVERRIDE { + DCHECK(dns_task_->needs_two_transactions()); + DCHECK_EQ(dns_task_->needs_another_transaction(), is_queued()); + // No longer need to occupy two dispatcher slots. + ReduceToOneJobSlot(); + + // We already have a job slot at the dispatcher, so if the second + // transaction hasn't started, reuse it now instead of waiting in the queue + // for the second slot. + if (dns_task_->needs_another_transaction()) + dns_task_->StartSecondTransaction(); + } + // Performs Job's last rites. Completes all Requests. Deletes this. void CompleteRequests(const HostCache::Entry& entry, base::TimeDelta ttl) { @@ -1532,12 +1651,12 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { resolver_->RemoveJob(this); if (is_running()) { - DCHECK(!is_queued()); if (is_proc_running()) { + DCHECK(!is_queued()); proc_task_->Cancel(); proc_task_ = NULL; } - dns_task_.reset(); + KillDnsTask(); // Signal dispatcher that a slot has opened. resolver_->dispatcher_.OnJobFinished(); @@ -1631,6 +1750,9 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { // Distinguishes measurements taken while DnsClient was fully configured. bool had_dns_config_; + // Number of slots occupied by this Job in resolver's PrioritizedDispatcher. + unsigned num_occupied_job_slots_; + // Result of DnsTask. int dns_task_error_; @@ -1681,6 +1803,7 @@ HostResolverImpl::HostResolverImpl( received_dns_config_(false), num_dns_failures_(0), probe_ipv6_support_(true), + use_local_ipv6_(false), resolved_known_ipv6_hostname_(false), additional_resolver_flags_(0), fallback_to_proctask_(true) { @@ -1706,19 +1829,22 @@ HostResolverImpl::HostResolverImpl( EnsureDnsReloaderInit(); #endif - // TODO(szym): Remove when received_dns_config_ is removed, once - // http://crbug.com/137914 is resolved. { DnsConfig dns_config; NetworkChangeNotifier::GetDnsConfig(&dns_config); received_dns_config_ = dns_config.IsValid(); + // Conservatively assume local IPv6 is needed when DnsConfig is not valid. + use_local_ipv6_ = !dns_config.IsValid() || dns_config.use_local_ipv6; } fallback_to_proctask_ = !ConfigureAsyncDnsNoFallbackFieldTrial(); } HostResolverImpl::~HostResolverImpl() { - // This will also cancel all outstanding requests. + // Prevent the dispatcher from starting new jobs. + dispatcher_.SetLimitsToZero(); + // It's now safe for Jobs to call KillDsnTask on destruction, because + // OnJobComplete will not start any new jobs. STLDeleteValues(&jobs_); NetworkChangeNotifier::RemoveIPAddressObserver(this); @@ -1732,6 +1858,7 @@ void HostResolverImpl::SetMaxQueuedJobs(size_t value) { } int HostResolverImpl::Resolve(const RequestInfo& info, + RequestPriority priority, AddressList* addresses, const CompletionCallback& callback, RequestHandle* out_req, @@ -1768,9 +1895,9 @@ int HostResolverImpl::Resolve(const RequestInfo& info, JobMap::iterator jobit = jobs_.find(key); Job* job; if (jobit == jobs_.end()) { - job = new Job(weak_ptr_factory_.GetWeakPtr(), key, info.priority(), - request_net_log); - job->Schedule(); + job = + new Job(weak_ptr_factory_.GetWeakPtr(), key, priority, request_net_log); + job->Schedule(false); // Check for queue overflow. if (dispatcher_.num_queued_jobs() > max_queued_jobs_) { @@ -1789,11 +1916,8 @@ int HostResolverImpl::Resolve(const RequestInfo& info, } // Can't complete synchronously. Create and attach request. - scoped_ptr<Request> req(new Request(source_net_log, - request_net_log, - info, - callback, - addresses)); + scoped_ptr<Request> req(new Request( + source_net_log, request_net_log, info, priority, callback, addresses)); if (out_req) *out_req = reinterpret_cast<RequestHandle>(req.get()); @@ -2023,35 +2147,35 @@ HostResolverImpl::Key HostResolverImpl::GetEffectiveKeyForRequest( AddressFamily effective_address_family = info.address_family(); if (info.address_family() == ADDRESS_FAMILY_UNSPECIFIED) { - base::TimeTicks start_time = base::TimeTicks::Now(); - // Google DNS address. - const uint8 kIPv6Address[] = - { 0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88 }; - IPAddressNumber address(kIPv6Address, - kIPv6Address + arraysize(kIPv6Address)); - bool rv6 = IsGloballyReachable(address, net_log); - if (rv6) - net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_IPV6_SUPPORTED); - - UMA_HISTOGRAM_TIMES("Net.IPv6ConnectDuration", - base::TimeTicks::Now() - start_time); - if (rv6) { - UMA_HISTOGRAM_BOOLEAN("Net.IPv6ConnectSuccessMatch", - default_address_family_ == ADDRESS_FAMILY_UNSPECIFIED); + if (probe_ipv6_support_ && !use_local_ipv6_) { + base::TimeTicks start_time = base::TimeTicks::Now(); + // Google DNS address. + const uint8 kIPv6Address[] = + { 0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88 }; + IPAddressNumber address(kIPv6Address, + kIPv6Address + arraysize(kIPv6Address)); + bool rv6 = IsGloballyReachable(address, net_log); + if (rv6) + net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_IPV6_SUPPORTED); + + UMA_HISTOGRAM_TIMES("Net.IPv6ConnectDuration", + base::TimeTicks::Now() - start_time); + if (rv6) { + UMA_HISTOGRAM_BOOLEAN("Net.IPv6ConnectSuccessMatch", + default_address_family_ == ADDRESS_FAMILY_UNSPECIFIED); + } else { + UMA_HISTOGRAM_BOOLEAN("Net.IPv6ConnectFailureMatch", + default_address_family_ != ADDRESS_FAMILY_UNSPECIFIED); + + effective_address_family = ADDRESS_FAMILY_IPV4; + effective_flags |= HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6; + } } else { - UMA_HISTOGRAM_BOOLEAN("Net.IPv6ConnectFailureMatch", - default_address_family_ != ADDRESS_FAMILY_UNSPECIFIED); + effective_address_family = default_address_family_; } } - if (effective_address_family == ADDRESS_FAMILY_UNSPECIFIED && - default_address_family_ != ADDRESS_FAMILY_UNSPECIFIED) { - effective_address_family = default_address_family_; - if (probe_ipv6_support_) - effective_flags |= HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6; - } - return Key(info.hostname(), effective_address_family, effective_flags); } @@ -2070,8 +2194,13 @@ void HostResolverImpl::AbortAllInProgressJobs() { } } - // Check if no dispatcher slots leaked out. - DCHECK_EQ(dispatcher_.num_running_jobs(), jobs_to_abort.size()); + // Pause the dispatcher so it won't start any new dispatcher jobs while + // aborting the old ones. This is needed so that it won't start the second + // DnsTransaction for a job in |jobs_to_abort| if the DnsConfig just became + // invalid. + PrioritizedDispatcher::Limits limits = dispatcher_.GetLimits(); + dispatcher_.SetLimits( + PrioritizedDispatcher::Limits(limits.reserved_slots.size(), 0)); // Life check to bail once |this| is deleted. base::WeakPtr<HostResolverImpl> self = weak_ptr_factory_.GetWeakPtr(); @@ -2081,6 +2210,22 @@ void HostResolverImpl::AbortAllInProgressJobs() { jobs_to_abort[i]->Abort(); jobs_to_abort[i] = NULL; } + + if (self) + dispatcher_.SetLimits(limits); +} + +void HostResolverImpl::AbortDnsTasks() { + // Pause the dispatcher so it won't start any new dispatcher jobs while + // aborting the old ones. This is needed so that it won't start the second + // DnsTransaction for a job if the DnsConfig just changed. + PrioritizedDispatcher::Limits limits = dispatcher_.GetLimits(); + dispatcher_.SetLimits( + PrioritizedDispatcher::Limits(limits.reserved_slots.size(), 0)); + + for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it) + it->second->AbortDnsTask(); + dispatcher_.SetLimits(limits); } void HostResolverImpl::TryServingAllJobsFromHosts() { @@ -2126,6 +2271,8 @@ void HostResolverImpl::OnDNSChanged() { // TODO(szym): Remove once http://crbug.com/137914 is resolved. received_dns_config_ = dns_config.IsValid(); + // Conservatively assume local IPv6 is needed when DnsConfig is not valid. + use_local_ipv6_ = !dns_config.IsValid() || dns_config.use_local_ipv6; num_dns_failures_ = 0; @@ -2133,7 +2280,7 @@ void HostResolverImpl::OnDNSChanged() { // the newly started jobs use the new config. if (dns_client_.get()) { dns_client_->SetConfig(dns_config); - if (dns_config.IsValid()) + if (dns_client_->GetConfig()) UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", true); } @@ -2175,10 +2322,14 @@ void HostResolverImpl::OnDnsTaskResolve(int net_error) { ++num_dns_failures_; if (num_dns_failures_ < kMaximumDnsFailures) return; - // Disable DnsClient until the next DNS change. - for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it) - it->second->AbortDnsTask(); + + // Disable DnsClient until the next DNS change. Must be done before aborting + // DnsTasks, since doing so may start new jobs. dns_client_->SetConfig(DnsConfig()); + + // Switch jobs with active DnsTasks over to using ProcTasks. + AbortDnsTasks(); + UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", false); UMA_HISTOGRAM_CUSTOM_ENUMERATION("AsyncDNS.DnsClientDisabledReason", std::abs(net_error), @@ -2186,21 +2337,20 @@ void HostResolverImpl::OnDnsTaskResolve(int net_error) { } void HostResolverImpl::SetDnsClient(scoped_ptr<DnsClient> dns_client) { - if (HaveDnsConfig()) { - for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it) - it->second->AbortDnsTask(); - } + // DnsClient and config must be updated before aborting DnsTasks, since doing + // so may start new jobs. dns_client_ = dns_client.Pass(); - if (!dns_client_ || dns_client_->GetConfig() || - num_dns_failures_ >= kMaximumDnsFailures) { - return; + if (dns_client_ && !dns_client_->GetConfig() && + num_dns_failures_ < kMaximumDnsFailures) { + DnsConfig dns_config; + NetworkChangeNotifier::GetDnsConfig(&dns_config); + dns_client_->SetConfig(dns_config); + num_dns_failures_ = 0; + if (dns_client_->GetConfig()) + UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", true); } - DnsConfig dns_config; - NetworkChangeNotifier::GetDnsConfig(&dns_config); - dns_client_->SetConfig(dns_config); - num_dns_failures_ = 0; - if (dns_config.IsValid()) - UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", true); + + AbortDnsTasks(); } } // namespace net diff --git a/chromium/net/dns/host_resolver_impl.h b/chromium/net/dns/host_resolver_impl.h index 928d07af8b8..e7f5f6541ca 100644 --- a/chromium/net/dns/host_resolver_impl.h +++ b/chromium/net/dns/host_resolver_impl.h @@ -131,6 +131,7 @@ class NET_EXPORT HostResolverImpl // HostResolver methods: virtual int Resolve(const RequestInfo& info, + RequestPriority priority, AddressList* addresses, const CompletionCallback& callback, RequestHandle* out_req, @@ -156,6 +157,10 @@ class NET_EXPORT HostResolverImpl typedef std::map<Key, Job*> JobMap; typedef ScopedVector<Request> RequestsList; + // Number of consecutive failures of DnsTask (with successful fallback to + // ProcTask) before the DnsClient is disabled until the next DNS change. + static const unsigned kMaximumDnsFailures; + // Helper used by |Resolve()| and |ResolveFromCache()|. Performs IP // literal, cache and HOSTS lookup (if enabled), returns OK if successful, // ERR_NAME_NOT_RESOLVED if either hostname is invalid or IP literal is @@ -207,6 +212,11 @@ class NET_EXPORT HostResolverImpl // requests. Might start new jobs. void AbortAllInProgressJobs(); + // Aborts all in progress DnsTasks. In-progress jobs will fall back to + // ProcTasks. Might start new jobs, if any jobs were taking up two dispatcher + // slots. + void AbortDnsTasks(); + // Attempts to serve each Job in |jobs_| from the HOSTS file if we have // a DnsClient with a valid DnsConfig. void TryServingAllJobsFromHosts(); @@ -224,8 +234,10 @@ class NET_EXPORT HostResolverImpl // and resulted in |net_error|. void OnDnsTaskResolve(int net_error); - // Allows the tests to catch slots leaking out of the dispatcher. - size_t num_running_jobs_for_tests() const { + // Allows the tests to catch slots leaking out of the dispatcher. One + // HostResolverImpl::Job could occupy multiple PrioritizedDispatcher job + // slots. + size_t num_running_dispatcher_jobs_for_tests() const { return dispatcher_.num_running_jobs(); } @@ -267,6 +279,10 @@ class NET_EXPORT HostResolverImpl // explicit setting in |default_address_family_| is used. bool probe_ipv6_support_; + // True if DnsConfigService detected that system configuration depends on + // local IPv6 connectivity. Disables probing. + bool use_local_ipv6_; + // True iff ProcTask has successfully resolved a hostname known to have IPv6 // addresses using ADDRESS_FAMILY_UNSPECIFIED. Reset on IP address change. bool resolved_known_ipv6_hostname_; diff --git a/chromium/net/dns/host_resolver_impl_unittest.cc b/chromium/net/dns/host_resolver_impl_unittest.cc index f6b7f690675..c314f80c24d 100644 --- a/chromium/net/dns/host_resolver_impl_unittest.cc +++ b/chromium/net/dns/host_resolver_impl_unittest.cc @@ -12,6 +12,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_vector.h" #include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/synchronization/condition_variable.h" @@ -197,10 +198,12 @@ class Request { }; Request(const HostResolver::RequestInfo& info, + RequestPriority priority, size_t index, HostResolver* resolver, Handler* handler) : info_(info), + priority_(priority), index_(index), resolver_(resolver), handler_(handler), @@ -213,8 +216,12 @@ class Request { DCHECK(!handle_); list_ = AddressList(); result_ = resolver_->Resolve( - info_, &list_, base::Bind(&Request::OnComplete, base::Unretained(this)), - &handle_, BoundNetLog()); + info_, + priority_, + &list_, + base::Bind(&Request::OnComplete, base::Unretained(this)), + &handle_, + BoundNetLog()); if (!list_.empty()) EXPECT_EQ(OK, result_); return result_; @@ -291,6 +298,7 @@ class Request { } HostResolver::RequestInfo info_; + RequestPriority priority_; size_t index_; HostResolver* resolver_; Handler* handler_; @@ -413,14 +421,29 @@ class HostResolverImplTest : public testing::Test { HostResolverImplTest() : proc_(new MockHostResolverProc()) {} + void CreateResolver() { + CreateResolverWithLimitsAndParams(DefaultLimits(), + DefaultParams(proc_.get())); + } + + // This HostResolverImpl will only allow 1 outstanding resolve at a time and + // perform no retries. + void CreateSerialResolver() { + HostResolverImpl::ProcTaskParams params = DefaultParams(proc_.get()); + params.max_retry_attempts = 0u; + PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1); + CreateResolverWithLimitsAndParams(limits, params); + } + protected: // A Request::Handler which is a proxy to the HostResolverImplTest fixture. struct Handler : public Request::Handler { virtual ~Handler() {} // Proxy functions so that classes derived from Handler can access them. - Request* CreateRequest(const HostResolver::RequestInfo& info) { - return test->CreateRequest(info); + Request* CreateRequest(const HostResolver::RequestInfo& info, + RequestPriority priority) { + return test->CreateRequest(info, priority); } Request* CreateRequest(const std::string& hostname, int port) { return test->CreateRequest(hostname, port); @@ -435,31 +458,30 @@ class HostResolverImplTest : public testing::Test { HostResolverImplTest* test; }; - void CreateResolver() { - resolver_.reset(new HostResolverImpl(HostCache::CreateDefaultCache(), - DefaultLimits(), - DefaultParams(proc_.get()), - NULL)); + // testing::Test implementation: + virtual void SetUp() OVERRIDE { + CreateResolver(); } - // This HostResolverImpl will only allow 1 outstanding resolve at a time and - // perform no retries. - void CreateSerialResolver() { - HostResolverImpl::ProcTaskParams params = DefaultParams(proc_.get()); - params.max_retry_attempts = 0u; - PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1); - resolver_.reset(new HostResolverImpl( - HostCache::CreateDefaultCache(), - limits, - params, - NULL)); + virtual void TearDown() OVERRIDE { + if (resolver_.get()) + EXPECT_EQ(0u, resolver_->num_running_dispatcher_jobs_for_tests()); + EXPECT_FALSE(proc_->HasBlockedRequests()); + } + + virtual void CreateResolverWithLimitsAndParams( + const PrioritizedDispatcher::Limits& limits, + const HostResolverImpl::ProcTaskParams& params) { + resolver_.reset(new HostResolverImpl(HostCache::CreateDefaultCache(), + limits, params, NULL)); } // The Request will not be made until a call to |Resolve()|, and the Job will // not start until released by |proc_->SignalXXX|. - Request* CreateRequest(const HostResolver::RequestInfo& info) { - Request* req = new Request(info, requests_.size(), resolver_.get(), - handler_.get()); + Request* CreateRequest(const HostResolver::RequestInfo& info, + RequestPriority priority) { + Request* req = new Request( + info, priority, requests_.size(), resolver_.get(), handler_.get()); requests_.push_back(req); return req; } @@ -469,9 +491,8 @@ class HostResolverImplTest : public testing::Test { RequestPriority priority, AddressFamily family) { HostResolver::RequestInfo info(HostPortPair(hostname, port)); - info.set_priority(priority); info.set_address_family(family); - return CreateRequest(info); + return CreateRequest(info, priority); } Request* CreateRequest(const std::string& hostname, @@ -488,25 +509,15 @@ class HostResolverImplTest : public testing::Test { return CreateRequest(hostname, kDefaultPort); } - virtual void SetUp() OVERRIDE { - CreateResolver(); - } - - virtual void TearDown() OVERRIDE { - if (resolver_.get()) - EXPECT_EQ(0u, resolver_->num_running_jobs_for_tests()); - EXPECT_FALSE(proc_->HasBlockedRequests()); - } - void set_handler(Handler* handler) { handler_.reset(handler); handler_->test = this; } // Friendship is not inherited, so use proxies to access those. - size_t num_running_jobs() const { + size_t num_running_dispatcher_jobs() const { DCHECK(resolver_.get()); - return resolver_->num_running_jobs_for_tests(); + return resolver_->num_running_dispatcher_jobs_for_tests(); } void set_fallback_to_proctask(bool fallback_to_proctask) { @@ -514,6 +525,10 @@ class HostResolverImplTest : public testing::Test { resolver_->fallback_to_proctask_ = fallback_to_proctask; } + static unsigned maximum_dns_failures() { + return HostResolverImpl::kMaximumDnsFailures; + } + scoped_refptr<MockHostResolverProc> proc_; scoped_ptr<HostResolverImpl> resolver_; ScopedVector<Request> requests_; @@ -817,7 +832,8 @@ TEST_F(HostResolverImplTest, BypassCache) { // longer service the request synchronously. HostResolver::RequestInfo info(HostPortPair(hostname, 71)); info.set_allow_cached_response(false); - EXPECT_EQ(ERR_IO_PENDING, CreateRequest(info)->Resolve()); + EXPECT_EQ(ERR_IO_PENDING, + CreateRequest(info, DEFAULT_PRIORITY)->Resolve()); } else if (71 == req->info().port()) { // Test is done. base::MessageLoop::current()->Quit(); @@ -889,7 +905,7 @@ TEST_F(HostResolverImplTest, ObeyPoolConstraintsAfterIPAddressChange) { EXPECT_EQ(ERR_NETWORK_CHANGED, requests_[0]->WaitForResult()); - EXPECT_EQ(1u, num_running_jobs()); + EXPECT_EQ(1u, num_running_dispatcher_jobs()); EXPECT_FALSE(requests_[1]->completed()); EXPECT_FALSE(requests_[2]->completed()); @@ -1189,14 +1205,15 @@ TEST_F(HostResolverImplTest, ResolveFromCache) { HostResolver::RequestInfo info(HostPortPair("just.testing", 80)); // First hit will miss the cache. - EXPECT_EQ(ERR_DNS_CACHE_MISS, CreateRequest(info)->ResolveFromCache()); + EXPECT_EQ(ERR_DNS_CACHE_MISS, + CreateRequest(info, DEFAULT_PRIORITY)->ResolveFromCache()); // This time, we fetch normally. - EXPECT_EQ(ERR_IO_PENDING, CreateRequest(info)->Resolve()); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest(info, DEFAULT_PRIORITY)->Resolve()); EXPECT_EQ(OK, requests_[1]->WaitForResult()); // Now we should be able to fetch from the cache. - EXPECT_EQ(OK, CreateRequest(info)->ResolveFromCache()); + EXPECT_EQ(OK, CreateRequest(info, DEFAULT_PRIORITY)->ResolveFromCache()); EXPECT_TRUE(requests_[2]->HasOneAddress("192.168.1.42", 80)); } @@ -1228,7 +1245,7 @@ TEST_F(HostResolverImplTest, MultipleAttempts) { // Resolve "host1". HostResolver::RequestInfo info(HostPortPair("host1", 70)); - Request* req = CreateRequest(info); + Request* req = CreateRequest(info, DEFAULT_PRIORITY); EXPECT_EQ(ERR_IO_PENDING, req->Resolve()); // Resolve returns -4 to indicate that 3rd attempt has resolved the host. @@ -1255,36 +1272,70 @@ DnsConfig CreateValidDnsConfig() { // Specialized fixture for tests of DnsTask. class HostResolverImplDnsTest : public HostResolverImplTest { + public: + HostResolverImplDnsTest() : dns_client_(NULL) {} + protected: + // testing::Test implementation: virtual void SetUp() OVERRIDE { - AddDnsRule("nx", dns_protocol::kTypeA, MockDnsClientRule::FAIL); - AddDnsRule("nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL); - AddDnsRule("ok", dns_protocol::kTypeA, MockDnsClientRule::OK); - AddDnsRule("ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK); - AddDnsRule("4ok", dns_protocol::kTypeA, MockDnsClientRule::OK); - AddDnsRule("4ok", dns_protocol::kTypeAAAA, MockDnsClientRule::EMPTY); - AddDnsRule("6ok", dns_protocol::kTypeA, MockDnsClientRule::EMPTY); - AddDnsRule("6ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK); - AddDnsRule("4nx", dns_protocol::kTypeA, MockDnsClientRule::OK); - AddDnsRule("4nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL); + AddDnsRule("nx", dns_protocol::kTypeA, MockDnsClientRule::FAIL, false); + AddDnsRule("nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL, false); + AddDnsRule("ok", dns_protocol::kTypeA, MockDnsClientRule::OK, false); + AddDnsRule("ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK, false); + AddDnsRule("4ok", dns_protocol::kTypeA, MockDnsClientRule::OK, false); + AddDnsRule("4ok", dns_protocol::kTypeAAAA, MockDnsClientRule::EMPTY, false); + AddDnsRule("6ok", dns_protocol::kTypeA, MockDnsClientRule::EMPTY, false); + AddDnsRule("6ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK, false); + AddDnsRule("4nx", dns_protocol::kTypeA, MockDnsClientRule::OK, false); + AddDnsRule("4nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL, false); + AddDnsRule("empty", dns_protocol::kTypeA, MockDnsClientRule::EMPTY, false); + AddDnsRule("empty", dns_protocol::kTypeAAAA, MockDnsClientRule::EMPTY, + false); + + AddDnsRule("slow_nx", dns_protocol::kTypeA, MockDnsClientRule::FAIL, true); + AddDnsRule("slow_nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL, + true); + + AddDnsRule("4slow_ok", dns_protocol::kTypeA, MockDnsClientRule::OK, true); + AddDnsRule("4slow_ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK, + false); + AddDnsRule("6slow_ok", dns_protocol::kTypeA, MockDnsClientRule::OK, false); + AddDnsRule("6slow_ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK, + true); + AddDnsRule("4slow_4ok", dns_protocol::kTypeA, MockDnsClientRule::OK, true); + AddDnsRule("4slow_4ok", dns_protocol::kTypeAAAA, MockDnsClientRule::EMPTY, + false); + AddDnsRule("4slow_4timeout", dns_protocol::kTypeA, + MockDnsClientRule::TIMEOUT, true); + AddDnsRule("4slow_4timeout", dns_protocol::kTypeAAAA, MockDnsClientRule::OK, + false); + AddDnsRule("4slow_6timeout", dns_protocol::kTypeA, + MockDnsClientRule::OK, true); + AddDnsRule("4slow_6timeout", dns_protocol::kTypeAAAA, + MockDnsClientRule::TIMEOUT, false); CreateResolver(); } - void CreateResolver() { + // HostResolverImplTest implementation: + virtual void CreateResolverWithLimitsAndParams( + const PrioritizedDispatcher::Limits& limits, + const HostResolverImpl::ProcTaskParams& params) OVERRIDE { resolver_.reset(new HostResolverImpl(HostCache::CreateDefaultCache(), - DefaultLimits(), - DefaultParams(proc_.get()), + limits, + params, NULL)); // Disable IPv6 support probing. resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED); - resolver_->SetDnsClient(CreateMockDnsClient(DnsConfig(), dns_rules_)); + dns_client_ = new MockDnsClient(DnsConfig(), dns_rules_); + resolver_->SetDnsClient(scoped_ptr<DnsClient>(dns_client_)); } // Adds a rule to |dns_rules_|. Must be followed by |CreateResolver| to apply. void AddDnsRule(const std::string& prefix, uint16 qtype, - MockDnsClientRule::Result result) { - dns_rules_.push_back(MockDnsClientRule(prefix, qtype, result)); + MockDnsClientRule::Result result, + bool delay) { + dns_rules_.push_back(MockDnsClientRule(prefix, qtype, result, delay)); } void ChangeDnsConfig(const DnsConfig& config) { @@ -1294,6 +1345,8 @@ class HostResolverImplDnsTest : public HostResolverImplTest { } MockDnsClientRuleList dns_rules_; + // Owned by |resolver_|. + MockDnsClient* dns_client_; }; // TODO(szym): Test AbortAllInProgressJobs due to DnsConfig change. @@ -1361,7 +1414,8 @@ TEST_F(HostResolverImplDnsTest, NoFallbackToProcTask) { // Simulate the case when the preference or policy has disabled the DNS client // causing AbortDnsTasks. - resolver_->SetDnsClient(CreateMockDnsClient(DnsConfig(), dns_rules_)); + resolver_->SetDnsClient( + scoped_ptr<DnsClient>(new MockDnsClient(DnsConfig(), dns_rules_))); ChangeDnsConfig(CreateValidDnsConfig()); // First request is resolved by MockDnsClient, others should fail due to @@ -1509,6 +1563,24 @@ TEST_F(HostResolverImplDnsTest, BypassDnsTask) { EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i; } +TEST_F(HostResolverImplDnsTest, SystemOnlyBypassesDnsTask) { + ChangeDnsConfig(CreateValidDnsConfig()); + + proc_->AddRuleForAllFamilies(std::string(), std::string()); + + HostResolver::RequestInfo info_bypass(HostPortPair("ok", 80)); + info_bypass.set_host_resolver_flags(HOST_RESOLVER_SYSTEM_ONLY); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest(info_bypass, MEDIUM)->Resolve()); + + HostResolver::RequestInfo info(HostPortPair("ok", 80)); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest(info, MEDIUM)->Resolve()); + + proc_->SignalMultiple(requests_.size()); + + EXPECT_EQ(ERR_NAME_NOT_RESOLVED, requests_[0]->WaitForResult()); + EXPECT_EQ(OK, requests_[1]->WaitForResult()); +} + TEST_F(HostResolverImplDnsTest, DisableDnsClientOnPersistentFailure) { ChangeDnsConfig(CreateValidDnsConfig()); @@ -1520,7 +1592,7 @@ TEST_F(HostResolverImplDnsTest, DisableDnsClientOnPersistentFailure) { EXPECT_EQ(ERR_IO_PENDING, req->Resolve()); EXPECT_EQ(OK, req->WaitForResult()); - for (unsigned i = 0; i < 20; ++i) { + for (unsigned i = 0; i < maximum_dns_failures(); ++i) { // Use custom names to require separate Jobs. std::string hostname = base::StringPrintf("nx_%u", i); // Ensure fallback to ProcTask succeeds. @@ -1585,7 +1657,8 @@ TEST_F(HostResolverImplDnsTest, DualFamilyLocalhost) { DefaultLimits(), DefaultParams(proc.get()), NULL)); - resolver_->SetDnsClient(CreateMockDnsClient(DnsConfig(), dns_rules_)); + resolver_->SetDnsClient( + scoped_ptr<DnsClient>(new MockDnsClient(DnsConfig(), dns_rules_))); resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4); // Get the expected output. @@ -1609,7 +1682,7 @@ TEST_F(HostResolverImplDnsTest, DualFamilyLocalhost) { // Try without DnsClient. ChangeDnsConfig(DnsConfig()); - Request* req = CreateRequest(info); + Request* req = CreateRequest(info, DEFAULT_PRIORITY); // It is resolved via getaddrinfo, so expect asynchronous result. EXPECT_EQ(ERR_IO_PENDING, req->Resolve()); EXPECT_EQ(OK, req->WaitForResult()); @@ -1630,7 +1703,7 @@ TEST_F(HostResolverImplDnsTest, DualFamilyLocalhost) { config.hosts = hosts; ChangeDnsConfig(config); - req = CreateRequest(info); + req = CreateRequest(info, DEFAULT_PRIORITY); // Expect synchronous resolution from DnsHosts. EXPECT_EQ(OK, req->Resolve()); @@ -1638,4 +1711,346 @@ TEST_F(HostResolverImplDnsTest, DualFamilyLocalhost) { EXPECT_EQ(saw_ipv6, req->HasAddress("::1", 80)); } +// Cancel a request with a single DNS transaction active. +TEST_F(HostResolverImplDnsTest, CancelWithOneTransactionActive) { + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4); + ChangeDnsConfig(CreateValidDnsConfig()); + + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80)->Resolve()); + EXPECT_EQ(1u, num_running_dispatcher_jobs()); + requests_[0]->Cancel(); + + // Dispatcher state checked in TearDown. +} + +// Cancel a request with a single DNS transaction active and another pending. +TEST_F(HostResolverImplDnsTest, CancelWithOneTransactionActiveOnePending) { + CreateSerialResolver(); + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED); + ChangeDnsConfig(CreateValidDnsConfig()); + + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80)->Resolve()); + EXPECT_EQ(1u, num_running_dispatcher_jobs()); + requests_[0]->Cancel(); + + // Dispatcher state checked in TearDown. +} + +// Cancel a request with two DNS transactions active. +TEST_F(HostResolverImplDnsTest, CancelWithTwoTransactionsActive) { + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED); + ChangeDnsConfig(CreateValidDnsConfig()); + + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80)->Resolve()); + EXPECT_EQ(2u, num_running_dispatcher_jobs()); + requests_[0]->Cancel(); + + // Dispatcher state checked in TearDown. +} + +// Delete a resolver with some active requests and some queued requests. +TEST_F(HostResolverImplDnsTest, DeleteWithActiveTransactions) { + // At most 10 Jobs active at once. + CreateResolverWithLimitsAndParams( + PrioritizedDispatcher::Limits(NUM_PRIORITIES, 10u), + DefaultParams(proc_.get())); + + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED); + ChangeDnsConfig(CreateValidDnsConfig()); + + // First active job is an IPv4 request. + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80, MEDIUM, + ADDRESS_FAMILY_IPV4)->Resolve()); + + // Add 10 more DNS lookups for different hostnames. First 4 should have two + // active jobs, next one has a single active job, and one pending. Others + // should all be queued. + for (int i = 0; i < 10; ++i) { + EXPECT_EQ(ERR_IO_PENDING, CreateRequest( + base::StringPrintf("ok%i", i))->Resolve()); + } + EXPECT_EQ(10u, num_running_dispatcher_jobs()); + + resolver_.reset(); +} + +// Cancel a request with only the IPv6 transaction active. +TEST_F(HostResolverImplDnsTest, CancelWithIPv6TransactionActive) { + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED); + ChangeDnsConfig(CreateValidDnsConfig()); + + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("6slow_ok", 80)->Resolve()); + EXPECT_EQ(2u, num_running_dispatcher_jobs()); + + // The IPv4 request should complete, the IPv6 request is still pending. + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1u, num_running_dispatcher_jobs()); + requests_[0]->Cancel(); + + // Dispatcher state checked in TearDown. +} + +// Cancel a request with only the IPv4 transaction pending. +TEST_F(HostResolverImplDnsTest, CancelWithIPv4TransactionPending) { + set_fallback_to_proctask(false); + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED); + ChangeDnsConfig(CreateValidDnsConfig()); + + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4slow_ok", 80)->Resolve()); + EXPECT_EQ(2u, num_running_dispatcher_jobs()); + + // The IPv6 request should complete, the IPv4 request is still pending. + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1u, num_running_dispatcher_jobs()); + + requests_[0]->Cancel(); +} + +// Test cases where AAAA completes first. +TEST_F(HostResolverImplDnsTest, AAAACompletesFirst) { + set_fallback_to_proctask(false); + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED); + ChangeDnsConfig(CreateValidDnsConfig()); + + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4slow_ok", 80)->Resolve()); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4slow_4ok", 80)->Resolve()); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4slow_4timeout", 80)->Resolve()); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4slow_6timeout", 80)->Resolve()); + + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(requests_[0]->completed()); + EXPECT_FALSE(requests_[1]->completed()); + EXPECT_FALSE(requests_[2]->completed()); + // The IPv6 of the third request should have failed and resulted in cancelling + // the IPv4 request. + EXPECT_TRUE(requests_[3]->completed()); + EXPECT_EQ(ERR_DNS_TIMED_OUT, requests_[3]->result()); + EXPECT_EQ(3u, num_running_dispatcher_jobs()); + + dns_client_->CompleteDelayedTransactions(); + EXPECT_TRUE(requests_[0]->completed()); + EXPECT_EQ(OK, requests_[0]->result()); + EXPECT_EQ(2u, requests_[0]->NumberOfAddresses()); + EXPECT_TRUE(requests_[0]->HasAddress("127.0.0.1", 80)); + EXPECT_TRUE(requests_[0]->HasAddress("::1", 80)); + + EXPECT_TRUE(requests_[1]->completed()); + EXPECT_EQ(OK, requests_[1]->result()); + EXPECT_EQ(1u, requests_[1]->NumberOfAddresses()); + EXPECT_TRUE(requests_[1]->HasAddress("127.0.0.1", 80)); + + EXPECT_TRUE(requests_[2]->completed()); + EXPECT_EQ(ERR_DNS_TIMED_OUT, requests_[2]->result()); +} + +// Test the case where only a single transaction slot is available. +TEST_F(HostResolverImplDnsTest, SerialResolver) { + CreateSerialResolver(); + set_fallback_to_proctask(false); + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED); + ChangeDnsConfig(CreateValidDnsConfig()); + + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80)->Resolve()); + EXPECT_EQ(1u, num_running_dispatcher_jobs()); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(requests_[0]->completed()); + EXPECT_EQ(OK, requests_[0]->result()); + EXPECT_EQ(2u, requests_[0]->NumberOfAddresses()); + EXPECT_TRUE(requests_[0]->HasAddress("127.0.0.1", 80)); + EXPECT_TRUE(requests_[0]->HasAddress("::1", 80)); +} + +// Test the case where the AAAA query is started when another transaction +// completes. +TEST_F(HostResolverImplDnsTest, AAAAStartsAfterOtherJobFinishes) { + CreateResolverWithLimitsAndParams( + PrioritizedDispatcher::Limits(NUM_PRIORITIES, 2), + DefaultParams(proc_.get())); + set_fallback_to_proctask(false); + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED); + ChangeDnsConfig(CreateValidDnsConfig()); + + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80, MEDIUM, + ADDRESS_FAMILY_IPV4)->Resolve()); + EXPECT_EQ(ERR_IO_PENDING, + CreateRequest("4slow_ok", 80, MEDIUM)->Resolve()); + // An IPv4 request should have been started pending for each job. + EXPECT_EQ(2u, num_running_dispatcher_jobs()); + + // Request 0's IPv4 request should complete, starting Request 1's IPv6 + // request, which should also complete. + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1u, num_running_dispatcher_jobs()); + EXPECT_TRUE(requests_[0]->completed()); + EXPECT_FALSE(requests_[1]->completed()); + + dns_client_->CompleteDelayedTransactions(); + EXPECT_TRUE(requests_[1]->completed()); + EXPECT_EQ(OK, requests_[1]->result()); + EXPECT_EQ(2u, requests_[1]->NumberOfAddresses()); + EXPECT_TRUE(requests_[1]->HasAddress("127.0.0.1", 80)); + EXPECT_TRUE(requests_[1]->HasAddress("::1", 80)); +} + +// Tests the case that a Job with a single transaction receives an empty address +// list, triggering fallback to ProcTask. +TEST_F(HostResolverImplDnsTest, IPv4EmptyFallback) { + ChangeDnsConfig(CreateValidDnsConfig()); + proc_->AddRuleForAllFamilies("empty_fallback", "192.168.0.1"); + proc_->SignalMultiple(1u); + EXPECT_EQ(ERR_IO_PENDING, + CreateRequest("empty_fallback", 80, MEDIUM, + ADDRESS_FAMILY_IPV4)->Resolve()); + EXPECT_EQ(OK, requests_[0]->WaitForResult()); + EXPECT_TRUE(requests_[0]->HasOneAddress("192.168.0.1", 80)); +} + +// Tests the case that a Job with two transactions receives two empty address +// lists, triggering fallback to ProcTask. +TEST_F(HostResolverImplDnsTest, UnspecEmptyFallback) { + ChangeDnsConfig(CreateValidDnsConfig()); + proc_->AddRuleForAllFamilies("empty_fallback", "192.168.0.1"); + proc_->SignalMultiple(1u); + EXPECT_EQ(ERR_IO_PENDING, + CreateRequest("empty_fallback", 80, MEDIUM, + ADDRESS_FAMILY_UNSPECIFIED)->Resolve()); + EXPECT_EQ(OK, requests_[0]->WaitForResult()); + EXPECT_TRUE(requests_[0]->HasOneAddress("192.168.0.1", 80)); +} + +// Tests getting a new invalid DnsConfig while there are active DnsTasks. +TEST_F(HostResolverImplDnsTest, InvalidDnsConfigWithPendingRequests) { + // At most 3 jobs active at once. This number is important, since we want to + // make sure that aborting the first HostResolverImpl::Job does not trigger + // another DnsTransaction on the second Job when it releases its second + // prioritized dispatcher slot. + CreateResolverWithLimitsAndParams( + PrioritizedDispatcher::Limits(NUM_PRIORITIES, 3u), + DefaultParams(proc_.get())); + + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED); + ChangeDnsConfig(CreateValidDnsConfig()); + + proc_->AddRuleForAllFamilies("slow_nx1", "192.168.0.1"); + proc_->AddRuleForAllFamilies("slow_nx2", "192.168.0.2"); + proc_->AddRuleForAllFamilies("ok", "192.168.0.3"); + + // First active job gets two slots. + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_nx1")->Resolve()); + // Next job gets one slot, and waits on another. + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_nx2")->Resolve()); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok")->Resolve()); + + EXPECT_EQ(3u, num_running_dispatcher_jobs()); + + // Clear DNS config. Two in-progress jobs should be aborted, and the next one + // should use a ProcTask. + ChangeDnsConfig(DnsConfig()); + EXPECT_EQ(ERR_NETWORK_CHANGED, requests_[0]->WaitForResult()); + EXPECT_EQ(ERR_NETWORK_CHANGED, requests_[1]->WaitForResult()); + + // Finish up the third job. Should bypass the DnsClient, and get its results + // from MockHostResolverProc. + EXPECT_FALSE(requests_[2]->completed()); + proc_->SignalMultiple(1u); + EXPECT_EQ(OK, requests_[2]->WaitForResult()); + EXPECT_TRUE(requests_[2]->HasOneAddress("192.168.0.3", 80)); +} + +// Tests the case that DnsClient is automatically disabled due to failures +// while there are active DnsTasks. +TEST_F(HostResolverImplDnsTest, + AutomaticallyDisableDnsClientWithPendingRequests) { + // Trying different limits is important for this test: Different limits + // result in different behavior when aborting in-progress DnsTasks. Having + // a DnsTask that has one job active and one in the queue when another job + // occupying two slots has its DnsTask aborted is the case most likely to run + // into problems. + for (size_t limit = 1u; limit < 6u; ++limit) { + CreateResolverWithLimitsAndParams( + PrioritizedDispatcher::Limits(NUM_PRIORITIES, limit), + DefaultParams(proc_.get())); + + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED); + ChangeDnsConfig(CreateValidDnsConfig()); + + // Queue up enough failures to disable DnsTasks. These will all fall back + // to ProcTasks, and succeed there. + for (unsigned i = 0u; i < maximum_dns_failures(); ++i) { + std::string host = base::StringPrintf("nx%u", i); + proc_->AddRuleForAllFamilies(host, "192.168.0.1"); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest(host)->Resolve()); + } + + // These requests should all bypass DnsTasks, due to the above failures, + // so should end up using ProcTasks. + proc_->AddRuleForAllFamilies("slow_ok1", "192.168.0.2"); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_ok1")->Resolve()); + proc_->AddRuleForAllFamilies("slow_ok2", "192.168.0.3"); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_ok2")->Resolve()); + proc_->AddRuleForAllFamilies("slow_ok3", "192.168.0.4"); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_ok3")->Resolve()); + proc_->SignalMultiple(maximum_dns_failures() + 3); + + for (size_t i = 0u; i < maximum_dns_failures(); ++i) { + EXPECT_EQ(OK, requests_[i]->WaitForResult()); + EXPECT_TRUE(requests_[i]->HasOneAddress("192.168.0.1", 80)); + } + + EXPECT_EQ(OK, requests_[maximum_dns_failures()]->WaitForResult()); + EXPECT_TRUE(requests_[maximum_dns_failures()]->HasOneAddress( + "192.168.0.2", 80)); + EXPECT_EQ(OK, requests_[maximum_dns_failures() + 1]->WaitForResult()); + EXPECT_TRUE(requests_[maximum_dns_failures() + 1]->HasOneAddress( + "192.168.0.3", 80)); + EXPECT_EQ(OK, requests_[maximum_dns_failures() + 2]->WaitForResult()); + EXPECT_TRUE(requests_[maximum_dns_failures() + 2]->HasOneAddress( + "192.168.0.4", 80)); + requests_.clear(); + } +} + +// Tests a call to SetDnsClient while there are active DnsTasks. +TEST_F(HostResolverImplDnsTest, ManuallyDisableDnsClientWithPendingRequests) { + // At most 3 jobs active at once. This number is important, since we want to + // make sure that aborting the first HostResolverImpl::Job does not trigger + // another DnsTransaction on the second Job when it releases its second + // prioritized dispatcher slot. + CreateResolverWithLimitsAndParams( + PrioritizedDispatcher::Limits(NUM_PRIORITIES, 3u), + DefaultParams(proc_.get())); + + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED); + ChangeDnsConfig(CreateValidDnsConfig()); + + proc_->AddRuleForAllFamilies("slow_ok1", "192.168.0.1"); + proc_->AddRuleForAllFamilies("slow_ok2", "192.168.0.2"); + proc_->AddRuleForAllFamilies("ok", "192.168.0.3"); + + // First active job gets two slots. + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_ok1")->Resolve()); + // Next job gets one slot, and waits on another. + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_ok2")->Resolve()); + // Next one is queued. + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok")->Resolve()); + + EXPECT_EQ(3u, num_running_dispatcher_jobs()); + + // Clear DnsClient. The two in-progress jobs should fall back to a ProcTask, + // and the next one should be started with a ProcTask. + resolver_->SetDnsClient(scoped_ptr<DnsClient>()); + + // All three in-progress requests should now be running a ProcTask. + EXPECT_EQ(3u, num_running_dispatcher_jobs()); + proc_->SignalMultiple(3u); + + EXPECT_EQ(OK, requests_[0]->WaitForResult()); + EXPECT_TRUE(requests_[0]->HasOneAddress("192.168.0.1", 80)); + EXPECT_EQ(OK, requests_[1]->WaitForResult()); + EXPECT_TRUE(requests_[1]->HasOneAddress("192.168.0.2", 80)); + EXPECT_EQ(OK, requests_[2]->WaitForResult()); + EXPECT_TRUE(requests_[2]->HasOneAddress("192.168.0.3", 80)); +} + } // namespace net diff --git a/chromium/net/dns/mapped_host_resolver.cc b/chromium/net/dns/mapped_host_resolver.cc index 4db7bc97928..7b182077cfb 100644 --- a/chromium/net/dns/mapped_host_resolver.cc +++ b/chromium/net/dns/mapped_host_resolver.cc @@ -19,6 +19,7 @@ MappedHostResolver::~MappedHostResolver() { } int MappedHostResolver::Resolve(const RequestInfo& original_info, + RequestPriority priority, AddressList* addresses, const CompletionCallback& callback, RequestHandle* out_req, @@ -28,7 +29,7 @@ int MappedHostResolver::Resolve(const RequestInfo& original_info, if (rv != OK) return rv; - return impl_->Resolve(info, addresses, callback, out_req, net_log); + return impl_->Resolve(info, priority, addresses, callback, out_req, net_log); } int MappedHostResolver::ResolveFromCache(const RequestInfo& original_info, diff --git a/chromium/net/dns/mapped_host_resolver.h b/chromium/net/dns/mapped_host_resolver.h index 50062a9848e..a121d4ed4c4 100644 --- a/chromium/net/dns/mapped_host_resolver.h +++ b/chromium/net/dns/mapped_host_resolver.h @@ -46,6 +46,7 @@ class NET_EXPORT MappedHostResolver : public HostResolver { // HostResolver methods: virtual int Resolve(const RequestInfo& info, + RequestPriority priority, AddressList* addresses, const CompletionCallback& callback, RequestHandle* out_req, diff --git a/chromium/net/dns/mapped_host_resolver_unittest.cc b/chromium/net/dns/mapped_host_resolver_unittest.cc index d8594663c9d..fbbfde8adff 100644 --- a/chromium/net/dns/mapped_host_resolver_unittest.cc +++ b/chromium/net/dns/mapped_host_resolver_unittest.cc @@ -40,10 +40,13 @@ TEST(MappedHostResolverTest, Inclusion) { // Try resolving "www.google.com:80". There are no mappings yet, so this // hits |resolver_impl| and fails. TestCompletionCallback callback; - rv = resolver->Resolve(HostResolver::RequestInfo( - HostPortPair("www.google.com", 80)), - &address_list, callback.callback(), NULL, - BoundNetLog()); + rv = resolver->Resolve( + HostResolver::RequestInfo(HostPortPair("www.google.com", 80)), + DEFAULT_PRIORITY, + &address_list, + callback.callback(), + NULL, + BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv); @@ -52,10 +55,13 @@ TEST(MappedHostResolverTest, Inclusion) { EXPECT_TRUE(resolver->AddRuleFromString("map *.google.com baz.com")); // Try resolving "www.google.com:80". Should be remapped to "baz.com:80". - rv = resolver->Resolve(HostResolver::RequestInfo( - HostPortPair("www.google.com", 80)), - &address_list, callback.callback(), NULL, - BoundNetLog()); + rv = resolver->Resolve( + HostResolver::RequestInfo(HostPortPair("www.google.com", 80)), + DEFAULT_PRIORITY, + &address_list, + callback.callback(), + NULL, + BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); EXPECT_EQ(OK, rv); @@ -64,7 +70,10 @@ TEST(MappedHostResolverTest, Inclusion) { // Try resolving "foo.com:77". This will NOT be remapped, so result // is "foo.com:77". rv = resolver->Resolve(HostResolver::RequestInfo(HostPortPair("foo.com", 77)), - &address_list, callback.callback(), NULL, + DEFAULT_PRIORITY, + &address_list, + callback.callback(), + NULL, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); @@ -75,10 +84,13 @@ TEST(MappedHostResolverTest, Inclusion) { EXPECT_TRUE(resolver->AddRuleFromString("Map *.org proxy:99")); // Try resolving "chromium.org:61". Should be remapped to "proxy:99". - rv = resolver->Resolve(HostResolver::RequestInfo - (HostPortPair("chromium.org", 61)), - &address_list, callback.callback(), NULL, - BoundNetLog()); + rv = resolver->Resolve( + HostResolver::RequestInfo(HostPortPair("chromium.org", 61)), + DEFAULT_PRIORITY, + &address_list, + callback.callback(), + NULL, + BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); EXPECT_EQ(OK, rv); @@ -107,20 +119,26 @@ TEST(MappedHostResolverTest, Exclusion) { EXPECT_TRUE(resolver->AddRuleFromString("EXCLUDE *.google.com")); // Try resolving "www.google.com". Should not be remapped due to exclusion). - rv = resolver->Resolve(HostResolver::RequestInfo( - HostPortPair("www.google.com", 80)), - &address_list, callback.callback(), NULL, - BoundNetLog()); + rv = resolver->Resolve( + HostResolver::RequestInfo(HostPortPair("www.google.com", 80)), + DEFAULT_PRIORITY, + &address_list, + callback.callback(), + NULL, + BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); EXPECT_EQ(OK, rv); EXPECT_EQ("192.168.1.3:80", FirstAddress(address_list)); // Try resolving "chrome.com:80". Should be remapped to "baz:80". - rv = resolver->Resolve(HostResolver::RequestInfo( - HostPortPair("chrome.com", 80)), - &address_list, callback.callback(), NULL, - BoundNetLog()); + rv = resolver->Resolve( + HostResolver::RequestInfo(HostPortPair("chrome.com", 80)), + DEFAULT_PRIORITY, + &address_list, + callback.callback(), + NULL, + BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); EXPECT_EQ(OK, rv); @@ -145,20 +163,26 @@ TEST(MappedHostResolverTest, SetRulesFromString) { resolver->SetRulesFromString("map *.com baz , map *.net bar:60"); // Try resolving "www.google.com". Should be remapped to "baz". - rv = resolver->Resolve(HostResolver::RequestInfo( - HostPortPair("www.google.com", 80)), - &address_list, callback.callback(), NULL, - BoundNetLog()); + rv = resolver->Resolve( + HostResolver::RequestInfo(HostPortPair("www.google.com", 80)), + DEFAULT_PRIORITY, + &address_list, + callback.callback(), + NULL, + BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); EXPECT_EQ(OK, rv); EXPECT_EQ("192.168.1.7:80", FirstAddress(address_list)); // Try resolving "chrome.net:80". Should be remapped to "bar:60". - rv = resolver->Resolve(HostResolver::RequestInfo( - HostPortPair("chrome.net", 80)), - &address_list, callback.callback(), NULL, - BoundNetLog()); + rv = resolver->Resolve( + HostResolver::RequestInfo(HostPortPair("chrome.net", 80)), + DEFAULT_PRIORITY, + &address_list, + callback.callback(), + NULL, + BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); EXPECT_EQ(OK, rv); @@ -196,18 +220,24 @@ TEST(MappedHostResolverTest, MapToError) { // Try resolving www.google.com --> Should give an error. TestCompletionCallback callback1; - rv = resolver->Resolve(HostResolver::RequestInfo( - HostPortPair("www.google.com", 80)), - &address_list, callback1.callback(), NULL, - BoundNetLog()); + rv = resolver->Resolve( + HostResolver::RequestInfo(HostPortPair("www.google.com", 80)), + DEFAULT_PRIORITY, + &address_list, + callback1.callback(), + NULL, + BoundNetLog()); EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv); // Try resolving www.foo.com --> Should succeed. TestCompletionCallback callback2; - rv = resolver->Resolve(HostResolver::RequestInfo( - HostPortPair("www.foo.com", 80)), - &address_list, callback2.callback(), NULL, - BoundNetLog()); + rv = resolver->Resolve( + HostResolver::RequestInfo(HostPortPair("www.foo.com", 80)), + DEFAULT_PRIORITY, + &address_list, + callback2.callback(), + NULL, + BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback2.WaitForResult(); EXPECT_EQ(OK, rv); diff --git a/chromium/net/dns/mdns_client.cc b/chromium/net/dns/mdns_client.cc index 631b01a706e..d0273c5784a 100644 --- a/chromium/net/dns/mdns_client.cc +++ b/chromium/net/dns/mdns_client.cc @@ -4,14 +4,43 @@ #include "net/dns/mdns_client.h" +#include "net/dns/dns_protocol.h" #include "net/dns/mdns_client_impl.h" namespace net { +namespace { + +const char kMDnsMulticastGroupIPv4[] = "224.0.0.251"; +const char kMDnsMulticastGroupIPv6[] = "FF02::FB"; + +IPEndPoint GetMDnsIPEndPoint(const char* address) { + IPAddressNumber multicast_group_number; + bool success = ParseIPLiteralToNumber(address, + &multicast_group_number); + DCHECK(success); + return IPEndPoint(multicast_group_number, + dns_protocol::kDefaultPortMulticast); +} + +} // namespace + // static scoped_ptr<MDnsClient> MDnsClient::CreateDefault() { return scoped_ptr<MDnsClient>( new MDnsClientImpl(MDnsConnection::SocketFactory::CreateDefault())); } +IPEndPoint GetMDnsIPEndPoint(AddressFamily address_family) { + switch (address_family) { + case ADDRESS_FAMILY_IPV4: + return GetMDnsIPEndPoint(kMDnsMulticastGroupIPv4); + case ADDRESS_FAMILY_IPV6: + return GetMDnsIPEndPoint(kMDnsMulticastGroupIPv6); + default: + NOTREACHED(); + return IPEndPoint(); + } +} + } // namespace net diff --git a/chromium/net/dns/mdns_client.h b/chromium/net/dns/mdns_client.h index f12c6e292cd..71006d69b01 100644 --- a/chromium/net/dns/mdns_client.h +++ b/chromium/net/dns/mdns_client.h @@ -9,6 +9,7 @@ #include <vector> #include "base/callback.h" +#include "net/base/ip_endpoint.h" #include "net/dns/dns_query.h" #include "net/dns/dns_response.h" #include "net/dns/record_parsed.h" @@ -154,5 +155,7 @@ class NET_EXPORT MDnsClient { static scoped_ptr<MDnsClient> CreateDefault(); }; +IPEndPoint NET_EXPORT GetMDnsIPEndPoint(AddressFamily address_family); + } // namespace net #endif // NET_DNS_MDNS_CLIENT_H_ diff --git a/chromium/net/dns/mdns_client_impl.cc b/chromium/net/dns/mdns_client_impl.cc index 8f79edf4cdf..1dfc21b37c6 100644 --- a/chromium/net/dns/mdns_client_impl.cc +++ b/chromium/net/dns/mdns_client_impl.cc @@ -24,8 +24,6 @@ namespace net { namespace { -const char kMDnsMulticastGroupIPv4[] = "224.0.0.251"; -const char kMDnsMulticastGroupIPv6[] = "FF02::FB"; const unsigned MDnsTransactionTimeoutSeconds = 3; } @@ -41,11 +39,6 @@ MDnsConnection::SocketHandler::~SocketHandler() { } int MDnsConnection::SocketHandler::Start() { - int rv = BindSocket(); - if (rv != OK) { - return rv; - } - return DoLoop(0); } @@ -87,7 +80,7 @@ void MDnsConnection::SocketHandler::SendDone(int rv) { // TODO(noamsml): Retry logic. } -int MDnsConnection::SocketHandler::BindSocket() { +int MDnsConnection::SocketHandler::Bind() { IPAddressNumber address_any(multicast_addr_.address().size()); IPEndPoint bind_endpoint(address_any, multicast_addr_.port()); @@ -102,41 +95,51 @@ int MDnsConnection::SocketHandler::BindSocket() { return socket_->JoinGroup(multicast_addr_.address()); } -MDnsConnection::MDnsConnection(MDnsConnection::SocketFactory* socket_factory, - MDnsConnection::Delegate* delegate) - : socket_handler_ipv4_(this, - GetMDnsIPEndPoint(kMDnsMulticastGroupIPv4), - socket_factory), - socket_handler_ipv6_(this, - GetMDnsIPEndPoint(kMDnsMulticastGroupIPv6), - socket_factory), +MDnsConnection::MDnsConnection(MDnsConnection::Delegate* delegate) : delegate_(delegate) { } MDnsConnection::~MDnsConnection() { } -int MDnsConnection::Init() { - int rv; +bool MDnsConnection::Init(MDnsConnection::SocketFactory* socket_factory) { + // TODO(vitalybuka): crbug.com/297690 Make socket_factory return list + // of initialized sockets. + socket_handlers_.push_back( + new SocketHandler(this, GetMDnsIPEndPoint(ADDRESS_FAMILY_IPV4), + socket_factory)); + socket_handlers_.push_back( + new SocketHandler(this, GetMDnsIPEndPoint(ADDRESS_FAMILY_IPV6), + socket_factory)); - rv = socket_handler_ipv4_.Start(); - if (rv != OK) return rv; - rv = socket_handler_ipv6_.Start(); - if (rv != OK) return rv; + for (size_t i = 0; i < socket_handlers_.size();) { + if (socket_handlers_[i]->Bind() != OK) { + socket_handlers_.erase(socket_handlers_.begin() + i); + } else { + ++i; + } + } - return OK; + // All unbound sockets need to be bound before processing untrusted input. + // This is done for security reasons, so that an attacker can't get an unbound + // socket. + for (size_t i = 0; i < socket_handlers_.size();) { + if (socket_handlers_[i]->Start() != OK) { + socket_handlers_.erase(socket_handlers_.begin() + i); + } else { + ++i; + } + } + return !socket_handlers_.empty(); } -int MDnsConnection::Send(IOBuffer* buffer, unsigned size) { - int rv; - - rv = socket_handler_ipv4_.Send(buffer, size); - if (rv < OK && rv != ERR_IO_PENDING) return rv; - - rv = socket_handler_ipv6_.Send(buffer, size); - if (rv < OK && rv != ERR_IO_PENDING) return rv; - - return OK; +bool MDnsConnection::Send(IOBuffer* buffer, unsigned size) { + bool success = false; + for (size_t i = 0; i < socket_handlers_.size(); ++i) { + int rv = socket_handlers_[i]->Send(buffer, size); + success = success || (rv >= OK || rv == ERR_IO_PENDING); + } + return success; } void MDnsConnection::OnError(SocketHandler* loop, @@ -146,15 +149,6 @@ void MDnsConnection::OnError(SocketHandler* loop, delegate_->OnConnectionError(error); } -IPEndPoint MDnsConnection::GetMDnsIPEndPoint(const char* address) { - IPAddressNumber multicast_group_number; - bool success = ParseIPLiteralToNumber(address, - &multicast_group_number); - DCHECK(success); - return IPEndPoint(multicast_group_number, - dns_protocol::kDefaultPortMulticast); -} - void MDnsConnection::OnDatagramReceived( DnsResponse* response, const IPEndPoint& recv_addr, @@ -192,17 +186,16 @@ MDnsConnection::SocketFactory::CreateDefault() { new MDnsConnectionSocketFactoryImpl); } -MDnsClientImpl::Core::Core(MDnsClientImpl* client, - MDnsConnection::SocketFactory* socket_factory) - : client_(client), connection_(new MDnsConnection(socket_factory, this)) { +MDnsClientImpl::Core::Core(MDnsClientImpl* client) + : client_(client), connection_(new MDnsConnection(this)) { } MDnsClientImpl::Core::~Core() { STLDeleteValues(&listeners_); } -bool MDnsClientImpl::Core::Init() { - return connection_->Init() == OK; +bool MDnsClientImpl::Core::Init(MDnsConnection::SocketFactory* socket_factory) { + return connection_->Init(socket_factory); } bool MDnsClientImpl::Core::SendQuery(uint16 rrtype, std::string name) { @@ -213,7 +206,7 @@ bool MDnsClientImpl::Core::SendQuery(uint16 rrtype, std::string name) { DnsQuery query(0, name_dns, rrtype); query.set_flags(0); // Remove the RD flag from the query. It is unneeded. - return connection_->Send(query.io_buffer(), query.io_buffer()->size()) == OK; + return connection_->Send(query.io_buffer(), query.io_buffer()->size()); } void MDnsClientImpl::Core::HandlePacket(DnsResponse* response, @@ -378,7 +371,7 @@ void MDnsClientImpl::Core::RemoveListener(MDnsListenerImpl* listener) { observer_list_iterator->second->RemoveObserver(listener); // Remove the observer list from the map if it is empty - if (observer_list_iterator->second->size() == 0) { + if (!observer_list_iterator->second->might_have_observers()) { // Schedule the actual removal for later in case the listener removal // happens while iterating over the observer list. base::MessageLoop::current()->PostTask( @@ -389,7 +382,7 @@ void MDnsClientImpl::Core::RemoveListener(MDnsListenerImpl* listener) { void MDnsClientImpl::Core::CleanupObserverList(const ListenerKey& key) { ListenerMap::iterator found = listeners_.find(key); - if (found != listeners_.end() && found->second->size() == 0) { + if (found != listeners_.end() && !found->second->might_have_observers()) { delete found->second; listeners_.erase(found); } @@ -442,8 +435,8 @@ MDnsClientImpl::~MDnsClientImpl() { bool MDnsClientImpl::StartListening() { DCHECK(!core_.get()); - core_.reset(new Core(this, socket_factory_.get())); - if (!core_->Init()) { + core_.reset(new Core(this)); + if (!core_->Init(socket_factory_.get())) { core_.reset(); return false; } diff --git a/chromium/net/dns/mdns_client_impl.h b/chromium/net/dns/mdns_client_impl.h index 9fe3f99e7dd..b69677bb747 100644 --- a/chromium/net/dns/mdns_client_impl.h +++ b/chromium/net/dns/mdns_client_impl.h @@ -11,6 +11,7 @@ #include <vector> #include "base/cancelable_callback.h" +#include "base/memory/scoped_vector.h" #include "base/observer_list.h" #include "net/base/io_buffer.h" #include "net/base/ip_endpoint.h" @@ -43,13 +44,13 @@ class NET_EXPORT_PRIVATE MDnsConnection { virtual ~Delegate() {} }; - explicit MDnsConnection(SocketFactory* socket_factory, - MDnsConnection::Delegate* delegate); + explicit MDnsConnection(MDnsConnection::Delegate* delegate); virtual ~MDnsConnection(); - int Init(); - int Send(IOBuffer* buffer, unsigned size); + // Both methods return true if at least one of the socket handlers succeeded. + bool Init(MDnsConnection::SocketFactory* socket_factory); + bool Send(IOBuffer* buffer, unsigned size); private: class SocketHandler { @@ -58,13 +59,13 @@ class NET_EXPORT_PRIVATE MDnsConnection { const IPEndPoint& multicast_addr, SocketFactory* socket_factory); ~SocketHandler(); - int DoLoop(int rv); + int Bind(); int Start(); int Send(IOBuffer* buffer, unsigned size); private: - int BindSocket(); + int DoLoop(int rv); void OnDatagramReceived(int rv); // Callback for when sending a query has finished. @@ -85,10 +86,8 @@ class NET_EXPORT_PRIVATE MDnsConnection { void OnError(SocketHandler* loop, int error); - IPEndPoint GetMDnsIPEndPoint(const char* address); - - SocketHandler socket_handler_ipv4_; - SocketHandler socket_handler_ipv6_; + // Only socket handlers which successfully bound and started are kept. + ScopedVector<SocketHandler> socket_handlers_; Delegate* delegate_; @@ -105,12 +104,11 @@ class NET_EXPORT_PRIVATE MDnsClientImpl : public MDnsClient { // invalidate the core. class Core : public base::SupportsWeakPtr<Core>, MDnsConnection::Delegate { public: - Core(MDnsClientImpl* client, - MDnsConnection::SocketFactory* socket_factory); + explicit Core(MDnsClientImpl* client); virtual ~Core(); // Initialize the core. Returns true on success. - bool Init(); + bool Init(MDnsConnection::SocketFactory* socket_factory); // Send a query with a specific rrtype and name. Returns true on success. bool SendQuery(uint16 rrtype, std::string name); diff --git a/chromium/net/dns/mdns_client_unittest.cc b/chromium/net/dns/mdns_client_unittest.cc index 324b4dfbee0..f524a5401c7 100644 --- a/chromium/net/dns/mdns_client_unittest.cc +++ b/chromium/net/dns/mdns_client_unittest.cc @@ -1025,24 +1025,17 @@ class SimpleMockSocketFactory } virtual scoped_ptr<DatagramServerSocket> CreateSocket() OVERRIDE { - scoped_ptr<MockMDnsDatagramServerSocket> socket( - new StrictMock<MockMDnsDatagramServerSocket>); - sockets_.push(socket.get()); - return socket.PassAs<DatagramServerSocket>(); + MockMDnsDatagramServerSocket* socket = sockets_.back(); + sockets_.weak_erase(sockets_.end() - 1); + return scoped_ptr<DatagramServerSocket>(socket); } - MockMDnsDatagramServerSocket* PopFirstSocket() { - MockMDnsDatagramServerSocket* socket = sockets_.front(); - sockets_.pop(); - return socket; - } - - size_t num_sockets() { - return sockets_.size(); + void PushSocket(MockMDnsDatagramServerSocket* socket) { + sockets_.push_back(socket); } private: - std::queue<MockMDnsDatagramServerSocket*> sockets_; + ScopedVector<MockMDnsDatagramServerSocket> sockets_; }; class MockMDnsConnectionDelegate : public MDnsConnection::Delegate { @@ -1058,16 +1051,16 @@ class MockMDnsConnectionDelegate : public MDnsConnection::Delegate { class MDnsConnectionTest : public ::testing::Test { public: - MDnsConnectionTest() : connection_(&factory_, &delegate_) { + MDnsConnectionTest() : connection_(&delegate_) { } protected: // Follow successful connection initialization. virtual void SetUp() OVERRIDE { - ASSERT_EQ(2u, factory_.num_sockets()); - - socket_ipv4_ = factory_.PopFirstSocket(); - socket_ipv6_ = factory_.PopFirstSocket(); + socket_ipv4_ = new MockMDnsDatagramServerSocket; + socket_ipv6_ = new MockMDnsDatagramServerSocket; + factory_.PushSocket(socket_ipv6_); + factory_.PushSocket(socket_ipv4_); } bool InitConnection() { @@ -1087,7 +1080,7 @@ class MDnsConnectionTest : public ::testing::Test { EXPECT_CALL(*socket_ipv6_, JoinGroupInternal("ff02::fb")) .WillOnce(Return(OK)); - return connection_.Init() == OK; + return connection_.Init(&factory_); } StrictMock<MockMDnsConnectionDelegate> delegate_; diff --git a/chromium/net/dns/mock_host_resolver.cc b/chromium/net/dns/mock_host_resolver.cc index b3d1489c9d7..25918ba3247 100644 --- a/chromium/net/dns/mock_host_resolver.cc +++ b/chromium/net/dns/mock_host_resolver.cc @@ -67,11 +67,13 @@ MockHostResolverBase::~MockHostResolverBase() { } int MockHostResolverBase::Resolve(const RequestInfo& info, + RequestPriority priority, AddressList* addresses, const CompletionCallback& callback, RequestHandle* handle, const BoundNetLog& net_log) { DCHECK(CalledOnValidThread()); + last_request_priority_ = priority; num_resolve_++; size_t id = next_request_id_++; int rv = ResolveFromIPLiteralOrCache(info, addresses); @@ -135,7 +137,8 @@ void MockHostResolverBase::ResolveAllPending() { // start id from 1 to distinguish from NULL RequestHandle MockHostResolverBase::MockHostResolverBase(bool use_caching) - : synchronous_mode_(false), + : last_request_priority_(DEFAULT_PRIORITY), + synchronous_mode_(false), ondemand_mode_(false), next_request_id_(1), num_resolve_(0), @@ -313,12 +316,14 @@ int RuleBasedHostResolverProc::Resolve(const std::string& host, bool matches_address_family = r->address_family == ADDRESS_FAMILY_UNSPECIFIED || r->address_family == address_family; + // Ignore HOST_RESOLVER_SYSTEM_ONLY, since it should have no impact on + // whether a rule matches. + HostResolverFlags flags = host_resolver_flags & ~HOST_RESOLVER_SYSTEM_ONLY; // Flags match if all of the bitflags in host_resolver_flags are enabled // in the rule's host_resolver_flags. However, the rule may have additional // flags specified, in which case the flags should still be considered a // match. - bool matches_flags = (r->host_resolver_flags & host_resolver_flags) == - host_resolver_flags; + bool matches_flags = (r->host_resolver_flags & flags) == flags; if (matches_flags && matches_address_family && MatchPattern(host, r->host_pattern)) { if (r->latency_ms != 0) { @@ -370,6 +375,7 @@ RuleBasedHostResolverProc* CreateCatchAllHostResolverProc() { //----------------------------------------------------------------------------- int HangingHostResolver::Resolve(const RequestInfo& info, + RequestPriority priority, AddressList* addresses, const CompletionCallback& callback, RequestHandle* out_req, diff --git a/chromium/net/dns/mock_host_resolver.h b/chromium/net/dns/mock_host_resolver.h index 282521cc3b9..f8a424011a9 100644 --- a/chromium/net/dns/mock_host_resolver.h +++ b/chromium/net/dns/mock_host_resolver.h @@ -75,6 +75,7 @@ class MockHostResolverBase : public HostResolver, // HostResolver methods: virtual int Resolve(const RequestInfo& info, + RequestPriority priority, AddressList* addresses, const CompletionCallback& callback, RequestHandle* out_req, @@ -104,6 +105,12 @@ class MockHostResolverBase : public HostResolver, return num_resolve_from_cache_; } + // Returns the RequestPriority of the last call to Resolve() (or + // DEFAULT_PRIORITY if Resolve() hasn't been called yet). + RequestPriority last_request_priority() const { + return last_request_priority_; + } + protected: explicit MockHostResolverBase(bool use_caching); @@ -120,6 +127,7 @@ class MockHostResolverBase : public HostResolver, // Resolve request stored in |requests_|. Pass rv to callback. void ResolveNow(size_t id); + RequestPriority last_request_priority_; bool synchronous_mode_; bool ondemand_mode_; scoped_refptr<RuleBasedHostResolverProc> rules_; @@ -246,6 +254,7 @@ RuleBasedHostResolverProc* CreateCatchAllHostResolverProc(); class HangingHostResolver : public HostResolver { public: virtual int Resolve(const RequestInfo& info, + RequestPriority priority, AddressList* addresses, const CompletionCallback& callback, RequestHandle* out_req, diff --git a/chromium/net/dns/single_request_host_resolver.cc b/chromium/net/dns/single_request_host_resolver.cc index 31ef4c56b6e..7974abafeef 100644 --- a/chromium/net/dns/single_request_host_resolver.cc +++ b/chromium/net/dns/single_request_host_resolver.cc @@ -25,9 +25,11 @@ SingleRequestHostResolver::~SingleRequestHostResolver() { Cancel(); } -int SingleRequestHostResolver::Resolve( - const HostResolver::RequestInfo& info, AddressList* addresses, - const CompletionCallback& callback, const BoundNetLog& net_log) { +int SingleRequestHostResolver::Resolve(const HostResolver::RequestInfo& info, + RequestPriority priority, + AddressList* addresses, + const CompletionCallback& callback, + const BoundNetLog& net_log) { DCHECK(addresses); DCHECK_EQ(false, callback.is_null()); DCHECK(cur_request_callback_.is_null()) << "resolver already in use"; @@ -40,7 +42,7 @@ int SingleRequestHostResolver::Resolve( callback.is_null() ? CompletionCallback() : callback_; int rv = resolver_->Resolve( - info, addresses, transient_callback, &request, net_log); + info, priority, addresses, transient_callback, &request, net_log); if (rv == ERR_IO_PENDING) { DCHECK_EQ(false, callback.is_null()); diff --git a/chromium/net/dns/single_request_host_resolver.h b/chromium/net/dns/single_request_host_resolver.h index 52d01328911..3c8ef020edc 100644 --- a/chromium/net/dns/single_request_host_resolver.h +++ b/chromium/net/dns/single_request_host_resolver.h @@ -5,10 +5,18 @@ #ifndef NET_DNS_SINGLE_REQUEST_HOST_RESOLVER_H_ #define NET_DNS_SINGLE_REQUEST_HOST_RESOLVER_H_ +#include "base/basictypes.h" + +#include "net/base/completion_callback.h" +#include "net/base/net_export.h" +#include "net/base/request_priority.h" #include "net/dns/host_resolver.h" namespace net { +class AddressList; +class BoundNetLog; + // This class represents the task of resolving a hostname (or IP address // literal) to an AddressList object. It wraps HostResolver to resolve only a // single hostname at a time and cancels this request when going out of scope. @@ -25,6 +33,7 @@ class NET_EXPORT SingleRequestHostResolver { // Resolves the given hostname (or IP address literal), filling out the // |addresses| object upon success. See HostResolver::Resolve() for details. int Resolve(const HostResolver::RequestInfo& info, + RequestPriority priority, AddressList* addresses, const CompletionCallback& callback, const BoundNetLog& net_log); diff --git a/chromium/net/dns/single_request_host_resolver_unittest.cc b/chromium/net/dns/single_request_host_resolver_unittest.cc index 1b0198f4fde..cc20bf3fe44 100644 --- a/chromium/net/dns/single_request_host_resolver_unittest.cc +++ b/chromium/net/dns/single_request_host_resolver_unittest.cc @@ -31,6 +31,7 @@ class HangingHostResolver : public HostResolver { } virtual int Resolve(const RequestInfo& info, + RequestPriority priority, AddressList* addresses, const CompletionCallback& callback, RequestHandle* out_req, @@ -77,7 +78,7 @@ TEST(SingleRequestHostResolverTest, NormalResolve) { TestCompletionCallback callback; HostResolver::RequestInfo request(HostPortPair("watsup", 90)); int rv = single_request_resolver.Resolve( - request, &addrlist, callback.callback(), BoundNetLog()); + request, DEFAULT_PRIORITY, &addrlist, callback.callback(), BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(OK, callback.WaitForResult()); @@ -97,8 +98,11 @@ TEST(SingleRequestHostResolverTest, Cancel) { AddressList addrlist; TestCompletionCallback callback; HostResolver::RequestInfo request(HostPortPair("watsup", 90)); - int rv = single_request_resolver.Resolve( - request, &addrlist, callback.callback(), BoundNetLog()); + int rv = single_request_resolver.Resolve(request, + DEFAULT_PRIORITY, + &addrlist, + callback.callback(), + BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_TRUE(resolver.has_outstanding_request()); } |