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