summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorszym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-07 23:21:57 +0000
committerszym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-07 23:21:57 +0000
commitfea484e58d81b67f8d52ee456b15cba75a555c8a (patch)
tree2dd96b20d40c8cfc1bdf27854eb3cd4f73543108 /net
parentb5827790ef97a92a783414f216e83bd98fcf68de (diff)
downloadchromium_src-fea484e58d81b67f8d52ee456b15cba75a555c8a.zip
chromium_src-fea484e58d81b67f8d52ee456b15cba75a555c8a.tar.gz
chromium_src-fea484e58d81b67f8d52ee456b15cba75a555c8a.tar.bz2
[net/dns] Suffix search list for DnsConfigService and related features.
Implements domain name devolution, search list policy and AppendToMultiLabelName. Also fixes behavior for single-label names with empty search list. BUG=99510,109902,109949 TEST=./net_unittests --gtest_filter=DnsConfigServiceWin* Review URL: http://codereview.chromium.org/9232059 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@120851 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/dns/dns_config_service.cc24
-rw-r--r--net/dns/dns_config_service.h6
-rw-r--r--net/dns/dns_config_service_posix.cc9
-rw-r--r--net/dns/dns_config_service_posix.h6
-rw-r--r--net/dns/dns_config_service_posix_unittest.cc10
-rw-r--r--net/dns/dns_config_service_win.cc440
-rw-r--r--net/dns/dns_config_service_win.h71
-rw-r--r--net/dns/dns_config_service_win_unittest.cc294
-rw-r--r--net/dns/dns_transaction.cc26
-rw-r--r--net/dns/dns_transaction_unittest.cc165
10 files changed, 809 insertions, 242 deletions
diff --git a/net/dns/dns_config_service.cc b/net/dns/dns_config_service.cc
index dcf58bd..d64b6735 100644
--- a/net/dns/dns_config_service.cc
+++ b/net/dns/dns_config_service.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -11,17 +11,19 @@ namespace net {
// Default values are taken from glibc resolv.h.
DnsConfig::DnsConfig()
- : ndots(1),
- timeout(base::TimeDelta::FromSeconds(5)),
- attempts(2),
- rotate(false),
- edns0(false) {}
+ : append_to_multi_label_name(true),
+ ndots(1),
+ timeout(base::TimeDelta::FromSeconds(5)),
+ attempts(2),
+ rotate(false),
+ edns0(false) {}
DnsConfig::~DnsConfig() {}
bool DnsConfig::EqualsIgnoreHosts(const DnsConfig& d) const {
return (nameservers == d.nameservers) &&
(search == d.search) &&
+ (append_to_multi_label_name == d.append_to_multi_label_name) &&
(ndots == d.ndots) &&
(timeout == d.timeout) &&
(attempts == d.attempts) &&
@@ -34,8 +36,8 @@ bool DnsConfig::Equals(const DnsConfig& d) const {
}
DnsConfigService::DnsConfigService()
- : have_config_(false),
- have_hosts_(false) {}
+ : have_config_(false),
+ have_hosts_(false) {}
DnsConfigService::~DnsConfigService() {}
@@ -77,9 +79,9 @@ void DnsConfigService::OnHostsRead(const DnsHosts& hosts) {
}
DnsHostsReader::DnsHostsReader(const FilePath& path, DnsConfigService* service)
- : path_(path),
- service_(service),
- success_(false) {
+ : path_(path),
+ service_(service),
+ success_(false) {
DCHECK(service);
}
diff --git a/net/dns/dns_config_service.h b/net/dns/dns_config_service.h
index 5fdd2b6..b3e151f 100644
--- a/net/dns/dns_config_service.h
+++ b/net/dns/dns_config_service.h
@@ -39,13 +39,15 @@ struct NET_EXPORT_PRIVATE DnsConfig {
std::vector<IPEndPoint> nameservers;
// Suffix search list; used on first lookup when number of dots in given name
// is less than |ndots|.
- // TODO(szym): Filter out duplicate entries from this list.
std::vector<std::string> search;
DnsHosts hosts;
+ // AppendToMultiLabelName: is suffix search performed for multi-label names?
+ // True, except on Windows where it can be configured.
+ bool append_to_multi_label_name;
+
// Resolver options; see man resolv.conf.
- // TODO(szym): implement DNS Devolution for windows
// Minimum number of dots before global resolution precedes |search|.
int ndots;
diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc
index f7833bc..18a69cc 100644
--- a/net/dns/dns_config_service_posix.cc
+++ b/net/dns/dns_config_service_posix.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -38,12 +38,12 @@ class DnsConfigServicePosix::ConfigReader : public SerialWorker {
// Note: res_ninit in glibc always returns 0 and sets RES_INIT.
// res_init behaves the same way.
if ((res_init() == 0) && (_res.options & RES_INIT)) {
- success_ = ConvertResToConfig(_res, &dns_config_);
+ success_ = ConvertResStateToDnsConfig(_res, &dns_config_);
}
#else
struct __res_state res;
if ((res_ninit(&res) == 0) && (res.options & RES_INIT)) {
- success_ = ConvertResToConfig(res, &dns_config_);
+ success_ = ConvertResStateToDnsConfig(res, &dns_config_);
}
#endif
#if defined(OS_MACOSX)
@@ -89,7 +89,8 @@ DnsConfigService* DnsConfigService::CreateSystemService() {
}
#if !defined(OS_ANDROID)
-bool ConvertResToConfig(const struct __res_state& res, DnsConfig* dns_config) {
+bool ConvertResStateToDnsConfig(const struct __res_state& res,
+ DnsConfig* dns_config) {
CHECK(dns_config != NULL);
DCHECK(res.options & RES_INIT);
diff --git a/net/dns/dns_config_service_posix.h b/net/dns/dns_config_service_posix.h
index 4fd48d2..fa9599e 100644
--- a/net/dns/dns_config_service_posix.h
+++ b/net/dns/dns_config_service_posix.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -36,8 +36,8 @@ class NET_EXPORT_PRIVATE DnsConfigServicePosix
};
// Fills in |dns_config| from |res|. Exposed for tests.
-bool NET_EXPORT_PRIVATE ConvertResToConfig(const struct __res_state& res,
- DnsConfig* dns_config);
+bool NET_EXPORT_PRIVATE ConvertResStateToDnsConfig(
+ const struct __res_state& res, DnsConfig* dns_config);
} // namespace net
diff --git a/net/dns/dns_config_service_posix_unittest.cc b/net/dns/dns_config_service_posix_unittest.cc
index ab9e56d..58c71ac 100644
--- a/net/dns/dns_config_service_posix_unittest.cc
+++ b/net/dns/dns_config_service_posix_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -53,7 +53,7 @@ void CompareConfig(const struct __res_state &res, const DnsConfig& config) {
}
// Fills in |res| with sane configuration. Change |generation| to add diversity.
-void InitializeResState(res_state res, int generation) {
+void InitializeResState(res_state res, unsigned generation) {
memset(res, 0, sizeof(*res));
res->options = RES_INIT | RES_ROTATE;
res->ndots = 2;
@@ -113,12 +113,12 @@ void CloseResState(res_state res) {
TEST(DnsConfigTest, ResolverConfigConvertAndEquals) {
struct __res_state res[2];
DnsConfig config[2];
- for (int i = 0; i < 2; ++i) {
+ for (unsigned i = 0; i < 2; ++i) {
EXPECT_FALSE(config[i].IsValid());
InitializeResState(&res[i], i);
- ASSERT_TRUE(ConvertResToConfig(res[i], &config[i]));
+ ASSERT_TRUE(ConvertResStateToDnsConfig(res[i], &config[i]));
}
- for (int i = 0; i < 2; ++i) {
+ for (unsigned i = 0; i < 2; ++i) {
EXPECT_TRUE(config[i].IsValid());
CompareConfig(res[i], config[i]);
CloseResState(&res[i]);
diff --git a/net/dns/dns_config_service_win.cc b/net/dns/dns_config_service_win.cc
index 93f72462..e089387 100644
--- a/net/dns/dns_config_service_win.cc
+++ b/net/dns/dns_config_service_win.cc
@@ -4,11 +4,11 @@
#include "net/dns/dns_config_service_win.h"
-#include <iphlpapi.h>
-#include <windows.h>
-
+#include <algorithm>
#include <string>
+#include "base/bind.h"
+#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/file_path.h"
#include "base/memory/scoped_ptr.h"
@@ -18,15 +18,96 @@
#include "base/utf_string_conversions.h"
#include "base/win/object_watcher.h"
#include "base/win/registry.h"
+#include "base/win/windows_version.h"
#include "googleurl/src/url_canon.h"
#include "net/base/net_util.h"
#include "net/dns/serial_worker.h"
#include "net/dns/watching_file_reader.h"
+#pragma comment(lib, "iphlpapi.lib")
+
namespace net {
+namespace {
+
+// Watches a single registry key for changes. This class is thread-safe with
+// the exception of StartWatch which must be called once and only once.
+class RegistryWatcher : public base::win::ObjectWatcher::Delegate {
+ public:
+ RegistryWatcher() {}
+
+ bool StartWatch(const wchar_t* key, const base::Closure& callback) {
+ DCHECK(!callback.is_null());
+ DCHECK(callback_.is_null());
+ // TODO(szym): This will need to change to address http://crbug.com/99509
+ DCHECK(!key_.Valid());
+ if (key_.Open(HKEY_LOCAL_MACHINE, key,
+ KEY_NOTIFY | KEY_QUERY_VALUE) != ERROR_SUCCESS)
+ return false;
+ if (key_.StartWatching() != ERROR_SUCCESS)
+ return false;
+ if (!watcher_.StartWatching(key_.watch_event(), this))
+ return false;
+ callback_ = callback;
+ return true;
+ }
+
+ void Cancel() {
+ callback_.Reset();
+ key_.StopWatching();
+ watcher_.StopWatching();
+ }
+
+ virtual void OnObjectSignaled(HANDLE object) OVERRIDE {
+ if (!callback_.is_null())
+ callback_.Run();
+ // TODO(szym): handle errors
+ bool success = key_.StartWatching() &&
+ watcher_.StartWatching(key_.watch_event(), this);
+ DCHECK(success);
+ }
+
+ bool ReadString(const wchar_t* name,
+ DnsSystemSettings::RegString* out) const {
+ out->set = false;
+ if (!key_.Valid()) {
+ // Assume that if the |key_| is invalid then the key is missing.
+ return true;
+ }
+ LONG result = key_.ReadValue(name, &out->value);
+ if (result == ERROR_SUCCESS) {
+ out->set = true;
+ return true;
+ }
+ return (result == ERROR_FILE_NOT_FOUND);
+ }
+
+ bool ReadDword(const wchar_t* name,
+ DnsSystemSettings::RegDword* out) const {
+ out->set = false;
+ if (!key_.Valid()) {
+ // Assume that if the |key_| is invalid then the key is missing.
+ return true;
+ }
+ LONG result = key_.ReadValueDW(name, &out->value);
+ if (result == ERROR_SUCCESS) {
+ out->set = true;
+ return true;
+ }
+ return (result == ERROR_FILE_NOT_FOUND);
+ }
+
+ private:
+ base::Closure callback_;
+ base::win::RegKey key_;
+ base::win::ObjectWatcher watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegistryWatcher);
+};
+
// Converts a string16 domain name to ASCII, possibly using punycode.
-// Returns true if the conversion succeeds.
+// Returns true if the conversion succeeds. In case of failure, |domain| might
+// become dirty.
bool ParseDomainASCII(const string16& widestr, std::string* domain) {
DCHECK(domain);
// Check if already ASCII.
@@ -38,117 +119,213 @@ bool ParseDomainASCII(const string16& widestr, std::string* domain) {
// Otherwise try to convert it from IDN to punycode.
const int kInitialBufferSize = 256;
url_canon::RawCanonOutputT<char16, kInitialBufferSize> punycode;
- if (!url_canon::IDNToASCII(widestr.data(),
- widestr.length(),
- &punycode)) {
+ if (!url_canon::IDNToASCII(widestr.data(), widestr.length(), &punycode)) {
return false;
}
// |punycode_output| should now be ASCII; convert it to a std::string.
// (We could use UTF16ToASCII() instead, but that requires an extra string
// copy. Since ASCII is a subset of UTF8 the following is equivalent).
- bool success = UTF16ToUTF8(punycode.data(),
- punycode.length(),
- domain);
+ bool success = UTF16ToUTF8(punycode.data(), punycode.length(), domain);
DCHECK(success);
DCHECK(IsStringASCII(*domain));
return success;
}
-bool ParseSearchList(const string16& value, std::vector<std::string>* out) {
- DCHECK(out);
+} // namespace
+
+bool ParseSearchList(const string16& value, std::vector<std::string>* output) {
+ DCHECK(output);
if (value.empty())
return false;
+ output->clear();
+
// If the list includes an empty hostname (",," or ", ,"), it is terminated.
// Although nslookup and network connection property tab ignore such
// fragments ("a,b,,c" becomes ["a", "b", "c"]), our reference is getaddrinfo
// (which sees ["a", "b"]). WMI queries also return a matching search list.
std::vector<string16> woutput;
base::SplitString(value, ',', &woutput);
- std::vector<std::string> output;
for (size_t i = 0; i < woutput.size(); ++i) {
- // Convert non-ascii to punycode, although getaddrinfo does not properly
+ // Convert non-ASCII to punycode, although getaddrinfo does not properly
// handle such suffixes.
const string16& t = woutput[i];
std::string parsed;
if (t.empty() || !ParseDomainASCII(t, &parsed))
break;
- output.push_back(parsed);
+ output->push_back(parsed);
}
- if (output.empty())
- return false;
- out->swap(output);
- return true;
+ return !output->empty();
}
-// Watches registry for changes and reads config from registry and IP helper.
-// Reading and opening of reg keys is always performed on WorkerPool. Setting
-// up watches requires IO loop.
-class DnsConfigServiceWin::ConfigReader : public SerialWorker {
- public:
- // Watches a single registry key for changes.
- class RegistryWatcher : public base::win::ObjectWatcher::Delegate {
- public:
- explicit RegistryWatcher(ConfigReader* reader) : reader_(reader) {}
-
- bool StartWatch(const wchar_t* key) {
- DCHECK(!key_.IsWatching());
- if (key_.Open(HKEY_LOCAL_MACHINE, key,
- KEY_NOTIFY | KEY_QUERY_VALUE) != ERROR_SUCCESS)
+bool ConvertSettingsToDnsConfig(const DnsSystemSettings& settings,
+ DnsConfig* config) {
+ *config = DnsConfig();
+
+ // Use GetAdapterAddresses to get effective DNS server order and
+ // connection-specific DNS suffix. Ignore disconnected and loopback adapters.
+ // TODO(szym): Also ignore VM adapters. http://crbug.com/112856
+ for (const IP_ADAPTER_ADDRESSES* adapter = settings.addresses.get();
+ adapter != NULL;
+ adapter = adapter->Next) {
+ if (adapter->OperStatus != IfOperStatusUp)
+ continue;
+ if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
+ continue;
+
+ for (const IP_ADAPTER_DNS_SERVER_ADDRESS* address =
+ adapter->FirstDnsServerAddress;
+ address != NULL;
+ address = address->Next) {
+ IPEndPoint ipe;
+ if (ipe.FromSockAddr(address->Address.lpSockaddr,
+ address->Address.iSockaddrLength)) {
+ config->nameservers.push_back(ipe);
+ } else {
return false;
- if (key_.StartWatching() != ERROR_SUCCESS)
- return false;
- if (!watcher_.StartWatching(key_.watch_event(), this))
- return false;
- return true;
+ }
}
- void Cancel() {
- reader_ = NULL;
- key_.StopWatching();
- watcher_.StopWatching();
+ // IP_ADAPTER_ADDRESSES in Vista+ has a search list at |FirstDnsSuffix|,
+ // but it came up empty in all trials.
+ // |DnsSuffix| stores the effective connection-specific suffix, which is
+ // obtained via DHCP (regkey: Tcpip\Parameters\Interfaces\{XXX}\DhcpDomain)
+ // or specified by the user (regkey: Tcpip\Parameters\Domain).
+ std::string dns_suffix;
+ if (ParseDomainASCII(adapter->DnsSuffix, &dns_suffix)) {
+ config->search.push_back(dns_suffix);
}
+ }
- void OnObjectSignaled(HANDLE object) OVERRIDE {
- if (reader_)
- reader_->WorkNow();
- // TODO(szym): handle errors
- key_.StartWatching();
- watcher_.StartWatching(key_.watch_event(), this);
+ if (config->nameservers.empty())
+ return false; // No point continuing.
+
+ // Windows always tries a multi-label name "as is" before using suffixes.
+ config->ndots = 1;
+
+ if (!settings.append_to_multi_label_name.set) {
+ // The default setting is true for XP, false for Vista+.
+ if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
+ config->append_to_multi_label_name = false;
+ } else {
+ config->append_to_multi_label_name = true;
}
+ } else {
+ config->append_to_multi_label_name =
+ (settings.append_to_multi_label_name.value != 0);
+ }
- bool Read(const wchar_t* name, string16* value) const {
- return key_.ReadValue(name, value) == ERROR_SUCCESS;
+ // 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 true;
+ }
+ // Even if invalid, the policy disables the user-specified setting below.
+ } else if (settings.tcpip_search_list.set) {
+ std::vector<std::string> search;
+ if (ParseSearchList(settings.tcpip_search_list.value, &search)) {
+ config->search.swap(search);
+ return true;
}
+ }
+
+ if (!settings.tcpip_domain.set)
+ return true;
- private:
- ConfigReader* reader_;
- base::win::RegKey key_;
- base::win::ObjectWatcher watcher_;
- };
+ std::string primary_suffix;
+ if (!ParseDomainASCII(settings.tcpip_domain.value, &primary_suffix))
+ return true; // No primary suffix, hence no devolution.
+
+ // Primary suffix goes in front.
+ config->search.insert(config->search.begin(), primary_suffix);
+
+ // 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 true; // 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 true; // 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 true;
+}
+
+// Watches registry for changes and reads config from registry and IP helper.
+// Reading and opening of reg keys is always performed on WorkerPool. Setting
+// up watches requires IO loop.
+class DnsConfigServiceWin::ConfigReader : public SerialWorker {
+ public:
explicit ConfigReader(DnsConfigServiceWin* service)
- : policy_watcher_(this),
- tcpip_watcher_(this),
- service_(service) {}
+ : service_(service),
+ success_(false) {}
bool StartWatch() {
DCHECK(loop()->BelongsToCurrentThread());
// This is done only once per lifetime so open the keys on this thread.
base::ThreadRestrictions::ScopedAllowIO allow_io;
- if (!tcpip_watcher_.StartWatch(
- L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"))
- return false;
- // DNS suffix search list can be installed via group policy which sets
- // this registry key. If the key is missing, the policy does not apply,
- // and the DNS client uses DHCP and manual settings.
+ base::Closure callback = base::Bind(&SerialWorker::WorkNow,
+ base::Unretained(this));
+
+ // DNS suffix search list and devolution can be configured via group
+ // policy which sets this registry key. If the key is missing, the policy
+ // does not apply, and the DNS client uses Tcpip and Dnscache settings.
// If a policy is installed, DnsConfigService will need to be restarted.
// BUG=99509
policy_watcher_.StartWatch(
- L"Software\\Policies\\Microsoft\\Windows NT\\DNSClient");
+ L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient",
+ callback);
+
+ // The Dnscache key might also be missing.
+ dnscache_watcher_.StartWatch(
+ L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters",
+ callback);
+
+ // The Tcpip key must be present.
+ if (!tcpip_watcher_.StartWatch(
+ L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
+ callback))
+ return false;
+
WorkNow();
return true;
}
@@ -157,6 +334,7 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker {
DCHECK(loop()->BelongsToCurrentThread());
SerialWorker::Cancel();
policy_watcher_.Cancel();
+ dnscache_watcher_.Cancel();
tcpip_watcher_.Cancel();
}
@@ -165,106 +343,96 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker {
private:
friend class RegistryWatcher;
- virtual ~ConfigReader() {}
+ virtual ~ConfigReader() {
+ DCHECK(IsCancelled());
+ }
- // Uses GetAdapterAddresses to get effective DNS server order and default
- // DNS suffix.
- bool ReadIpHelper(DnsConfig* config) {
+ bool ReadIpHelper(DnsSystemSettings* settings) {
base::ThreadRestrictions::AssertIOAllowed();
- ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_UNICAST |
- GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_FRIENDLY_NAME;
- IP_ADAPTER_ADDRESSES buf;
- IP_ADAPTER_ADDRESSES* adapters = &buf;
- ULONG len = sizeof(buf);
- scoped_array<char> extra_buf;
-
- UINT rv = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapters, &len);
- if (rv == ERROR_BUFFER_OVERFLOW) {
- extra_buf.reset(new char[len]);
- adapters = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(extra_buf.get());
- rv = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapters, &len);
+ ULONG flags = GAA_FLAG_SKIP_ANYCAST |
+ GAA_FLAG_SKIP_UNICAST |
+ GAA_FLAG_SKIP_MULTICAST |
+ GAA_FLAG_SKIP_FRIENDLY_NAME;
+ ULONG len = 15000; // As recommended by MSDN for GetAdaptersAddresses.
+ UINT rv = ERROR_BUFFER_OVERFLOW;
+ // Try up to three times.
+ for (unsigned tries = 0;
+ (tries < 3) && (rv == ERROR_BUFFER_OVERFLOW);
+ tries++) {
+ settings->addresses.reset(
+ reinterpret_cast<PIP_ADAPTER_ADDRESSES>(malloc(len)));
+ rv = GetAdaptersAddresses(AF_UNSPEC, flags, NULL,
+ settings->addresses.get(), &len);
}
- if (rv != NO_ERROR)
- return false;
-
- config->search.clear();
- config->nameservers.clear();
-
- IP_ADAPTER_ADDRESSES* adapter;
- for (adapter = adapters; adapter != NULL; adapter = adapter->Next) {
- if (adapter->OperStatus != IfOperStatusUp)
- continue;
- if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
- continue;
-
- // IP_ADAPTER_ADDRESSES in Vista+ has a search list at FirstDnsSuffix,
- // but it's only the per-interface search list. First initialize with
- // DnsSuffix, then override from registry.
- std::string dns_suffix;
- if (ParseDomainASCII(adapter->DnsSuffix, &dns_suffix)) {
- config->search.push_back(dns_suffix);
- }
- IP_ADAPTER_DNS_SERVER_ADDRESS* address;
- for (address = adapter->FirstDnsServerAddress; address != NULL;
- address = address->Next) {
- IPEndPoint ipe;
- if (ipe.FromSockAddr(address->Address.lpSockaddr,
- address->Address.iSockaddrLength)) {
- config->nameservers.push_back(ipe);
- } else {
- return false;
- }
- }
- }
- return true;
+ return (rv == NO_ERROR);
}
- // Read and parse SearchList from registry. Only accept the value if all
- // elements of the search list are non-empty and can be converted to UTF-8.
- bool ReadSearchList(const RegistryWatcher& watcher, DnsConfig* config) {
- string16 value;
- if (!watcher.Read(L"SearchList", &value))
- return false;
- return ParseSearchList(value, &(config->search));
+ bool ReadDevolutionSetting(const RegistryWatcher& watcher,
+ DnsSystemSettings::DevolutionSetting& setting) {
+ return watcher.ReadDword(L"UseDomainNameDevolution", &setting.enabled) &&
+ watcher.ReadDword(L"DomainNameDevolutionLevel", &setting.level);
}
- void DoWork() OVERRIDE {
+ virtual void DoWork() OVERRIDE {
// Should be called on WorkerPool.
- dns_config_ = DnsConfig();
- if (!ReadIpHelper(&dns_config_))
+ success_ = false;
+
+ DnsSystemSettings settings;
+ memset(&settings, 0, sizeof(settings));
+ if (!ReadIpHelper(&settings))
return; // no point reading the rest
- // check global registry overrides
- if (!ReadSearchList(policy_watcher_, &dns_config_))
- ReadSearchList(tcpip_watcher_, &dns_config_);
+ if (!policy_watcher_.ReadString(L"SearchList",
+ &settings.policy_search_list))
+ return;
+
+ if (!tcpip_watcher_.ReadString(L"SearchList", &settings.tcpip_search_list))
+ return;
+
+ if (!tcpip_watcher_.ReadString(L"Domain", &settings.tcpip_domain))
+ return;
+
+ if (!ReadDevolutionSetting(policy_watcher_, settings.policy_devolution))
+ return;
+
+ if (!ReadDevolutionSetting(dnscache_watcher_,
+ settings.dnscache_devolution))
+ return;
- // TODO(szym): add support for DNS suffix devolution BUG=99510
- // TODO(szym): read AppendToMultiLabelName to determine ndots BUG=109902
+ if (!ReadDevolutionSetting(tcpip_watcher_, settings.tcpip_devolution))
+ return;
+
+ if (!policy_watcher_.ReadDword(L"AppendToMultiLabelName",
+ &settings.append_to_multi_label_name))
+ return;
+
+ success_ = ConvertSettingsToDnsConfig(settings, &dns_config_);
}
- void OnWorkFinished() OVERRIDE {
+ virtual void OnWorkFinished() OVERRIDE {
DCHECK(loop()->BelongsToCurrentThread());
DCHECK(!IsCancelled());
- if (dns_config_.IsValid()) {
+ if (success_) {
service_->OnConfigRead(dns_config_);
} else {
- LOG(WARNING) << "Failed to read name servers from registry";
+ LOG(WARNING) << "Failed to read config.";
}
}
+ DnsConfigServiceWin* service_;
// Written in DoRead(), read in OnReadFinished(). No locking required.
DnsConfig dns_config_;
+ bool success_;
RegistryWatcher policy_watcher_;
+ RegistryWatcher dnscache_watcher_;
RegistryWatcher tcpip_watcher_;
-
- DnsConfigServiceWin* service_;
};
DnsConfigServiceWin::DnsConfigServiceWin()
- : config_reader_(new ConfigReader(this)),
- hosts_watcher_(new WatchingFileReader()) {}
+ : config_reader_(new ConfigReader(this)),
+ hosts_watcher_(new WatchingFileReader()) {}
DnsConfigServiceWin::~DnsConfigServiceWin() {
DCHECK(CalledOnValidThread());
diff --git a/net/dns/dns_config_service_win.h b/net/dns/dns_config_service_win.h
index 97dcf6c..d60ae04 100644
--- a/net/dns/dns_config_service_win.h
+++ b/net/dns/dns_config_service_win.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,6 +6,11 @@
#define NET_DNS_DNS_CONFIG_SERVICE_WIN_H_
#pragma once
+// The sole purpose of dns_config_service_win.h is for unittests so we just
+// include these headers here.
+#include <winsock2.h>
+#include <iphlpapi.h>
+
#include <string>
#include <vector>
@@ -15,6 +20,17 @@
#include "net/base/net_export.h"
#include "net/dns/dns_config_service.h"
+// The general effort of DnsConfigServiceWin is to configure |nameservers| and
+// |search| in DnsConfig. The settings are stored in the Windows registry, but
+// to simplify the task we use the IP Helper API wherever possible. That API
+// yields the complete and ordered |nameservers|, but to determine |search| we
+// need to use the registry. On Windows 7, WMI does return the correct |search|
+// but on earlier versions it is insufficient.
+//
+// Experimental evaluation of Windows behavior suggests that domain parsing is
+// naive. Domain suffixes in |search| are not validated until they are appended
+// to the resolved name. We attempt to replicate this behavior.
+
namespace net {
class WatchingFileReader;
@@ -35,13 +51,60 @@ class NET_EXPORT_PRIVATE DnsConfigServiceWin
DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceWin);
};
+// All relevant settings read from registry and IP Helper. This isolates our
+// logic from system calls and is exposed for unit tests. Keep it an aggregate
+// struct for easy initialization.
+struct NET_EXPORT_PRIVATE DnsSystemSettings {
+ // The |set| flag distinguishes between empty and unset values.
+ struct RegString {
+ bool set;
+ string16 value;
+ };
+
+ struct RegDword {
+ bool set;
+ DWORD value;
+ };
+
+ struct DevolutionSetting {
+ // UseDomainNameDevolution
+ RegDword enabled;
+ // DomainNameDevolutionLevel
+ RegDword level;
+ };
+
+ // Filled in by GetAdapterAddresses.
+ scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> addresses;
+
+ // SOFTWARE\Policies\Microsoft\Windows NT\DNSClient\SearchList
+ RegString policy_search_list;
+ // SYSTEM\CurrentControlSet\Tcpip\Parameters\SearchList
+ RegString tcpip_search_list;
+ // SYSTEM\CurrentControlSet\Tcpip\Parameters\Domain
+ RegString tcpip_domain;
+
+ // SOFTWARE\Policies\Microsoft\Windows NT\DNSClient
+ DevolutionSetting policy_devolution;
+ // SYSTEM\CurrentControlSet\Dnscache\Parameters
+ DevolutionSetting dnscache_devolution;
+ // SYSTEM\CurrentControlSet\Tcpip\Parameters
+ DevolutionSetting tcpip_devolution;
+
+ // SOFTWARE\Policies\Microsoft\Windows NT\DNSClient\AppendToMultiLabelName
+ RegDword append_to_multi_label_name;
+};
+
// 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 are converted to punycode.
+// 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
+// are converted to punycode.
bool NET_EXPORT_PRIVATE ParseSearchList(const string16& value,
std::vector<std::string>* out);
+// Fills in |dns_config| from |settings|. Exposed for tests.
+bool NET_EXPORT_PRIVATE ConvertSettingsToDnsConfig(
+ const DnsSystemSettings& settings, DnsConfig* dns_config);
+
} // namespace net
#endif // NET_DNS_DNS_CONFIG_SERVICE_WIN_H_
diff --git a/net/dns/dns_config_service_win_unittest.cc b/net/dns/dns_config_service_win_unittest.cc
index 6397c32..917656b 100644
--- a/net/dns/dns_config_service_win_unittest.cc
+++ b/net/dns/dns_config_service_win_unittest.cc
@@ -1,16 +1,17 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/dns/dns_config_service_win.h"
+#include "base/win/windows_version.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
-TEST(DnsConfigServiceWinTest, ParseDomain) {
+TEST(DnsConfigServiceWinTest, ParseSearchList) {
const struct TestCase {
const wchar_t* input;
const char* output[4]; // NULL-terminated, empty if expected false
@@ -25,26 +26,301 @@ TEST(DnsConfigServiceWinTest, ParseDomain) {
// Empty search list is invalid
{ L"", { NULL } },
{ L",,", { NULL } },
- { NULL, { NULL } },
};
std::vector<std::string> actual_output, expected_output;
- for (const TestCase* t = cases; t->input; ++t) {
+ for (unsigned i = 0; i < arraysize(cases); ++i) {
+ const TestCase& t = cases[i];
actual_output.clear();
actual_output.push_back("UNSET");
expected_output.clear();
- for (const char* const* output = t->output; *output; ++output) {
+ for (const char* const* output = t.output; *output; ++output) {
expected_output.push_back(*output);
}
- bool result = ParseSearchList(t->input, &actual_output);
+ bool result = ParseSearchList(t.input, &actual_output);
if (!expected_output.empty()) {
EXPECT_TRUE(result);
EXPECT_EQ(expected_output, actual_output);
} else {
- EXPECT_FALSE(result) << "Unexpected parse success on " << t->input;
- expected_output.push_back("UNSET");
- EXPECT_EQ(expected_output, actual_output);
+ EXPECT_FALSE(result) << "Unexpected parse success on " << t.input;
+ }
+ }
+}
+
+struct AdapterInfo {
+ IFTYPE if_type;
+ IF_OPER_STATUS oper_status;
+ PWCHAR dns_suffix;
+ std::string dns_server_addresses[4]; // Empty string indicates end.
+};
+
+scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> CreateAdapterAddresses(
+ const AdapterInfo* infos) {
+ size_t num_adapters = 0;
+ size_t num_addresses = 0;
+ for (size_t i = 0; infos[i].if_type; ++i) {
+ ++num_adapters;
+ for (size_t j = 0; !infos[i].dns_server_addresses[j].empty(); ++j) {
+ ++num_addresses;
+ }
+ }
+
+ size_t heap_size = num_adapters * sizeof(IP_ADAPTER_ADDRESSES) +
+ num_addresses * (sizeof(IP_ADAPTER_DNS_SERVER_ADDRESS) +
+ sizeof(struct sockaddr_storage));
+ scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> heap(
+ reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(heap_size)));
+ CHECK(heap.get());
+ memset(heap.get(), 0, heap_size);
+
+ IP_ADAPTER_ADDRESSES* adapters = heap.get();
+ IP_ADAPTER_DNS_SERVER_ADDRESS* addresses =
+ reinterpret_cast<IP_ADAPTER_DNS_SERVER_ADDRESS*>(adapters + num_adapters);
+ struct sockaddr_storage* storage =
+ reinterpret_cast<struct sockaddr_storage*>(addresses + num_addresses);
+
+ for (size_t i = 0; i < num_adapters; ++i) {
+ const AdapterInfo& info = infos[i];
+ IP_ADAPTER_ADDRESSES* adapter = adapters + i;
+ if (i + 1 < num_adapters)
+ adapter->Next = adapter + 1;
+ adapter->IfType = info.if_type;
+ adapter->OperStatus = info.oper_status;
+ adapter->DnsSuffix = info.dns_suffix;
+ IP_ADAPTER_DNS_SERVER_ADDRESS* address;
+ for (size_t j = 0; !info.dns_server_addresses[j].empty(); ++j) {
+ --num_addresses;
+ if (j == 0) {
+ address = adapter->FirstDnsServerAddress = addresses + num_addresses;
+ } else {
+ address = address->Next = address + 1;
+ }
+ IPAddressNumber ip;
+ CHECK(ParseIPLiteralToNumber(info.dns_server_addresses[j], &ip));
+ IPEndPoint ipe(ip, 53);
+ address->Address.lpSockaddr =
+ reinterpret_cast<LPSOCKADDR>(storage + num_addresses);
+ size_t length = sizeof(struct sockaddr_storage);
+ CHECK(ipe.ToSockAddr(address->Address.lpSockaddr, &length));
+ address->Address.iSockaddrLength = static_cast<int>(length);
+ }
+ }
+
+ return heap.Pass();
+}
+
+TEST(DnsConfigServiceWinTest, ConvertAdaptersAddresses) {
+ // Check nameservers and connection-specific suffix.
+ const struct TestCase {
+ AdapterInfo input_adapters[4]; // |if_type| == 0 indicates end.
+ std::string expected_nameservers[4]; // Empty string indicates end.
+ std::string expected_suffix;
+ } cases[] = {
+ { // Ignore loopback and inactive adapters.
+ {
+ { IF_TYPE_SOFTWARE_LOOPBACK, IfOperStatusUp, L"funnyloop",
+ { "2.0.0.2" } },
+ { IF_TYPE_FASTETHER, IfOperStatusDormant, L"example.com",
+ { "1.0.0.1" } },
+ { IF_TYPE_USB, IfOperStatusUp, L"chromium.org",
+ { "10.0.0.10", "2001:FFFF::1111" } },
+ { 0 },
+ },
+ { "10.0.0.10", "2001:FFFF::1111" },
+ "chromium.org",
+ },
+ { // No usable nameservers.
+ {
+ { IF_TYPE_SOFTWARE_LOOPBACK, IfOperStatusUp, L"localhost",
+ { "2.0.0.2" } },
+ { IF_TYPE_FASTETHER, IfOperStatusDormant, L"example.com",
+ { "1.0.0.1" } },
+ { IF_TYPE_USB, IfOperStatusUp, L"chromium.org" },
+ { 0 },
+ },
+ },
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ const TestCase& t = cases[i];
+ DnsSystemSettings settings = {
+ CreateAdapterAddresses(t.input_adapters),
+ // Default settings for the rest.
+ };
+ std::vector<IPEndPoint> expected_nameservers;
+ for (size_t j = 0; !t.expected_nameservers[j].empty(); ++j) {
+ IPAddressNumber ip;
+ ASSERT_TRUE(ParseIPLiteralToNumber(t.expected_nameservers[j], &ip));
+ expected_nameservers.push_back(IPEndPoint(ip, 53));
+ }
+
+ DnsConfig config;
+ bool result = ConvertSettingsToDnsConfig(settings, &config);
+ bool expected_result = !expected_nameservers.empty();
+ ASSERT_EQ(expected_result, result);
+ EXPECT_EQ(expected_nameservers, config.nameservers);
+ if (result) {
+ ASSERT_EQ(1u, config.search.size());
+ EXPECT_EQ(t.expected_suffix, config.search[0]);
+ }
+ }
+}
+
+TEST(DnsConfigServiceWinTest, ConvertSuffixSearch) {
+ AdapterInfo infos[2] = {
+ { IF_TYPE_USB, IfOperStatusUp, L"connection.suffix", { "1.0.0.1" } },
+ { 0 },
+ };
+
+ const struct TestCase {
+ DnsSystemSettings input_settings;
+ std::string expected_search[5];
+ } cases[] = {
+ { // Policy SearchList override.
+ {
+ CreateAdapterAddresses(infos),
+ { true, L"policy.searchlist.a,policy.searchlist.b" },
+ { true, L"tcpip.searchlist.a,tcpip.searchlist.b" },
+ { true, L"tcpip.domain" },
+ },
+ { "policy.searchlist.a", "policy.searchlist.b" },
+ },
+ { // User-specified SearchList override.
+ {
+ CreateAdapterAddresses(infos),
+ { false },
+ { true, L"tcpip.searchlist.a,tcpip.searchlist.b" },
+ { true, L"tcpip.domain" },
+ },
+ { "tcpip.searchlist.a", "tcpip.searchlist.b" },
+ },
+ { // Void SearchList.
+ {
+ CreateAdapterAddresses(infos),
+ { true, L",bad.searchlist,parsed.as.empty" },
+ { true, L"tcpip.searchlist,good.but.overridden" },
+ { true, L"tcpip.domain" },
+ },
+ { "tcpip.domain", "connection.suffix" },
+ },
+ { // Devolution enabled by policy, level by dnscache.
+ {
+ CreateAdapterAddresses(infos),
+ { false },
+ { false },
+ { true, L"a.b.c.d.e" },
+ { { true, 1 }, { false } },
+ { { true, 0 }, { true, 3 } },
+ { { true, 0 }, { true, 1 } },
+ },
+ { "a.b.c.d.e", "connection.suffix", "b.c.d.e", "c.d.e" },
+ },
+ { // Devolution enabled by dnscache, level by policy.
+ {
+ CreateAdapterAddresses(infos),
+ { false },
+ { false },
+ { true, L"a.b.c.d.e" },
+ { { false }, { true, 4 } },
+ { { true, 1 }, { false } },
+ { { true, 0 }, { true, 3 } },
+ },
+ { "a.b.c.d.e", "connection.suffix", "b.c.d.e" },
+ },
+ { // Devolution enabled by default.
+ {
+ CreateAdapterAddresses(infos),
+ { false },
+ { false },
+ { true, L"a.b.c.d.e" },
+ { { false }, { false } },
+ { { false }, { true, 3 } },
+ { { false }, { true, 1 } },
+ },
+ { "a.b.c.d.e", "connection.suffix", "b.c.d.e", "c.d.e" },
+ },
+ { // Devolution disabled when no explicit level.
+ // Windows XP and Vista use a default level = 2, but we don't.
+ {
+ CreateAdapterAddresses(infos),
+ { false },
+ { false },
+ { true, L"a.b.c.d.e" },
+ { { true, 1 }, { false } },
+ { { true, 1 }, { false } },
+ { { true, 1 }, { false } },
+ },
+ { "a.b.c.d.e", "connection.suffix" },
+ },
+ { // Devolution disabled by policy level.
+ {
+ CreateAdapterAddresses(infos),
+ { false },
+ { false },
+ { true, L"a.b.c.d.e" },
+ { { false }, { true, 1 } },
+ { { true, 1 }, { true, 3 } },
+ { { true, 1 }, { true, 4 } },
+ },
+ { "a.b.c.d.e", "connection.suffix" },
+ },
+ { // Devolution disabled by user setting.
+ {
+ CreateAdapterAddresses(infos),
+ { false },
+ { false },
+ { true, L"a.b.c.d.e" },
+ { { false }, { true, 3 } },
+ { { false }, { true, 3 } },
+ { { true, 0 }, { true, 3 } },
+ },
+ { "a.b.c.d.e", "connection.suffix" },
+ },
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ const TestCase& t = cases[i];
+ DnsConfig config;
+ ASSERT_TRUE(ConvertSettingsToDnsConfig(t.input_settings, &config));
+ std::vector<std::string> expected_search;
+ for (size_t j = 0; !t.expected_search[j].empty(); ++j) {
+ expected_search.push_back(t.expected_search[j]);
}
+ EXPECT_EQ(expected_search, config.search);
+ }
+}
+
+TEST(DnsConfigServiceWinTest, AppendToMultiLabelName) {
+ AdapterInfo infos[2] = {
+ { IF_TYPE_USB, IfOperStatusUp, L"connection.suffix", { "1.0.0.1" } },
+ { 0 },
+ };
+
+ // The default setting was true pre-Vista.
+ bool default_value = (base::win::GetVersion() < base::win::VERSION_VISTA);
+
+ const struct TestCase {
+ DnsSystemSettings::RegDword input;
+ bool expected_output;
+ } cases[] = {
+ { { true, 0 }, false },
+ { { true, 1 }, true },
+ { { false, 0 }, default_value },
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ const TestCase& t = cases[i];
+ DnsSystemSettings settings = {
+ CreateAdapterAddresses(infos),
+ { false }, { false }, { false },
+ { { false }, { false } },
+ { { false }, { false } },
+ { { false }, { false } },
+ t.input,
+ };
+ DnsConfig config;
+ ASSERT_TRUE(ConvertSettingsToDnsConfig(settings, &config));
+ EXPECT_EQ(config.append_to_multi_label_name, t.expected_output);
}
}
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index 8222863..6cd02a3 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -308,22 +308,28 @@ class DnsTransactionImpl : public DnsTransaction, public base::NonThreadSafe {
int PrepareSearch() {
const DnsConfig& config = session_->config();
- std::string labelled_hostname;
- if (!DNSDomainFromDot(hostname_, &labelled_hostname))
+ std::string labeled_hostname;
+ if (!DNSDomainFromDot(hostname_, &labeled_hostname))
return ERR_INVALID_ARGUMENT;
if (hostname_[hostname_.size() - 1] == '.') {
// It's a fully-qualified name, no suffix search.
- qnames_.push_back(labelled_hostname);
+ qnames_.push_back(labeled_hostname);
return OK;
}
- // Set true when |labelled_hostname| is put on the list.
+ int ndots = CountLabels(labeled_hostname) - 1;
+
+ if (ndots > 0 && !config.append_to_multi_label_name) {
+ qnames_.push_back(labeled_hostname);
+ return OK;
+ }
+
+ // Set true when |labeled_hostname| is put on the list.
bool had_hostname = false;
- int ndots = CountLabels(labelled_hostname) - 1;
if (ndots >= config.ndots) {
- qnames_.push_back(labelled_hostname);
+ qnames_.push_back(labeled_hostname);
had_hostname = true;
}
@@ -332,7 +338,7 @@ class DnsTransactionImpl : public DnsTransaction, public base::NonThreadSafe {
// Ignore invalid (too long) combinations.
if (!DNSDomainFromDot(hostname_ + "." + config.search[i], &qname))
continue;
- if (qname.size() == labelled_hostname.size()) {
+ if (qname.size() == labeled_hostname.size()) {
if (had_hostname)
continue;
had_hostname = true;
@@ -340,10 +346,10 @@ class DnsTransactionImpl : public DnsTransaction, public base::NonThreadSafe {
qnames_.push_back(qname);
}
- if (!had_hostname)
- qnames_.push_back(labelled_hostname);
+ if (ndots > 0 && !had_hostname)
+ qnames_.push_back(labeled_hostname);
- return OK;
+ return qnames_.empty() ? ERR_NAME_NOT_RESOLVED : OK;
}
void DoCallback(int rv, const DnsUDPAttempt* successful_attempt) {
diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc
index 7c7e9e4..d3eadb9 100644
--- a/net/dns/dns_transaction_unittest.cc
+++ b/net/dns/dns_transaction_unittest.cc
@@ -117,6 +117,7 @@ class TransactionHelper {
if (rv != ERR_IO_PENDING) {
EXPECT_NE(OK, rv);
EXPECT_EQ(expected_answer_count_, rv);
+ completed_ = true;
}
}
@@ -162,6 +163,22 @@ class TransactionHelper {
return completed_;
}
+ // Shorthands for commonly used commands.
+
+ bool Run(DnsTransactionFactory* factory) {
+ StartTransaction(factory);
+ MessageLoop::current()->RunAllPending();
+ return has_completed();
+ }
+
+ // Use when some of the responses are timeouts.
+ bool RunUntilDone(DnsTransactionFactory* factory) {
+ set_quit_in_callback();
+ StartTransaction(factory);
+ MessageLoop::current()->Run();
+ return has_completed();
+ }
+
private:
std::string hostname_;
uint16 qtype_;
@@ -179,7 +196,7 @@ class DnsTransactionTest : public testing::Test {
// Generates |nameservers| for DnsConfig.
void ConfigureNumServers(unsigned num_servers) {
- DCHECK_LT(num_servers, 255u);
+ CHECK_LE(num_servers, 255u);
config_.nameservers.clear();
IPAddressNumber dns_ip;
{
@@ -214,7 +231,7 @@ class DnsTransactionTest : public testing::Test {
uint16 id,
const char* data,
size_t data_length) {
- DCHECK(socket_factory_);
+ CHECK(socket_factory_);
DnsQuery* query = new DnsQuery(id, DomainFromDot(dotted_name), qtype);
queries_.push_back(query);
@@ -234,7 +251,7 @@ class DnsTransactionTest : public testing::Test {
// Add expected query of |dotted_name| and |qtype| and no response.
void AddTimeout(const char* dotted_name, uint16 qtype) {
- DCHECK(socket_factory_);
+ CHECK(socket_factory_);
uint16 id = base::RandInt(0, kuint16max);
DnsQuery* query = new DnsQuery(id, DomainFromDot(dotted_name), qtype);
queries_.push_back(query);
@@ -250,8 +267,8 @@ class DnsTransactionTest : public testing::Test {
// Add expected query of |dotted_name| and |qtype| and response with no answer
// and rcode set to |rcode|.
void AddRcode(const char* dotted_name, uint16 qtype, int rcode) {
- DCHECK(socket_factory_);
- DCHECK_NE(dns_protocol::kRcodeNOERROR, rcode);
+ CHECK(socket_factory_);
+ CHECK_NE(dns_protocol::kRcodeNOERROR, rcode);
uint16 id = base::RandInt(0, kuint16max);
DnsQuery* query = new DnsQuery(id, DomainFromDot(dotted_name), qtype);
queries_.push_back(query);
@@ -277,12 +294,13 @@ class DnsTransactionTest : public testing::Test {
// This separation is necessary because the |reads_| and |writes_| vectors
// could reallocate their data during those calls.
void PrepareSockets() {
- DCHECK_EQ(writes_.size(), reads_.size());
+ CHECK_EQ(writes_.size(), reads_.size());
for (size_t i = 0; i < writes_.size(); ++i) {
- SocketDataProvider *data;
+ DelayedSocketData* data;
if (reads_[i].data) {
data = new DelayedSocketData(1, &reads_[i], 1, &writes_[i], 1);
} else {
+ // Timeout.
data = new DelayedSocketData(2, NULL, 0, &writes_[i], 1);
}
socket_data_.push_back(data);
@@ -310,6 +328,13 @@ class DnsTransactionTest : public testing::Test {
ConfigureFactory();
}
+ void TearDown() OVERRIDE {
+ // Check that all socket data was at least written to.
+ for (size_t i = 0; i < socket_data_.size(); ++i) {
+ EXPECT_GT(socket_data_[i]->write_index(), 0u);
+ }
+ }
+
protected:
int GetNextId(int min, int max) {
EXPECT_FALSE(transaction_ids_.empty());
@@ -331,7 +356,7 @@ class DnsTransactionTest : public testing::Test {
std::vector<MockWrite> writes_;
// Holder for the socket data (MockClientSocketFactory does not own it).
- ScopedVector<SocketDataProvider> socket_data_;
+ ScopedVector<DelayedSocketData> socket_data_;
std::deque<int> transaction_ids_;
// Owned by |session_|.
@@ -351,12 +376,7 @@ TEST_F(DnsTransactionTest, Lookup) {
TransactionHelper helper0(kT0HostName,
kT0Qtype,
arraysize(kT0IpAddresses) + 1);
- helper0.StartTransaction(transaction_factory_.get());
-
- // Wait until result.
- MessageLoop::current()->RunAllPending();
-
- EXPECT_TRUE(helper0.has_completed());
+ EXPECT_TRUE(helper0.Run(transaction_factory_.get()));
}
// Concurrent lookup tests assume that DnsTransaction::Start immediately
@@ -452,11 +472,7 @@ TEST_F(DnsTransactionTest, CancelFromCallback) {
kT0Qtype,
arraysize(kT0IpAddresses) + 1);
helper0.set_cancel_in_callback();
- helper0.StartTransaction(transaction_factory_.get());
-
- MessageLoop::current()->RunAllPending();
-
- EXPECT_TRUE(helper0.has_completed());
+ EXPECT_TRUE(helper0.Run(transaction_factory_.get()));
}
TEST_F(DnsTransactionTest, ServerFail) {
@@ -466,11 +482,7 @@ TEST_F(DnsTransactionTest, ServerFail) {
TransactionHelper helper0(kT0HostName,
kT0Qtype,
ERR_DNS_SERVER_FAILED);
- helper0.StartTransaction(transaction_factory_.get());
-
- MessageLoop::current()->RunAllPending();
-
- EXPECT_TRUE(helper0.has_completed());
+ EXPECT_TRUE(helper0.Run(transaction_factory_.get()));
}
TEST_F(DnsTransactionTest, NoDomain) {
@@ -480,11 +492,7 @@ TEST_F(DnsTransactionTest, NoDomain) {
TransactionHelper helper0(kT0HostName,
kT0Qtype,
ERR_NAME_NOT_RESOLVED);
- helper0.StartTransaction(transaction_factory_.get());
-
- MessageLoop::current()->RunAllPending();
-
- EXPECT_TRUE(helper0.has_completed());
+ EXPECT_TRUE(helper0.Run(transaction_factory_.get()));
}
TEST_F(DnsTransactionTest, Timeout) {
@@ -502,12 +510,7 @@ TEST_F(DnsTransactionTest, Timeout) {
TransactionHelper helper0(kT0HostName,
kT0Qtype,
ERR_DNS_TIMED_OUT);
- helper0.set_quit_in_callback();
- helper0.StartTransaction(transaction_factory_.get());
-
- MessageLoop::current()->Run();
-
- EXPECT_TRUE(helper0.has_completed());
+ EXPECT_TRUE(helper0.RunUntilDone(transaction_factory_.get()));
MessageLoop::current()->AssertIdle();
}
@@ -539,18 +542,9 @@ TEST_F(DnsTransactionTest, ServerFallbackAndRotate) {
TransactionHelper helper1(kT1HostName,
kT1Qtype,
ERR_NAME_NOT_RESOLVED);
- helper0.set_quit_in_callback();
- helper0.StartTransaction(transaction_factory_.get());
- MessageLoop::current()->Run();
-
- EXPECT_TRUE(helper0.has_completed());
-
- helper1.StartTransaction(transaction_factory_.get());
-
- MessageLoop::current()->RunAllPending();
-
- EXPECT_TRUE(helper1.has_completed());
+ EXPECT_TRUE(helper0.RunUntilDone(transaction_factory_.get()));
+ EXPECT_TRUE(helper1.Run(transaction_factory_.get()));
unsigned kOrder[] = {
0, 1, 2, 0, 1, // The first transaction.
@@ -578,11 +572,7 @@ TEST_F(DnsTransactionTest, SuffixSearchAboveNdots) {
dns_protocol::kTypeA,
ERR_NAME_NOT_RESOLVED);
- helper0.StartTransaction(transaction_factory_.get());
-
- MessageLoop::current()->RunAllPending();
-
- EXPECT_TRUE(helper0.has_completed());
+ EXPECT_TRUE(helper0.Run(transaction_factory_.get()));
// Also check if suffix search causes server rotation.
unsigned kOrder0[] = { 0, 1, 0, 1 };
@@ -602,6 +592,10 @@ TEST_F(DnsTransactionTest, SuffixSearchBelowNdots) {
AddRcode("x.y.c", dns_protocol::kTypeA, dns_protocol::kRcodeNXDOMAIN);
AddRcode("x.y", dns_protocol::kTypeA, dns_protocol::kRcodeNXDOMAIN);
// Responses for second transaction.
+ AddRcode("x.a", dns_protocol::kTypeA, dns_protocol::kRcodeNXDOMAIN);
+ AddRcode("x.b", dns_protocol::kTypeA, dns_protocol::kRcodeNXDOMAIN);
+ AddRcode("x.c", dns_protocol::kTypeA, dns_protocol::kRcodeNXDOMAIN);
+ // Responses for third transaction.
AddRcode("x", dns_protocol::kTypeAAAA, dns_protocol::kRcodeNXDOMAIN);
PrepareSockets();
@@ -609,24 +603,79 @@ TEST_F(DnsTransactionTest, SuffixSearchBelowNdots) {
dns_protocol::kTypeA,
ERR_NAME_NOT_RESOLVED);
- helper0.StartTransaction(transaction_factory_.get());
+ EXPECT_TRUE(helper0.Run(transaction_factory_.get()));
- MessageLoop::current()->RunAllPending();
+ // A single-label name.
+ TransactionHelper helper1("x",
+ dns_protocol::kTypeA,
+ ERR_NAME_NOT_RESOLVED);
- EXPECT_TRUE(helper0.has_completed());
+ EXPECT_TRUE(helper1.Run(transaction_factory_.get()));
- // Test fully-qualified name.
- TransactionHelper helper1("x.",
+ // A fully-qualified name.
+ TransactionHelper helper2("x.",
dns_protocol::kTypeAAAA,
ERR_NAME_NOT_RESOLVED);
- helper1.StartTransaction(transaction_factory_.get());
+ EXPECT_TRUE(helper2.Run(transaction_factory_.get()));
+}
- MessageLoop::current()->RunAllPending();
+TEST_F(DnsTransactionTest, EmptySuffixSearch) {
+ ConfigureFactory();
+ // Responses for first transaction.
+ AddRcode("x", dns_protocol::kTypeA, dns_protocol::kRcodeNXDOMAIN);
+ PrepareSockets();
+
+ // A fully-qualified name.
+ TransactionHelper helper0("x.",
+ dns_protocol::kTypeA,
+ ERR_NAME_NOT_RESOLVED);
+
+ EXPECT_TRUE(helper0.Run(transaction_factory_.get()));
+
+ // A single label name is not even attempted.
+ TransactionHelper helper1("singlelabel",
+ dns_protocol::kTypeA,
+ ERR_NAME_NOT_RESOLVED);
+
+ helper1.StartTransaction(transaction_factory_.get());
EXPECT_TRUE(helper1.has_completed());
}
+TEST_F(DnsTransactionTest, DontAppendToMultiLabelName) {
+ config_.search.push_back("a");
+ config_.search.push_back("b");
+ config_.search.push_back("c");
+ config_.append_to_multi_label_name = false;
+ ConfigureFactory();
+
+ // Responses for first transaction.
+ AddRcode("x.y.z", dns_protocol::kTypeA, dns_protocol::kRcodeNXDOMAIN);
+ // Responses for second transaction.
+ AddRcode("x.y", dns_protocol::kTypeA, dns_protocol::kRcodeNXDOMAIN);
+ // Responses for third transaction.
+ AddRcode("x.a", dns_protocol::kTypeA, dns_protocol::kRcodeNXDOMAIN);
+ AddRcode("x.b", dns_protocol::kTypeA, dns_protocol::kRcodeNXDOMAIN);
+ AddRcode("x.c", dns_protocol::kTypeA, dns_protocol::kRcodeNXDOMAIN);
+ PrepareSockets();
+
+ TransactionHelper helper0("x.y.z",
+ dns_protocol::kTypeA,
+ ERR_NAME_NOT_RESOLVED);
+ EXPECT_TRUE(helper0.Run(transaction_factory_.get()));
+
+ TransactionHelper helper1("x.y",
+ dns_protocol::kTypeA,
+ ERR_NAME_NOT_RESOLVED);
+ EXPECT_TRUE(helper1.Run(transaction_factory_.get()));
+
+ TransactionHelper helper2("x",
+ dns_protocol::kTypeA,
+ ERR_NAME_NOT_RESOLVED);
+ EXPECT_TRUE(helper2.Run(transaction_factory_.get()));
+}
+
} // namespace
} // namespace net