• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
RegistryReader(const wchar_t * key)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 
ReadString(const wchar_t * name,DnsSystemSettings::RegString * out) const77   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 
ReadDword(const wchar_t * name,DnsSystemSettings::RegDword * out) const93   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.
ReadIpHelper(ULONG flags)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.
ParseDomainASCII(const base::string16 & widestr,std::string * domain)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 
ReadDevolutionSetting(const RegistryReader & reader,DnsSystemSettings::DevolutionSetting * setting)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.
ReadSystemSettings(DnsSystemSettings * settings)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.
AddLocalhostEntries(DnsHosts * hosts)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;
RegistryWatcher()298   RegistryWatcher() {}
299 
Watch(const wchar_t * key,const CallbackType & callback)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 
OnObjectSignaled(HANDLE object)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
IsStatelessDiscoveryAddress(const IPAddressNumber & address)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.
GetHostsPath()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 
ConfigureSuffixSearch(const DnsSystemSettings & settings,DnsConfig * config)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 
ParseSearchList(const base::string16 & value,std::vector<std::string> * output)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 
ConvertSettingsToDnsConfig(const DnsSystemSettings & settings,DnsConfig * config)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:
Watcher(DnsConfigServiceWin * service)550   explicit Watcher(DnsConfigServiceWin* service) : service_(service) {}
~Watcher()551   ~Watcher() {
552     NetworkChangeNotifier::RemoveIPAddressObserver(this);
553   }
554 
Watch()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:
OnHostsChanged(const base::FilePath & path,bool error)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:
OnIPAddressChanged()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:
ConfigReader(DnsConfigServiceWin * service)625   explicit ConfigReader(DnsConfigServiceWin* service)
626       : service_(service),
627         success_(false) {}
628 
629  private:
~ConfigReader()630   virtual ~ConfigReader() {}
631 
DoWork()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 
OnWorkFinished()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:
HostsReader(DnsConfigServiceWin * service)673   explicit HostsReader(DnsConfigServiceWin* service)
674       : path_(GetHostsPath()),
675         service_(service),
676         success_(false) {
677   }
678 
679  private:
~HostsReader()680   virtual ~HostsReader() {}
681 
DoWork()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 
OnWorkFinished()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 
DnsConfigServiceWin()713 DnsConfigServiceWin::DnsConfigServiceWin()
714     : config_reader_(new ConfigReader(this)),
715       hosts_reader_(new HostsReader(this)) {}
716 
~DnsConfigServiceWin()717 DnsConfigServiceWin::~DnsConfigServiceWin() {
718   config_reader_->Cancel();
719   hosts_reader_->Cancel();
720 }
721 
ReadNow()722 void DnsConfigServiceWin::ReadNow() {
723   config_reader_->WorkNow();
724   hosts_reader_->WorkNow();
725 }
726 
StartWatching()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 
OnConfigChanged(bool succeeded)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 
OnHostsChanged(bool succeeded)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
CreateSystemService()764 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
765   return scoped_ptr<DnsConfigService>(new internal::DnsConfigServiceWin());
766 }
767 
768 }  // namespace net
769