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.
5 #include "net/dns/dns_config_service_win.h"
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_macros.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/registry.h"
26 #include "base/win/scoped_handle.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"
35 #pragma comment(lib, "iphlpapi.lib")
43 // Interval between retries to parse config. Used only until parsing succeeds.
44 const int kRetryIntervalSeconds
= 5;
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";
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.
69 // Convenience for reading values using RegKey.
70 class RegistryReader
: public base::NonThreadSafe
{
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
);
77 bool ReadString(const wchar_t* name
,
78 DnsSystemSettings::RegString
* out
) const {
79 DCHECK(CalledOnValidThread());
82 // Assume that if the |key_| is invalid then the key is missing.
85 LONG result
= key_
.ReadValue(name
, &out
->value
);
86 if (result
== ERROR_SUCCESS
) {
90 return (result
== ERROR_FILE_NOT_FOUND
);
93 bool ReadDword(const wchar_t* name
,
94 DnsSystemSettings::RegDword
* out
) const {
95 DCHECK(CalledOnValidThread());
98 // Assume that if the |key_| is invalid then the key is missing.
101 LONG result
= key_
.ReadValueDW(name
, &out
->value
);
102 if (result
== ERROR_SUCCESS
) {
106 return (result
== ERROR_FILE_NOT_FOUND
);
110 base::win::RegKey key_
;
112 DISALLOW_COPY_AND_ASSIGN(RegistryReader
);
115 // Wrapper for GetAdaptersAddresses. Returns NULL if failed.
116 scoped_ptr
<IP_ADAPTER_ADDRESSES
, base::FreeDeleter
> ReadIpHelper(ULONG flags
) {
117 base::ThreadRestrictions::AssertIOAllowed();
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
);
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
);
134 // Converts a base::string16 domain name to ASCII, possibly using punycode.
135 // Returns true if the conversion succeeds and output is not empty. In case of
136 // failure, |domain| might become dirty.
137 bool ParseDomainASCII(base::StringPiece16 widestr
, std::string
* domain
) {
142 // Check if already ASCII.
143 if (base::IsStringASCII(widestr
)) {
144 domain
->assign(widestr
.begin(), widestr
.end());
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
))
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
);
159 DCHECK(base::IsStringASCII(*domain
));
160 return success
&& !domain
->empty();
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
);
169 // Reads DnsSystemSettings from IpHelper and registry.
170 ConfigParseWinResult
ReadSystemSettings(DnsSystemSettings
* settings
) {
171 settings
->addresses
= ReadIpHelper(GAA_FLAG_SKIP_ANYCAST
|
172 GAA_FLAG_SKIP_UNICAST
|
173 GAA_FLAG_SKIP_MULTICAST
|
174 GAA_FLAG_SKIP_FRIENDLY_NAME
);
175 if (!settings
->addresses
.get())
176 return CONFIG_PARSE_WIN_READ_IPHELPER
;
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
);
184 if (!policy_reader
.ReadString(L
"SearchList",
185 &settings
->policy_search_list
)) {
186 return CONFIG_PARSE_WIN_READ_POLICY_SEARCHLIST
;
189 if (!tcpip_reader
.ReadString(L
"SearchList", &settings
->tcpip_search_list
))
190 return CONFIG_PARSE_WIN_READ_TCPIP_SEARCHLIST
;
192 if (!tcpip_reader
.ReadString(L
"Domain", &settings
->tcpip_domain
))
193 return CONFIG_PARSE_WIN_READ_DOMAIN
;
195 if (!ReadDevolutionSetting(policy_reader
, &settings
->policy_devolution
))
196 return CONFIG_PARSE_WIN_READ_POLICY_DEVOLUTION
;
198 if (!ReadDevolutionSetting(dnscache_reader
, &settings
->dnscache_devolution
))
199 return CONFIG_PARSE_WIN_READ_DNSCACHE_DEVOLUTION
;
201 if (!ReadDevolutionSetting(tcpip_reader
, &settings
->tcpip_devolution
))
202 return CONFIG_PARSE_WIN_READ_TCPIP_DEVOLUTION
;
204 if (!policy_reader
.ReadDword(L
"AppendToMultiLabelName",
205 &settings
->append_to_multi_label_name
)) {
206 return CONFIG_PARSE_WIN_READ_APPEND_MULTILABEL
;
209 if (!primary_dns_suffix_reader
.ReadString(L
"PrimaryDnsSuffix",
210 &settings
->primary_dns_suffix
)) {
211 return CONFIG_PARSE_WIN_READ_PRIMARY_SUFFIX
;
214 base::win::RegistryKeyIterator
nrpt_rules(HKEY_LOCAL_MACHINE
, kNRPTPath
);
215 settings
->have_name_resolution_policy
= (nrpt_rules
.SubkeyCount() > 0);
217 return CONFIG_PARSE_WIN_OK
;
220 // Default address of "localhost" and local computer name can be overridden
221 // by the HOSTS file, but if it's not there, then we need to fill it in.
222 HostsParseWinResult
AddLocalhostEntries(DnsHosts
* hosts
) {
223 const unsigned char kIPv4Localhost
[] = { 127, 0, 0, 1 };
224 const unsigned char kIPv6Localhost
[] = { 0, 0, 0, 0, 0, 0, 0, 0,
225 0, 0, 0, 0, 0, 0, 0, 1 };
226 IPAddressNumber
loopback_ipv4(kIPv4Localhost
,
227 kIPv4Localhost
+ arraysize(kIPv4Localhost
));
228 IPAddressNumber
loopback_ipv6(kIPv6Localhost
,
229 kIPv6Localhost
+ arraysize(kIPv6Localhost
));
231 // This does not override any pre-existing entries from the HOSTS file.
232 hosts
->insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4
),
234 hosts
->insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV6
),
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
;
244 localname
= base::ToLowerASCII(localname
);
247 hosts
->count(DnsHostsKey(localname
, ADDRESS_FAMILY_IPV4
)) > 0;
249 hosts
->count(DnsHostsKey(localname
, ADDRESS_FAMILY_IPV6
)) > 0;
251 if (have_ipv4
&& have_ipv6
)
252 return HOSTS_PARSE_WIN_OK
;
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
;
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
)
269 if (adapter
->IfType
== IF_TYPE_SOFTWARE_LOOPBACK
)
272 for (const IP_ADAPTER_UNICAST_ADDRESS
* address
=
273 adapter
->FirstUnicastAddress
;
275 address
= address
->Next
) {
277 if (!ipe
.FromSockAddr(address
->Address
.lpSockaddr
,
278 address
->Address
.iSockaddrLength
)) {
279 return HOSTS_PARSE_WIN_BAD_ADDRESS
;
281 if (!have_ipv4
&& (ipe
.GetFamily() == ADDRESS_FAMILY_IPV4
)) {
283 (*hosts
)[DnsHostsKey(localname
, ADDRESS_FAMILY_IPV4
)] = ipe
.address();
284 } else if (!have_ipv6
&& (ipe
.GetFamily() == ADDRESS_FAMILY_IPV6
)) {
286 (*hosts
)[DnsHostsKey(localname
, ADDRESS_FAMILY_IPV6
)] = ipe
.address();
290 return HOSTS_PARSE_WIN_OK
;
293 // Watches a single registry key for changes.
294 class RegistryWatcher
: public base::NonThreadSafe
{
296 typedef base::Callback
<void(bool succeeded
)> CallbackType
;
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
)
307 return key_
.StartWatching(base::Bind(&RegistryWatcher::OnObjectSignaled
,
308 base::Unretained(this)));
311 void OnObjectSignaled() {
312 DCHECK(CalledOnValidThread());
313 DCHECK(!callback_
.is_null());
314 if (key_
.StartWatching(base::Bind(&RegistryWatcher::OnObjectSignaled
,
315 base::Unretained(this)))) {
319 callback_
.Run(false);
324 CallbackType callback_
;
325 base::win::RegKey key_
;
327 DISALLOW_COPY_AND_ASSIGN(RegistryWatcher
);
330 // Returns true iff |address| is DNS address from IPv6 stateless discovery,
331 // i.e., matches fec0:0:0:ffff::{1,2,3}.
332 // http://tools.ietf.org/html/draft-ietf-ipngwg-dns-discovery
333 bool IsStatelessDiscoveryAddress(const IPAddressNumber
& address
) {
334 if (address
.size() != kIPv6AddressSize
)
336 const uint8 kPrefix
[] = {
337 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
338 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340 return std::equal(kPrefix
, kPrefix
+ arraysize(kPrefix
),
341 address
.begin()) && (address
.back() < 4);
344 // Returns the path to the HOSTS file.
345 base::FilePath
GetHostsPath() {
346 TCHAR buffer
[MAX_PATH
];
347 UINT rc
= GetSystemDirectory(buffer
, MAX_PATH
);
348 DCHECK(0 < rc
&& rc
< MAX_PATH
);
349 return base::FilePath(buffer
).Append(
350 FILE_PATH_LITERAL("drivers\\etc\\hosts"));
353 void ConfigureSuffixSearch(const DnsSystemSettings
& settings
,
355 // SearchList takes precedence, so check it first.
356 if (settings
.policy_search_list
.set
) {
357 std::vector
<std::string
> search
;
358 if (ParseSearchList(settings
.policy_search_list
.value
, &search
)) {
359 config
->search
.swap(search
);
362 // Even if invalid, the policy disables the user-specified setting below.
363 } else if (settings
.tcpip_search_list
.set
) {
364 std::vector
<std::string
> search
;
365 if (ParseSearchList(settings
.tcpip_search_list
.value
, &search
)) {
366 config
->search
.swap(search
);
371 // In absence of explicit search list, suffix search is:
372 // [primary suffix, connection-specific suffix, devolution of primary suffix].
373 // Primary suffix can be set by policy (primary_dns_suffix) or
374 // user setting (tcpip_domain).
376 // The policy (primary_dns_suffix) can be edited via Group Policy Editor
377 // (gpedit.msc) at Local Computer Policy => Computer Configuration
378 // => Administrative Template => Network => DNS Client => Primary DNS Suffix.
380 // The user setting (tcpip_domain) can be configurred at Computer Name in
382 std::string primary_suffix
;
383 if ((settings
.primary_dns_suffix
.set
&&
384 ParseDomainASCII(settings
.primary_dns_suffix
.value
, &primary_suffix
)) ||
385 (settings
.tcpip_domain
.set
&&
386 ParseDomainASCII(settings
.tcpip_domain
.value
, &primary_suffix
))) {
387 // Primary suffix goes in front.
388 config
->search
.insert(config
->search
.begin(), primary_suffix
);
390 return; // No primary suffix, hence no devolution.
393 // Devolution is determined by precedence: policy > dnscache > tcpip.
394 // |enabled|: UseDomainNameDevolution and |level|: DomainNameDevolutionLevel
395 // are overridden independently.
396 DnsSystemSettings::DevolutionSetting devolution
= settings
.policy_devolution
;
398 if (!devolution
.enabled
.set
)
399 devolution
.enabled
= settings
.dnscache_devolution
.enabled
;
400 if (!devolution
.enabled
.set
)
401 devolution
.enabled
= settings
.tcpip_devolution
.enabled
;
402 if (devolution
.enabled
.set
&& (devolution
.enabled
.value
== 0))
403 return; // Devolution disabled.
405 // By default devolution is enabled.
407 if (!devolution
.level
.set
)
408 devolution
.level
= settings
.dnscache_devolution
.level
;
409 if (!devolution
.level
.set
)
410 devolution
.level
= settings
.tcpip_devolution
.level
;
412 // After the recent update, Windows will try to determine a safe default
413 // value by comparing the forest root domain (FRD) to the primary suffix.
414 // See http://support.microsoft.com/kb/957579 for details.
415 // For now, if the level is not set, we disable devolution, assuming that
416 // we will fallback to the system getaddrinfo anyway. This might cause
417 // performance loss for resolutions which depend on the system default
418 // devolution setting.
420 // If the level is explicitly set below 2, devolution is disabled.
421 if (!devolution
.level
.set
|| devolution
.level
.value
< 2)
422 return; // Devolution disabled.
424 // Devolve the primary suffix. This naive logic matches the observed
425 // behavior (see also ParseSearchList). If a suffix is not valid, it will be
426 // discarded when the fully-qualified name is converted to DNS format.
428 unsigned num_dots
= std::count(primary_suffix
.begin(),
429 primary_suffix
.end(), '.');
431 for (size_t offset
= 0; num_dots
>= devolution
.level
.value
; --num_dots
) {
432 offset
= primary_suffix
.find('.', offset
+ 1);
433 config
->search
.push_back(primary_suffix
.substr(offset
+ 1));
439 DnsSystemSettings::DnsSystemSettings()
440 : policy_search_list(),
443 primary_dns_suffix(),
445 dnscache_devolution(),
447 append_to_multi_label_name(),
448 have_name_resolution_policy(false) {
449 policy_search_list
.set
= false;
450 tcpip_search_list
.set
= false;
451 tcpip_domain
.set
= false;
452 primary_dns_suffix
.set
= false;
454 policy_devolution
.enabled
.set
= false;
455 policy_devolution
.level
.set
= false;
456 dnscache_devolution
.enabled
.set
= false;
457 dnscache_devolution
.level
.set
= false;
458 tcpip_devolution
.enabled
.set
= false;
459 tcpip_devolution
.level
.set
= false;
461 append_to_multi_label_name
.set
= false;
464 DnsSystemSettings::~DnsSystemSettings() {
467 bool ParseSearchList(const base::string16
& value
,
468 std::vector
<std::string
>* output
) {
475 // If the list includes an empty hostname (",," or ", ,"), it is terminated.
476 // Although nslookup and network connection property tab ignore such
477 // fragments ("a,b,,c" becomes ["a", "b", "c"]), our reference is getaddrinfo
478 // (which sees ["a", "b"]). WMI queries also return a matching search list.
479 for (const base::StringPiece16
& t
: base::SplitStringPiece(
480 value
, L
",", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
)) {
481 // Convert non-ASCII to punycode, although getaddrinfo does not properly
482 // handle such suffixes.
484 if (!ParseDomainASCII(t
, &parsed
))
486 output
->push_back(parsed
);
488 return !output
->empty();
491 ConfigParseWinResult
ConvertSettingsToDnsConfig(
492 const DnsSystemSettings
& settings
,
494 *config
= DnsConfig();
496 // Use GetAdapterAddresses to get effective DNS server order and
497 // connection-specific DNS suffix. Ignore disconnected and loopback adapters.
498 // The order of adapters is the network binding order, so stick to the
499 // first good adapter.
500 for (const IP_ADAPTER_ADDRESSES
* adapter
= settings
.addresses
.get();
501 adapter
!= NULL
&& config
->nameservers
.empty();
502 adapter
= adapter
->Next
) {
503 if (adapter
->OperStatus
!= IfOperStatusUp
)
505 if (adapter
->IfType
== IF_TYPE_SOFTWARE_LOOPBACK
)
508 for (const IP_ADAPTER_DNS_SERVER_ADDRESS
* address
=
509 adapter
->FirstDnsServerAddress
;
511 address
= address
->Next
) {
513 if (ipe
.FromSockAddr(address
->Address
.lpSockaddr
,
514 address
->Address
.iSockaddrLength
)) {
515 if (IsStatelessDiscoveryAddress(ipe
.address()))
517 // Override unset port.
519 ipe
= IPEndPoint(ipe
.address(), dns_protocol::kDefaultPort
);
520 config
->nameservers
.push_back(ipe
);
522 return CONFIG_PARSE_WIN_BAD_ADDRESS
;
526 // IP_ADAPTER_ADDRESSES in Vista+ has a search list at |FirstDnsSuffix|,
527 // but it came up empty in all trials.
528 // |DnsSuffix| stores the effective connection-specific suffix, which is
529 // obtained via DHCP (regkey: Tcpip\Parameters\Interfaces\{XXX}\DhcpDomain)
530 // or specified by the user (regkey: Tcpip\Parameters\Domain).
531 std::string dns_suffix
;
532 if (ParseDomainASCII(adapter
->DnsSuffix
, &dns_suffix
))
533 config
->search
.push_back(dns_suffix
);
536 if (config
->nameservers
.empty())
537 return CONFIG_PARSE_WIN_NO_NAMESERVERS
; // No point continuing.
539 // Windows always tries a multi-label name "as is" before using suffixes.
542 if (!settings
.append_to_multi_label_name
.set
) {
543 // The default setting is true for XP, false for Vista+.
544 if (base::win::GetVersion() >= base::win::VERSION_VISTA
) {
545 config
->append_to_multi_label_name
= false;
547 config
->append_to_multi_label_name
= true;
550 config
->append_to_multi_label_name
=
551 (settings
.append_to_multi_label_name
.value
!= 0);
554 ConfigParseWinResult result
= CONFIG_PARSE_WIN_OK
;
555 if (settings
.have_name_resolution_policy
) {
556 config
->unhandled_options
= true;
557 // TODO(szym): only set this to true if NRPT has DirectAccess rules.
558 config
->use_local_ipv6
= true;
559 result
= CONFIG_PARSE_WIN_UNHANDLED_OPTIONS
;
562 ConfigureSuffixSearch(settings
, config
);
566 // Watches registry and HOSTS file for changes. Must live on a thread which
568 class DnsConfigServiceWin::Watcher
569 : public NetworkChangeNotifier::IPAddressObserver
{
571 explicit Watcher(DnsConfigServiceWin
* service
) : service_(service
) {}
572 ~Watcher() override
{ NetworkChangeNotifier::RemoveIPAddressObserver(this); }
575 RegistryWatcher::CallbackType callback
=
576 base::Bind(&DnsConfigServiceWin::OnConfigChanged
,
577 base::Unretained(service_
));
581 // The Tcpip key must be present.
582 if (!tcpip_watcher_
.Watch(kTcpipPath
, callback
)) {
583 LOG(ERROR
) << "DNS registry watch failed to start.";
585 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
586 DNS_CONFIG_WATCH_FAILED_TO_START_CONFIG
,
587 DNS_CONFIG_WATCH_MAX
);
590 // Watch for IPv6 nameservers.
591 tcpip6_watcher_
.Watch(kTcpip6Path
, callback
);
593 // DNS suffix search list and devolution can be configured via group
594 // policy which sets this registry key. If the key is missing, the policy
595 // does not apply, and the DNS client uses Tcpip and Dnscache settings.
596 // If a policy is installed, DnsConfigService will need to be restarted.
599 dnscache_watcher_
.Watch(kDnscachePath
, callback
);
600 policy_watcher_
.Watch(kPolicyPath
, callback
);
602 if (!hosts_watcher_
.Watch(GetHostsPath(), false,
603 base::Bind(&Watcher::OnHostsChanged
,
604 base::Unretained(this)))) {
605 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
606 DNS_CONFIG_WATCH_FAILED_TO_START_HOSTS
,
607 DNS_CONFIG_WATCH_MAX
);
608 LOG(ERROR
) << "DNS hosts watch failed to start.";
611 // Also need to observe changes to local non-loopback IP for DnsHosts.
612 NetworkChangeNotifier::AddIPAddressObserver(this);
618 void OnHostsChanged(const base::FilePath
& path
, bool error
) {
620 NetworkChangeNotifier::RemoveIPAddressObserver(this);
621 service_
->OnHostsChanged(!error
);
624 // NetworkChangeNotifier::IPAddressObserver:
625 void OnIPAddressChanged() override
{
626 // Need to update non-loopback IP of local host.
627 service_
->OnHostsChanged(true);
630 DnsConfigServiceWin
* service_
;
632 RegistryWatcher tcpip_watcher_
;
633 RegistryWatcher tcpip6_watcher_
;
634 RegistryWatcher dnscache_watcher_
;
635 RegistryWatcher policy_watcher_
;
636 base::FilePathWatcher hosts_watcher_
;
638 DISALLOW_COPY_AND_ASSIGN(Watcher
);
641 // Reads config from registry and IpHelper. All work performed on WorkerPool.
642 class DnsConfigServiceWin::ConfigReader
: public SerialWorker
{
644 explicit ConfigReader(DnsConfigServiceWin
* service
)
649 ~ConfigReader() override
{}
651 void DoWork() override
{
652 // Should be called on WorkerPool.
653 base::TimeTicks start_time
= base::TimeTicks::Now();
654 DnsSystemSettings settings
= {};
655 ConfigParseWinResult result
= ReadSystemSettings(&settings
);
656 if (result
== CONFIG_PARSE_WIN_OK
)
657 result
= ConvertSettingsToDnsConfig(settings
, &dns_config_
);
658 success_
= (result
== CONFIG_PARSE_WIN_OK
||
659 result
== CONFIG_PARSE_WIN_UNHANDLED_OPTIONS
);
660 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParseWin",
661 result
, CONFIG_PARSE_WIN_MAX
);
662 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_
);
663 UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration",
664 base::TimeTicks::Now() - start_time
);
667 void OnWorkFinished() override
{
668 DCHECK(loop()->BelongsToCurrentThread());
669 DCHECK(!IsCancelled());
671 service_
->OnConfigRead(dns_config_
);
673 LOG(WARNING
) << "Failed to read DnsConfig.";
674 // Try again in a while in case DnsConfigWatcher missed the signal.
675 base::MessageLoop::current()->PostDelayedTask(
677 base::Bind(&ConfigReader::WorkNow
, this),
678 base::TimeDelta::FromSeconds(kRetryIntervalSeconds
));
682 DnsConfigServiceWin
* service_
;
683 // Written in DoWork(), read in OnWorkFinished(). No locking required.
684 DnsConfig dns_config_
;
688 // Reads hosts from HOSTS file and fills in localhost and local computer name if
689 // necessary. All work performed on WorkerPool.
690 class DnsConfigServiceWin::HostsReader
: public SerialWorker
{
692 explicit HostsReader(DnsConfigServiceWin
* service
)
693 : path_(GetHostsPath()),
699 ~HostsReader() override
{}
701 void DoWork() override
{
702 base::TimeTicks start_time
= base::TimeTicks::Now();
703 HostsParseWinResult result
= HOSTS_PARSE_WIN_UNREADABLE_HOSTS_FILE
;
704 if (ParseHostsFile(path_
, &hosts_
))
705 result
= AddLocalhostEntries(&hosts_
);
706 success_
= (result
== HOSTS_PARSE_WIN_OK
);
707 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.HostsParseWin",
708 result
, HOSTS_PARSE_WIN_MAX
);
709 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostParseResult", success_
);
710 UMA_HISTOGRAM_TIMES("AsyncDNS.HostsParseDuration",
711 base::TimeTicks::Now() - start_time
);
714 void OnWorkFinished() override
{
715 DCHECK(loop()->BelongsToCurrentThread());
717 service_
->OnHostsRead(hosts_
);
719 LOG(WARNING
) << "Failed to read DnsHosts.";
723 const base::FilePath path_
;
724 DnsConfigServiceWin
* service_
;
725 // Written in DoWork, read in OnWorkFinished, no locking necessary.
729 DISALLOW_COPY_AND_ASSIGN(HostsReader
);
732 DnsConfigServiceWin::DnsConfigServiceWin()
733 : config_reader_(new ConfigReader(this)),
734 hosts_reader_(new HostsReader(this)) {}
736 DnsConfigServiceWin::~DnsConfigServiceWin() {
737 config_reader_
->Cancel();
738 hosts_reader_
->Cancel();
741 void DnsConfigServiceWin::ReadNow() {
742 config_reader_
->WorkNow();
743 hosts_reader_
->WorkNow();
746 bool DnsConfigServiceWin::StartWatching() {
747 // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
748 watcher_
.reset(new Watcher(this));
749 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", DNS_CONFIG_WATCH_STARTED
,
750 DNS_CONFIG_WATCH_MAX
);
751 return watcher_
->Watch();
754 void DnsConfigServiceWin::OnConfigChanged(bool succeeded
) {
756 config_reader_
->WorkNow();
758 LOG(ERROR
) << "DNS config watch failed.";
759 set_watch_failed(true);
760 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
761 DNS_CONFIG_WATCH_FAILED_CONFIG
,
762 DNS_CONFIG_WATCH_MAX
);
766 void DnsConfigServiceWin::OnHostsChanged(bool succeeded
) {
769 hosts_reader_
->WorkNow();
771 LOG(ERROR
) << "DNS hosts watch failed.";
772 set_watch_failed(true);
773 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
774 DNS_CONFIG_WATCH_FAILED_HOSTS
,
775 DNS_CONFIG_WATCH_MAX
);
779 } // namespace internal
782 scoped_ptr
<DnsConfigService
> DnsConfigService::CreateSystemService() {
783 return scoped_ptr
<DnsConfigService
>(new internal::DnsConfigServiceWin());