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