Home | History | Annotate | Download | only in dns
      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 const wchar_t* const kPrimaryDnsSuffixPath =
     47     L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient";
     48 
     49 enum 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.
     59 class 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.
    105 scoped_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.
    125 bool 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 
    151 bool 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.
    158 ConfigParseWinResult 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.
    206 HostsParseWinResult 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.
    278 class 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
    322 bool 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 
    335 base::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 
    343 bool 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 
    369 ConfigParseWinResult 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.
    517 class 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.
    593 class 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.
    640 class 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 
    682 DnsConfigServiceWin::DnsConfigServiceWin()
    683     : config_reader_(new ConfigReader(this)),
    684       hosts_reader_(new HostsReader(this)) {}
    685 
    686 DnsConfigServiceWin::~DnsConfigServiceWin() {
    687   config_reader_->Cancel();
    688   hosts_reader_->Cancel();
    689 }
    690 
    691 void DnsConfigServiceWin::ReadNow() {
    692   config_reader_->WorkNow();
    693   hosts_reader_->WorkNow();
    694 }
    695 
    696 bool 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 
    704 void 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 
    717 void 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
    733 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
    734   return scoped_ptr<DnsConfigService>(new internal::DnsConfigServiceWin());
    735 }
    736 
    737 }  // namespace net
    738