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