| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/dns/dns_config_service_win.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <string> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/callback.h" | |
| 12 #include "base/compiler_specific.h" | |
| 13 #include "base/files/file_path.h" | |
| 14 #include "base/files/file_path_watcher.h" | |
| 15 #include "base/logging.h" | |
| 16 #include "base/memory/scoped_ptr.h" | |
| 17 #include "base/metrics/histogram.h" | |
| 18 #include "base/profiler/scoped_tracker.h" | |
| 19 #include "base/strings/string_split.h" | |
| 20 #include "base/strings/string_util.h" | |
| 21 #include "base/strings/utf_string_conversions.h" | |
| 22 #include "base/synchronization/lock.h" | |
| 23 #include "base/threading/non_thread_safe.h" | |
| 24 #include "base/threading/thread_restrictions.h" | |
| 25 #include "base/time/time.h" | |
| 26 #include "base/win/registry.h" | |
| 27 #include "base/win/scoped_handle.h" | |
| 28 #include "base/win/windows_version.h" | |
| 29 #include "net/base/net_util.h" | |
| 30 #include "net/base/network_change_notifier.h" | |
| 31 #include "net/dns/dns_hosts.h" | |
| 32 #include "net/dns/dns_protocol.h" | |
| 33 #include "net/dns/serial_worker.h" | |
| 34 #include "url/url_canon.h" | |
| 35 | |
| 36 #pragma comment(lib, "iphlpapi.lib") | |
| 37 | |
| 38 namespace net { | |
| 39 | |
| 40 namespace internal { | |
| 41 | |
| 42 namespace { | |
| 43 | |
| 44 // Interval between retries to parse config. Used only until parsing succeeds. | |
| 45 const int kRetryIntervalSeconds = 5; | |
| 46 | |
| 47 // Registry key paths. | |
| 48 const wchar_t* const kTcpipPath = | |
| 49 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"; | |
| 50 const wchar_t* const kTcpip6Path = | |
| 51 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters"; | |
| 52 const wchar_t* const kDnscachePath = | |
| 53 L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters"; | |
| 54 const wchar_t* const kPolicyPath = | |
| 55 L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient"; | |
| 56 const wchar_t* const kPrimaryDnsSuffixPath = | |
| 57 L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient"; | |
| 58 const wchar_t* const kNRPTPath = | |
| 59 L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig"; | |
| 60 | |
| 61 enum HostsParseWinResult { | |
| 62 HOSTS_PARSE_WIN_OK = 0, | |
| 63 HOSTS_PARSE_WIN_UNREADABLE_HOSTS_FILE, | |
| 64 HOSTS_PARSE_WIN_COMPUTER_NAME_FAILED, | |
| 65 HOSTS_PARSE_WIN_IPHELPER_FAILED, | |
| 66 HOSTS_PARSE_WIN_BAD_ADDRESS, | |
| 67 HOSTS_PARSE_WIN_MAX // Bounding values for enumeration. | |
| 68 }; | |
| 69 | |
| 70 // Convenience for reading values using RegKey. | |
| 71 class RegistryReader : public base::NonThreadSafe { | |
| 72 public: | |
| 73 explicit RegistryReader(const wchar_t* key) { | |
| 74 // Ignoring the result. |key_.Valid()| will catch failures. | |
| 75 key_.Open(HKEY_LOCAL_MACHINE, key, KEY_QUERY_VALUE); | |
| 76 } | |
| 77 | |
| 78 bool ReadString(const wchar_t* name, | |
| 79 DnsSystemSettings::RegString* out) const { | |
| 80 DCHECK(CalledOnValidThread()); | |
| 81 out->set = false; | |
| 82 if (!key_.Valid()) { | |
| 83 // Assume that if the |key_| is invalid then the key is missing. | |
| 84 return true; | |
| 85 } | |
| 86 LONG result = key_.ReadValue(name, &out->value); | |
| 87 if (result == ERROR_SUCCESS) { | |
| 88 out->set = true; | |
| 89 return true; | |
| 90 } | |
| 91 return (result == ERROR_FILE_NOT_FOUND); | |
| 92 } | |
| 93 | |
| 94 bool ReadDword(const wchar_t* name, | |
| 95 DnsSystemSettings::RegDword* out) const { | |
| 96 DCHECK(CalledOnValidThread()); | |
| 97 out->set = false; | |
| 98 if (!key_.Valid()) { | |
| 99 // Assume that if the |key_| is invalid then the key is missing. | |
| 100 return true; | |
| 101 } | |
| 102 LONG result = key_.ReadValueDW(name, &out->value); | |
| 103 if (result == ERROR_SUCCESS) { | |
| 104 out->set = true; | |
| 105 return true; | |
| 106 } | |
| 107 return (result == ERROR_FILE_NOT_FOUND); | |
| 108 } | |
| 109 | |
| 110 private: | |
| 111 base::win::RegKey key_; | |
| 112 | |
| 113 DISALLOW_COPY_AND_ASSIGN(RegistryReader); | |
| 114 }; | |
| 115 | |
| 116 // Wrapper for GetAdaptersAddresses. Returns NULL if failed. | |
| 117 scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> ReadIpHelper(ULONG flags) { | |
| 118 base::ThreadRestrictions::AssertIOAllowed(); | |
| 119 | |
| 120 scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> out; | |
| 121 ULONG len = 15000; // As recommended by MSDN for GetAdaptersAddresses. | |
| 122 UINT rv = ERROR_BUFFER_OVERFLOW; | |
| 123 // Try up to three times. | |
| 124 for (unsigned tries = 0; (tries < 3) && (rv == ERROR_BUFFER_OVERFLOW); | |
| 125 tries++) { | |
| 126 out.reset(static_cast<PIP_ADAPTER_ADDRESSES>(malloc(len))); | |
| 127 memset(out.get(), 0, len); | |
| 128 rv = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, out.get(), &len); | |
| 129 } | |
| 130 if (rv != NO_ERROR) | |
| 131 out.reset(); | |
| 132 return out.Pass(); | |
| 133 } | |
| 134 | |
| 135 // Converts a base::string16 domain name to ASCII, possibly using punycode. | |
| 136 // Returns true if the conversion succeeds and output is not empty. In case of | |
| 137 // failure, |domain| might become dirty. | |
| 138 bool ParseDomainASCII(const base::string16& widestr, std::string* domain) { | |
| 139 DCHECK(domain); | |
| 140 if (widestr.empty()) | |
| 141 return false; | |
| 142 | |
| 143 // Check if already ASCII. | |
| 144 if (base::IsStringASCII(widestr)) { | |
| 145 *domain = base::UTF16ToASCII(widestr); | |
| 146 return true; | |
| 147 } | |
| 148 | |
| 149 // Otherwise try to convert it from IDN to punycode. | |
| 150 const int kInitialBufferSize = 256; | |
| 151 url::RawCanonOutputT<base::char16, kInitialBufferSize> punycode; | |
| 152 if (!url::IDNToASCII(widestr.data(), widestr.length(), &punycode)) | |
| 153 return false; | |
| 154 | |
| 155 // |punycode_output| should now be ASCII; convert it to a std::string. | |
| 156 // (We could use UTF16ToASCII() instead, but that requires an extra string | |
| 157 // copy. Since ASCII is a subset of UTF8 the following is equivalent). | |
| 158 bool success = base::UTF16ToUTF8(punycode.data(), punycode.length(), domain); | |
| 159 DCHECK(success); | |
| 160 DCHECK(base::IsStringASCII(*domain)); | |
| 161 return success && !domain->empty(); | |
| 162 } | |
| 163 | |
| 164 bool ReadDevolutionSetting(const RegistryReader& reader, | |
| 165 DnsSystemSettings::DevolutionSetting* setting) { | |
| 166 return reader.ReadDword(L"UseDomainNameDevolution", &setting->enabled) && | |
| 167 reader.ReadDword(L"DomainNameDevolutionLevel", &setting->level); | |
| 168 } | |
| 169 | |
| 170 // Reads DnsSystemSettings from IpHelper and registry. | |
| 171 ConfigParseWinResult ReadSystemSettings(DnsSystemSettings* settings) { | |
| 172 settings->addresses = ReadIpHelper(GAA_FLAG_SKIP_ANYCAST | | |
| 173 GAA_FLAG_SKIP_UNICAST | | |
| 174 GAA_FLAG_SKIP_MULTICAST | | |
| 175 GAA_FLAG_SKIP_FRIENDLY_NAME); | |
| 176 if (!settings->addresses.get()) | |
| 177 return CONFIG_PARSE_WIN_READ_IPHELPER; | |
| 178 | |
| 179 RegistryReader tcpip_reader(kTcpipPath); | |
| 180 RegistryReader tcpip6_reader(kTcpip6Path); | |
| 181 RegistryReader dnscache_reader(kDnscachePath); | |
| 182 RegistryReader policy_reader(kPolicyPath); | |
| 183 RegistryReader primary_dns_suffix_reader(kPrimaryDnsSuffixPath); | |
| 184 | |
| 185 if (!policy_reader.ReadString(L"SearchList", | |
| 186 &settings->policy_search_list)) { | |
| 187 return CONFIG_PARSE_WIN_READ_POLICY_SEARCHLIST; | |
| 188 } | |
| 189 | |
| 190 if (!tcpip_reader.ReadString(L"SearchList", &settings->tcpip_search_list)) | |
| 191 return CONFIG_PARSE_WIN_READ_TCPIP_SEARCHLIST; | |
| 192 | |
| 193 if (!tcpip_reader.ReadString(L"Domain", &settings->tcpip_domain)) | |
| 194 return CONFIG_PARSE_WIN_READ_DOMAIN; | |
| 195 | |
| 196 if (!ReadDevolutionSetting(policy_reader, &settings->policy_devolution)) | |
| 197 return CONFIG_PARSE_WIN_READ_POLICY_DEVOLUTION; | |
| 198 | |
| 199 if (!ReadDevolutionSetting(dnscache_reader, &settings->dnscache_devolution)) | |
| 200 return CONFIG_PARSE_WIN_READ_DNSCACHE_DEVOLUTION; | |
| 201 | |
| 202 if (!ReadDevolutionSetting(tcpip_reader, &settings->tcpip_devolution)) | |
| 203 return CONFIG_PARSE_WIN_READ_TCPIP_DEVOLUTION; | |
| 204 | |
| 205 if (!policy_reader.ReadDword(L"AppendToMultiLabelName", | |
| 206 &settings->append_to_multi_label_name)) { | |
| 207 return CONFIG_PARSE_WIN_READ_APPEND_MULTILABEL; | |
| 208 } | |
| 209 | |
| 210 if (!primary_dns_suffix_reader.ReadString(L"PrimaryDnsSuffix", | |
| 211 &settings->primary_dns_suffix)) { | |
| 212 return CONFIG_PARSE_WIN_READ_PRIMARY_SUFFIX; | |
| 213 } | |
| 214 | |
| 215 base::win::RegistryKeyIterator nrpt_rules(HKEY_LOCAL_MACHINE, kNRPTPath); | |
| 216 settings->have_name_resolution_policy = (nrpt_rules.SubkeyCount() > 0); | |
| 217 | |
| 218 return CONFIG_PARSE_WIN_OK; | |
| 219 } | |
| 220 | |
| 221 // Default address of "localhost" and local computer name can be overridden | |
| 222 // by the HOSTS file, but if it's not there, then we need to fill it in. | |
| 223 HostsParseWinResult AddLocalhostEntries(DnsHosts* hosts) { | |
| 224 const unsigned char kIPv4Localhost[] = { 127, 0, 0, 1 }; | |
| 225 const unsigned char kIPv6Localhost[] = { 0, 0, 0, 0, 0, 0, 0, 0, | |
| 226 0, 0, 0, 0, 0, 0, 0, 1 }; | |
| 227 IPAddressNumber loopback_ipv4(kIPv4Localhost, | |
| 228 kIPv4Localhost + arraysize(kIPv4Localhost)); | |
| 229 IPAddressNumber loopback_ipv6(kIPv6Localhost, | |
| 230 kIPv6Localhost + arraysize(kIPv6Localhost)); | |
| 231 | |
| 232 // This does not override any pre-existing entries from the HOSTS file. | |
| 233 hosts->insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4), | |
| 234 loopback_ipv4)); | |
| 235 hosts->insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV6), | |
| 236 loopback_ipv6)); | |
| 237 | |
| 238 WCHAR buffer[MAX_PATH]; | |
| 239 DWORD size = MAX_PATH; | |
| 240 std::string localname; | |
| 241 if (!GetComputerNameExW(ComputerNameDnsHostname, buffer, &size) || | |
| 242 !ParseDomainASCII(buffer, &localname)) { | |
| 243 return HOSTS_PARSE_WIN_COMPUTER_NAME_FAILED; | |
| 244 } | |
| 245 base::StringToLowerASCII(&localname); | |
| 246 | |
| 247 bool have_ipv4 = | |
| 248 hosts->count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)) > 0; | |
| 249 bool have_ipv6 = | |
| 250 hosts->count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)) > 0; | |
| 251 | |
| 252 if (have_ipv4 && have_ipv6) | |
| 253 return HOSTS_PARSE_WIN_OK; | |
| 254 | |
| 255 scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> addresses = | |
| 256 ReadIpHelper(GAA_FLAG_SKIP_ANYCAST | | |
| 257 GAA_FLAG_SKIP_DNS_SERVER | | |
| 258 GAA_FLAG_SKIP_MULTICAST | | |
| 259 GAA_FLAG_SKIP_FRIENDLY_NAME); | |
| 260 if (!addresses.get()) | |
| 261 return HOSTS_PARSE_WIN_IPHELPER_FAILED; | |
| 262 | |
| 263 // The order of adapters is the network binding order, so stick to the | |
| 264 // first good adapter for each family. | |
| 265 for (const IP_ADAPTER_ADDRESSES* adapter = addresses.get(); | |
| 266 adapter != NULL && (!have_ipv4 || !have_ipv6); | |
| 267 adapter = adapter->Next) { | |
| 268 if (adapter->OperStatus != IfOperStatusUp) | |
| 269 continue; | |
| 270 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) | |
| 271 continue; | |
| 272 | |
| 273 for (const IP_ADAPTER_UNICAST_ADDRESS* address = | |
| 274 adapter->FirstUnicastAddress; | |
| 275 address != NULL; | |
| 276 address = address->Next) { | |
| 277 IPEndPoint ipe; | |
| 278 if (!ipe.FromSockAddr(address->Address.lpSockaddr, | |
| 279 address->Address.iSockaddrLength)) { | |
| 280 return HOSTS_PARSE_WIN_BAD_ADDRESS; | |
| 281 } | |
| 282 if (!have_ipv4 && (ipe.GetFamily() == ADDRESS_FAMILY_IPV4)) { | |
| 283 have_ipv4 = true; | |
| 284 (*hosts)[DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)] = ipe.address(); | |
| 285 } else if (!have_ipv6 && (ipe.GetFamily() == ADDRESS_FAMILY_IPV6)) { | |
| 286 have_ipv6 = true; | |
| 287 (*hosts)[DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)] = ipe.address(); | |
| 288 } | |
| 289 } | |
| 290 } | |
| 291 return HOSTS_PARSE_WIN_OK; | |
| 292 } | |
| 293 | |
| 294 // Watches a single registry key for changes. | |
| 295 class RegistryWatcher : public base::NonThreadSafe { | |
| 296 public: | |
| 297 typedef base::Callback<void(bool succeeded)> CallbackType; | |
| 298 RegistryWatcher() {} | |
| 299 | |
| 300 bool Watch(const wchar_t* key, const CallbackType& callback) { | |
| 301 DCHECK(CalledOnValidThread()); | |
| 302 DCHECK(!callback.is_null()); | |
| 303 DCHECK(callback_.is_null()); | |
| 304 callback_ = callback; | |
| 305 if (key_.Open(HKEY_LOCAL_MACHINE, key, KEY_NOTIFY) != ERROR_SUCCESS) | |
| 306 return false; | |
| 307 | |
| 308 return key_.StartWatching(base::Bind(&RegistryWatcher::OnObjectSignaled, | |
| 309 base::Unretained(this))); | |
| 310 } | |
| 311 | |
| 312 void OnObjectSignaled() { | |
| 313 // TODO(vadimt): Remove ScopedTracker below once crbug.com/418183 is fixed. | |
| 314 tracked_objects::ScopedTracker tracking_profile( | |
| 315 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 316 "418183 RegistryWatcher::OnObjectSignaled")); | |
| 317 | |
| 318 DCHECK(CalledOnValidThread()); | |
| 319 DCHECK(!callback_.is_null()); | |
| 320 if (key_.StartWatching(base::Bind(&RegistryWatcher::OnObjectSignaled, | |
| 321 base::Unretained(this)))) { | |
| 322 callback_.Run(true); | |
| 323 } else { | |
| 324 key_.Close(); | |
| 325 callback_.Run(false); | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 private: | |
| 330 CallbackType callback_; | |
| 331 base::win::RegKey key_; | |
| 332 | |
| 333 DISALLOW_COPY_AND_ASSIGN(RegistryWatcher); | |
| 334 }; | |
| 335 | |
| 336 // Returns true iff |address| is DNS address from IPv6 stateless discovery, | |
| 337 // i.e., matches fec0:0:0:ffff::{1,2,3}. | |
| 338 // http://tools.ietf.org/html/draft-ietf-ipngwg-dns-discovery | |
| 339 bool IsStatelessDiscoveryAddress(const IPAddressNumber& address) { | |
| 340 if (address.size() != kIPv6AddressSize) | |
| 341 return false; | |
| 342 const uint8 kPrefix[] = { | |
| 343 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, | |
| 344 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 345 }; | |
| 346 return std::equal(kPrefix, kPrefix + arraysize(kPrefix), | |
| 347 address.begin()) && (address.back() < 4); | |
| 348 } | |
| 349 | |
| 350 // Returns the path to the HOSTS file. | |
| 351 base::FilePath GetHostsPath() { | |
| 352 TCHAR buffer[MAX_PATH]; | |
| 353 UINT rc = GetSystemDirectory(buffer, MAX_PATH); | |
| 354 DCHECK(0 < rc && rc < MAX_PATH); | |
| 355 return base::FilePath(buffer).Append( | |
| 356 FILE_PATH_LITERAL("drivers\\etc\\hosts")); | |
| 357 } | |
| 358 | |
| 359 void ConfigureSuffixSearch(const DnsSystemSettings& settings, | |
| 360 DnsConfig* config) { | |
| 361 // SearchList takes precedence, so check it first. | |
| 362 if (settings.policy_search_list.set) { | |
| 363 std::vector<std::string> search; | |
| 364 if (ParseSearchList(settings.policy_search_list.value, &search)) { | |
| 365 config->search.swap(search); | |
| 366 return; | |
| 367 } | |
| 368 // Even if invalid, the policy disables the user-specified setting below. | |
| 369 } else if (settings.tcpip_search_list.set) { | |
| 370 std::vector<std::string> search; | |
| 371 if (ParseSearchList(settings.tcpip_search_list.value, &search)) { | |
| 372 config->search.swap(search); | |
| 373 return; | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 // In absence of explicit search list, suffix search is: | |
| 378 // [primary suffix, connection-specific suffix, devolution of primary suffix]. | |
| 379 // Primary suffix can be set by policy (primary_dns_suffix) or | |
| 380 // user setting (tcpip_domain). | |
| 381 // | |
| 382 // The policy (primary_dns_suffix) can be edited via Group Policy Editor | |
| 383 // (gpedit.msc) at Local Computer Policy => Computer Configuration | |
| 384 // => Administrative Template => Network => DNS Client => Primary DNS Suffix. | |
| 385 // | |
| 386 // The user setting (tcpip_domain) can be configurred at Computer Name in | |
| 387 // System Settings | |
| 388 std::string primary_suffix; | |
| 389 if ((settings.primary_dns_suffix.set && | |
| 390 ParseDomainASCII(settings.primary_dns_suffix.value, &primary_suffix)) || | |
| 391 (settings.tcpip_domain.set && | |
| 392 ParseDomainASCII(settings.tcpip_domain.value, &primary_suffix))) { | |
| 393 // Primary suffix goes in front. | |
| 394 config->search.insert(config->search.begin(), primary_suffix); | |
| 395 } else { | |
| 396 return; // No primary suffix, hence no devolution. | |
| 397 } | |
| 398 | |
| 399 // Devolution is determined by precedence: policy > dnscache > tcpip. | |
| 400 // |enabled|: UseDomainNameDevolution and |level|: DomainNameDevolutionLevel | |
| 401 // are overridden independently. | |
| 402 DnsSystemSettings::DevolutionSetting devolution = settings.policy_devolution; | |
| 403 | |
| 404 if (!devolution.enabled.set) | |
| 405 devolution.enabled = settings.dnscache_devolution.enabled; | |
| 406 if (!devolution.enabled.set) | |
| 407 devolution.enabled = settings.tcpip_devolution.enabled; | |
| 408 if (devolution.enabled.set && (devolution.enabled.value == 0)) | |
| 409 return; // Devolution disabled. | |
| 410 | |
| 411 // By default devolution is enabled. | |
| 412 | |
| 413 if (!devolution.level.set) | |
| 414 devolution.level = settings.dnscache_devolution.level; | |
| 415 if (!devolution.level.set) | |
| 416 devolution.level = settings.tcpip_devolution.level; | |
| 417 | |
| 418 // After the recent update, Windows will try to determine a safe default | |
| 419 // value by comparing the forest root domain (FRD) to the primary suffix. | |
| 420 // See http://support.microsoft.com/kb/957579 for details. | |
| 421 // For now, if the level is not set, we disable devolution, assuming that | |
| 422 // we will fallback to the system getaddrinfo anyway. This might cause | |
| 423 // performance loss for resolutions which depend on the system default | |
| 424 // devolution setting. | |
| 425 // | |
| 426 // If the level is explicitly set below 2, devolution is disabled. | |
| 427 if (!devolution.level.set || devolution.level.value < 2) | |
| 428 return; // Devolution disabled. | |
| 429 | |
| 430 // Devolve the primary suffix. This naive logic matches the observed | |
| 431 // behavior (see also ParseSearchList). If a suffix is not valid, it will be | |
| 432 // discarded when the fully-qualified name is converted to DNS format. | |
| 433 | |
| 434 unsigned num_dots = std::count(primary_suffix.begin(), | |
| 435 primary_suffix.end(), '.'); | |
| 436 | |
| 437 for (size_t offset = 0; num_dots >= devolution.level.value; --num_dots) { | |
| 438 offset = primary_suffix.find('.', offset + 1); | |
| 439 config->search.push_back(primary_suffix.substr(offset + 1)); | |
| 440 } | |
| 441 } | |
| 442 | |
| 443 } // namespace | |
| 444 | |
| 445 bool ParseSearchList(const base::string16& value, | |
| 446 std::vector<std::string>* output) { | |
| 447 DCHECK(output); | |
| 448 if (value.empty()) | |
| 449 return false; | |
| 450 | |
| 451 output->clear(); | |
| 452 | |
| 453 // If the list includes an empty hostname (",," or ", ,"), it is terminated. | |
| 454 // Although nslookup and network connection property tab ignore such | |
| 455 // fragments ("a,b,,c" becomes ["a", "b", "c"]), our reference is getaddrinfo | |
| 456 // (which sees ["a", "b"]). WMI queries also return a matching search list. | |
| 457 std::vector<base::string16> woutput; | |
| 458 base::SplitString(value, ',', &woutput); | |
| 459 for (size_t i = 0; i < woutput.size(); ++i) { | |
| 460 // Convert non-ASCII to punycode, although getaddrinfo does not properly | |
| 461 // handle such suffixes. | |
| 462 const base::string16& t = woutput[i]; | |
| 463 std::string parsed; | |
| 464 if (!ParseDomainASCII(t, &parsed)) | |
| 465 break; | |
| 466 output->push_back(parsed); | |
| 467 } | |
| 468 return !output->empty(); | |
| 469 } | |
| 470 | |
| 471 ConfigParseWinResult ConvertSettingsToDnsConfig( | |
| 472 const DnsSystemSettings& settings, | |
| 473 DnsConfig* config) { | |
| 474 *config = DnsConfig(); | |
| 475 | |
| 476 // Use GetAdapterAddresses to get effective DNS server order and | |
| 477 // connection-specific DNS suffix. Ignore disconnected and loopback adapters. | |
| 478 // The order of adapters is the network binding order, so stick to the | |
| 479 // first good adapter. | |
| 480 for (const IP_ADAPTER_ADDRESSES* adapter = settings.addresses.get(); | |
| 481 adapter != NULL && config->nameservers.empty(); | |
| 482 adapter = adapter->Next) { | |
| 483 if (adapter->OperStatus != IfOperStatusUp) | |
| 484 continue; | |
| 485 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) | |
| 486 continue; | |
| 487 | |
| 488 for (const IP_ADAPTER_DNS_SERVER_ADDRESS* address = | |
| 489 adapter->FirstDnsServerAddress; | |
| 490 address != NULL; | |
| 491 address = address->Next) { | |
| 492 IPEndPoint ipe; | |
| 493 if (ipe.FromSockAddr(address->Address.lpSockaddr, | |
| 494 address->Address.iSockaddrLength)) { | |
| 495 if (IsStatelessDiscoveryAddress(ipe.address())) | |
| 496 continue; | |
| 497 // Override unset port. | |
| 498 if (!ipe.port()) | |
| 499 ipe = IPEndPoint(ipe.address(), dns_protocol::kDefaultPort); | |
| 500 config->nameservers.push_back(ipe); | |
| 501 } else { | |
| 502 return CONFIG_PARSE_WIN_BAD_ADDRESS; | |
| 503 } | |
| 504 } | |
| 505 | |
| 506 // IP_ADAPTER_ADDRESSES in Vista+ has a search list at |FirstDnsSuffix|, | |
| 507 // but it came up empty in all trials. | |
| 508 // |DnsSuffix| stores the effective connection-specific suffix, which is | |
| 509 // obtained via DHCP (regkey: Tcpip\Parameters\Interfaces\{XXX}\DhcpDomain) | |
| 510 // or specified by the user (regkey: Tcpip\Parameters\Domain). | |
| 511 std::string dns_suffix; | |
| 512 if (ParseDomainASCII(adapter->DnsSuffix, &dns_suffix)) | |
| 513 config->search.push_back(dns_suffix); | |
| 514 } | |
| 515 | |
| 516 if (config->nameservers.empty()) | |
| 517 return CONFIG_PARSE_WIN_NO_NAMESERVERS; // No point continuing. | |
| 518 | |
| 519 // Windows always tries a multi-label name "as is" before using suffixes. | |
| 520 config->ndots = 1; | |
| 521 | |
| 522 if (!settings.append_to_multi_label_name.set) { | |
| 523 // The default setting is true for XP, false for Vista+. | |
| 524 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | |
| 525 config->append_to_multi_label_name = false; | |
| 526 } else { | |
| 527 config->append_to_multi_label_name = true; | |
| 528 } | |
| 529 } else { | |
| 530 config->append_to_multi_label_name = | |
| 531 (settings.append_to_multi_label_name.value != 0); | |
| 532 } | |
| 533 | |
| 534 ConfigParseWinResult result = CONFIG_PARSE_WIN_OK; | |
| 535 if (settings.have_name_resolution_policy) { | |
| 536 config->unhandled_options = true; | |
| 537 // TODO(szym): only set this to true if NRPT has DirectAccess rules. | |
| 538 config->use_local_ipv6 = true; | |
| 539 result = CONFIG_PARSE_WIN_UNHANDLED_OPTIONS; | |
| 540 } | |
| 541 | |
| 542 ConfigureSuffixSearch(settings, config); | |
| 543 return result; | |
| 544 } | |
| 545 | |
| 546 // Watches registry and HOSTS file for changes. Must live on a thread which | |
| 547 // allows IO. | |
| 548 class DnsConfigServiceWin::Watcher | |
| 549 : public NetworkChangeNotifier::IPAddressObserver { | |
| 550 public: | |
| 551 explicit Watcher(DnsConfigServiceWin* service) : service_(service) {} | |
| 552 ~Watcher() { | |
| 553 NetworkChangeNotifier::RemoveIPAddressObserver(this); | |
| 554 } | |
| 555 | |
| 556 bool Watch() { | |
| 557 RegistryWatcher::CallbackType callback = | |
| 558 base::Bind(&DnsConfigServiceWin::OnConfigChanged, | |
| 559 base::Unretained(service_)); | |
| 560 | |
| 561 bool success = true; | |
| 562 | |
| 563 // The Tcpip key must be present. | |
| 564 if (!tcpip_watcher_.Watch(kTcpipPath, callback)) { | |
| 565 LOG(ERROR) << "DNS registry watch failed to start."; | |
| 566 success = false; | |
| 567 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", | |
| 568 DNS_CONFIG_WATCH_FAILED_TO_START_CONFIG, | |
| 569 DNS_CONFIG_WATCH_MAX); | |
| 570 } | |
| 571 | |
| 572 // Watch for IPv6 nameservers. | |
| 573 tcpip6_watcher_.Watch(kTcpip6Path, callback); | |
| 574 | |
| 575 // DNS suffix search list and devolution can be configured via group | |
| 576 // policy which sets this registry key. If the key is missing, the policy | |
| 577 // does not apply, and the DNS client uses Tcpip and Dnscache settings. | |
| 578 // If a policy is installed, DnsConfigService will need to be restarted. | |
| 579 // BUG=99509 | |
| 580 | |
| 581 dnscache_watcher_.Watch(kDnscachePath, callback); | |
| 582 policy_watcher_.Watch(kPolicyPath, callback); | |
| 583 | |
| 584 if (!hosts_watcher_.Watch(GetHostsPath(), false, | |
| 585 base::Bind(&Watcher::OnHostsChanged, | |
| 586 base::Unretained(this)))) { | |
| 587 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", | |
| 588 DNS_CONFIG_WATCH_FAILED_TO_START_HOSTS, | |
| 589 DNS_CONFIG_WATCH_MAX); | |
| 590 LOG(ERROR) << "DNS hosts watch failed to start."; | |
| 591 success = false; | |
| 592 } else { | |
| 593 // Also need to observe changes to local non-loopback IP for DnsHosts. | |
| 594 NetworkChangeNotifier::AddIPAddressObserver(this); | |
| 595 } | |
| 596 return success; | |
| 597 } | |
| 598 | |
| 599 private: | |
| 600 void OnHostsChanged(const base::FilePath& path, bool error) { | |
| 601 if (error) | |
| 602 NetworkChangeNotifier::RemoveIPAddressObserver(this); | |
| 603 service_->OnHostsChanged(!error); | |
| 604 } | |
| 605 | |
| 606 // NetworkChangeNotifier::IPAddressObserver: | |
| 607 virtual void OnIPAddressChanged() override { | |
| 608 // Need to update non-loopback IP of local host. | |
| 609 service_->OnHostsChanged(true); | |
| 610 } | |
| 611 | |
| 612 DnsConfigServiceWin* service_; | |
| 613 | |
| 614 RegistryWatcher tcpip_watcher_; | |
| 615 RegistryWatcher tcpip6_watcher_; | |
| 616 RegistryWatcher dnscache_watcher_; | |
| 617 RegistryWatcher policy_watcher_; | |
| 618 base::FilePathWatcher hosts_watcher_; | |
| 619 | |
| 620 DISALLOW_COPY_AND_ASSIGN(Watcher); | |
| 621 }; | |
| 622 | |
| 623 // Reads config from registry and IpHelper. All work performed on WorkerPool. | |
| 624 class DnsConfigServiceWin::ConfigReader : public SerialWorker { | |
| 625 public: | |
| 626 explicit ConfigReader(DnsConfigServiceWin* service) | |
| 627 : service_(service), | |
| 628 success_(false) {} | |
| 629 | |
| 630 private: | |
| 631 virtual ~ConfigReader() {} | |
| 632 | |
| 633 virtual void DoWork() override { | |
| 634 // Should be called on WorkerPool. | |
| 635 base::TimeTicks start_time = base::TimeTicks::Now(); | |
| 636 DnsSystemSettings settings = {}; | |
| 637 ConfigParseWinResult result = ReadSystemSettings(&settings); | |
| 638 if (result == CONFIG_PARSE_WIN_OK) | |
| 639 result = ConvertSettingsToDnsConfig(settings, &dns_config_); | |
| 640 success_ = (result == CONFIG_PARSE_WIN_OK || | |
| 641 result == CONFIG_PARSE_WIN_UNHANDLED_OPTIONS); | |
| 642 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParseWin", | |
| 643 result, CONFIG_PARSE_WIN_MAX); | |
| 644 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_); | |
| 645 UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration", | |
| 646 base::TimeTicks::Now() - start_time); | |
| 647 } | |
| 648 | |
| 649 virtual void OnWorkFinished() override { | |
| 650 DCHECK(loop()->BelongsToCurrentThread()); | |
| 651 DCHECK(!IsCancelled()); | |
| 652 if (success_) { | |
| 653 service_->OnConfigRead(dns_config_); | |
| 654 } else { | |
| 655 LOG(WARNING) << "Failed to read DnsConfig."; | |
| 656 // Try again in a while in case DnsConfigWatcher missed the signal. | |
| 657 base::MessageLoop::current()->PostDelayedTask( | |
| 658 FROM_HERE, | |
| 659 base::Bind(&ConfigReader::WorkNow, this), | |
| 660 base::TimeDelta::FromSeconds(kRetryIntervalSeconds)); | |
| 661 } | |
| 662 } | |
| 663 | |
| 664 DnsConfigServiceWin* service_; | |
| 665 // Written in DoWork(), read in OnWorkFinished(). No locking required. | |
| 666 DnsConfig dns_config_; | |
| 667 bool success_; | |
| 668 }; | |
| 669 | |
| 670 // Reads hosts from HOSTS file and fills in localhost and local computer name if | |
| 671 // necessary. All work performed on WorkerPool. | |
| 672 class DnsConfigServiceWin::HostsReader : public SerialWorker { | |
| 673 public: | |
| 674 explicit HostsReader(DnsConfigServiceWin* service) | |
| 675 : path_(GetHostsPath()), | |
| 676 service_(service), | |
| 677 success_(false) { | |
| 678 } | |
| 679 | |
| 680 private: | |
| 681 virtual ~HostsReader() {} | |
| 682 | |
| 683 virtual void DoWork() override { | |
| 684 base::TimeTicks start_time = base::TimeTicks::Now(); | |
| 685 HostsParseWinResult result = HOSTS_PARSE_WIN_UNREADABLE_HOSTS_FILE; | |
| 686 if (ParseHostsFile(path_, &hosts_)) | |
| 687 result = AddLocalhostEntries(&hosts_); | |
| 688 success_ = (result == HOSTS_PARSE_WIN_OK); | |
| 689 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.HostsParseWin", | |
| 690 result, HOSTS_PARSE_WIN_MAX); | |
| 691 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostParseResult", success_); | |
| 692 UMA_HISTOGRAM_TIMES("AsyncDNS.HostsParseDuration", | |
| 693 base::TimeTicks::Now() - start_time); | |
| 694 } | |
| 695 | |
| 696 virtual void OnWorkFinished() override { | |
| 697 DCHECK(loop()->BelongsToCurrentThread()); | |
| 698 if (success_) { | |
| 699 service_->OnHostsRead(hosts_); | |
| 700 } else { | |
| 701 LOG(WARNING) << "Failed to read DnsHosts."; | |
| 702 } | |
| 703 } | |
| 704 | |
| 705 const base::FilePath path_; | |
| 706 DnsConfigServiceWin* service_; | |
| 707 // Written in DoWork, read in OnWorkFinished, no locking necessary. | |
| 708 DnsHosts hosts_; | |
| 709 bool success_; | |
| 710 | |
| 711 DISALLOW_COPY_AND_ASSIGN(HostsReader); | |
| 712 }; | |
| 713 | |
| 714 DnsConfigServiceWin::DnsConfigServiceWin() | |
| 715 : config_reader_(new ConfigReader(this)), | |
| 716 hosts_reader_(new HostsReader(this)) {} | |
| 717 | |
| 718 DnsConfigServiceWin::~DnsConfigServiceWin() { | |
| 719 config_reader_->Cancel(); | |
| 720 hosts_reader_->Cancel(); | |
| 721 } | |
| 722 | |
| 723 void DnsConfigServiceWin::ReadNow() { | |
| 724 config_reader_->WorkNow(); | |
| 725 hosts_reader_->WorkNow(); | |
| 726 } | |
| 727 | |
| 728 bool DnsConfigServiceWin::StartWatching() { | |
| 729 // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139 | |
| 730 watcher_.reset(new Watcher(this)); | |
| 731 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", DNS_CONFIG_WATCH_STARTED, | |
| 732 DNS_CONFIG_WATCH_MAX); | |
| 733 return watcher_->Watch(); | |
| 734 } | |
| 735 | |
| 736 void DnsConfigServiceWin::OnConfigChanged(bool succeeded) { | |
| 737 InvalidateConfig(); | |
| 738 config_reader_->WorkNow(); | |
| 739 if (!succeeded) { | |
| 740 LOG(ERROR) << "DNS config watch failed."; | |
| 741 set_watch_failed(true); | |
| 742 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", | |
| 743 DNS_CONFIG_WATCH_FAILED_CONFIG, | |
| 744 DNS_CONFIG_WATCH_MAX); | |
| 745 } | |
| 746 } | |
| 747 | |
| 748 void DnsConfigServiceWin::OnHostsChanged(bool succeeded) { | |
| 749 InvalidateHosts(); | |
| 750 if (succeeded) { | |
| 751 hosts_reader_->WorkNow(); | |
| 752 } else { | |
| 753 LOG(ERROR) << "DNS hosts watch failed."; | |
| 754 set_watch_failed(true); | |
| 755 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", | |
| 756 DNS_CONFIG_WATCH_FAILED_HOSTS, | |
| 757 DNS_CONFIG_WATCH_MAX); | |
| 758 } | |
| 759 } | |
| 760 | |
| 761 } // namespace internal | |
| 762 | |
| 763 // static | |
| 764 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { | |
| 765 return scoped_ptr<DnsConfigService>(new internal::DnsConfigServiceWin()); | |
| 766 } | |
| 767 | |
| 768 } // namespace net | |
| OLD | NEW |