| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/dns/public/win_dns_system_settings.h" |
| |
| #include <sysinfoapi.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/compiler_specific.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/memory/free_deleter.h" |
| #include "base/sequence_checker.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/threading/scoped_blocking_call.h" |
| #include "base/win/registry.h" |
| #include "base/win/scoped_handle.h" |
| #include "base/win/windows_types.h" |
| #include "net/base/ip_address.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/dns/public/dns_protocol.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| // Registry key paths. |
| const wchar_t kTcpipPath[] = |
| L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"; |
| const wchar_t kTcpip6Path[] = |
| L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters"; |
| const wchar_t kDnscachePath[] = |
| L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters"; |
| const wchar_t kPolicyPath[] = |
| L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient"; |
| const wchar_t kPrimaryDnsSuffixPath[] = |
| L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient"; |
| const wchar_t kNrptPath[] = |
| L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig"; |
| const wchar_t kControlSetNrptPath[] = |
| L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\" |
| L"DnsPolicyConfig"; |
| const wchar_t kDnsConnectionsPath[] = |
| L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\" |
| L"DnsConnections"; |
| const wchar_t kDnsConnectionsProxies[] = |
| L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\" |
| L"DnsConnectionsProxies"; |
| |
| // Convenience for reading values using RegKey. |
| class RegistryReader { |
| public: |
| explicit RegistryReader(const wchar_t key[]) { |
| // Ignoring the result. |key_.Valid()| will catch failures. |
| key_.Open(HKEY_LOCAL_MACHINE, key, KEY_QUERY_VALUE); |
| } |
| |
| RegistryReader(const RegistryReader&) = delete; |
| RegistryReader& operator=(const RegistryReader&) = delete; |
| |
| ~RegistryReader() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } |
| |
| // Returns `false` if any error occurs, but not if the value is unset. |
| bool ReadString(const wchar_t name[], |
| absl::optional<std::wstring>* output) const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| std::wstring reg_string; |
| if (!key_.Valid()) { |
| // Assume that if the |key_| is invalid then the key is missing. |
| *output = absl::nullopt; |
| return true; |
| } |
| LONG result = key_.ReadValue(name, ®_string); |
| if (result == ERROR_SUCCESS) { |
| *output = std::move(reg_string); |
| return true; |
| } |
| |
| if (result == ERROR_FILE_NOT_FOUND) { |
| *output = absl::nullopt; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // Returns `false` if any error occurs, but not if the value is unset. |
| bool ReadDword(const wchar_t name[], absl::optional<DWORD>* output) const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| DWORD reg_dword; |
| if (!key_.Valid()) { |
| // Assume that if the |key_| is invalid then the key is missing. |
| *output = absl::nullopt; |
| return true; |
| } |
| |
| LONG result = key_.ReadValueDW(name, ®_dword); |
| if (result == ERROR_SUCCESS) { |
| *output = reg_dword; |
| return true; |
| } |
| |
| if (result == ERROR_FILE_NOT_FOUND) { |
| *output = absl::nullopt; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private: |
| base::win::RegKey key_; |
| |
| SEQUENCE_CHECKER(sequence_checker_); |
| }; |
| |
| // Wrapper for GetAdaptersAddresses to get DNS addresses. |
| // Returns nullptr if failed. |
| std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> |
| ReadAdapterDnsAddresses() { |
| base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, |
| base::BlockingType::MAY_BLOCK); |
| |
| std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> out; |
| ULONG len = 15000; // As recommended by MSDN for GetAdaptersAddresses. |
| UINT rv = ERROR_BUFFER_OVERFLOW; |
| // Try up to three times. |
| for (unsigned tries = 0; (tries < 3) && (rv == ERROR_BUFFER_OVERFLOW); |
| tries++) { |
| out.reset(static_cast<PIP_ADAPTER_ADDRESSES>(malloc(len))); |
| memset(out.get(), 0, len); |
| rv = GetAdaptersAddresses(AF_UNSPEC, |
| GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_UNICAST | |
| GAA_FLAG_SKIP_MULTICAST | |
| GAA_FLAG_SKIP_FRIENDLY_NAME, |
| nullptr, out.get(), &len); |
| } |
| if (rv != NO_ERROR) |
| out.reset(); |
| return out; |
| } |
| |
| // Returns `false` if any error occurs, but not if the value is unset. |
| bool ReadDevolutionSetting(const RegistryReader& reader, |
| WinDnsSystemSettings::DevolutionSetting* output) { |
| absl::optional<DWORD> enabled; |
| absl::optional<DWORD> level; |
| if (!reader.ReadDword(L"UseDomainNameDevolution", &enabled) || |
| !reader.ReadDword(L"DomainNameDevolutionLevel", &level)) { |
| return false; |
| } |
| |
| *output = {enabled, level}; |
| return true; |
| } |
| |
| } // namespace |
| |
| WinDnsSystemSettings::WinDnsSystemSettings() = default; |
| WinDnsSystemSettings::~WinDnsSystemSettings() = default; |
| |
| WinDnsSystemSettings::DevolutionSetting::DevolutionSetting() = default; |
| WinDnsSystemSettings::DevolutionSetting::DevolutionSetting( |
| absl::optional<DWORD> enabled, |
| absl::optional<DWORD> level) |
| : enabled(enabled), level(level) {} |
| WinDnsSystemSettings::DevolutionSetting::DevolutionSetting( |
| const DevolutionSetting&) = default; |
| WinDnsSystemSettings::DevolutionSetting& |
| WinDnsSystemSettings::DevolutionSetting::operator=( |
| const WinDnsSystemSettings::DevolutionSetting&) = default; |
| WinDnsSystemSettings::DevolutionSetting::~DevolutionSetting() = default; |
| |
| WinDnsSystemSettings::WinDnsSystemSettings(WinDnsSystemSettings&&) = default; |
| WinDnsSystemSettings& WinDnsSystemSettings::operator=(WinDnsSystemSettings&&) = |
| default; |
| |
| // static |
| bool WinDnsSystemSettings::IsStatelessDiscoveryAddress( |
| const IPAddress& address) { |
| if (!address.IsIPv6()) |
| return false; |
| const uint8_t kPrefix[] = {0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
| return IPAddressStartsWith(address, kPrefix) && (address.bytes().back() < 4); |
| } |
| |
| absl::optional<std::vector<IPEndPoint>> |
| WinDnsSystemSettings::GetAllNameservers() { |
| std::vector<IPEndPoint> nameservers; |
| for (const IP_ADAPTER_ADDRESSES* adapter = addresses.get(); |
| adapter != nullptr; adapter = adapter->Next) { |
| for (const IP_ADAPTER_DNS_SERVER_ADDRESS* address = |
| adapter->FirstDnsServerAddress; |
| address != nullptr; address = address->Next) { |
| IPEndPoint ipe; |
| if (ipe.FromSockAddr(address->Address.lpSockaddr, |
| address->Address.iSockaddrLength)) { |
| if (IsStatelessDiscoveryAddress(ipe.address())) |
| continue; |
| // Override unset port. |
| if (!ipe.port()) |
| ipe = IPEndPoint(ipe.address(), dns_protocol::kDefaultPort); |
| nameservers.push_back(ipe); |
| } else { |
| return absl::nullopt; |
| } |
| } |
| } |
| return nameservers; |
| } |
| |
| absl::optional<WinDnsSystemSettings> ReadWinSystemDnsSettings() { |
| base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, |
| base::BlockingType::MAY_BLOCK); |
| WinDnsSystemSettings settings; |
| |
| // Filled in by GetAdapterAddresses. Note that the alternative |
| // GetNetworkParams does not include IPv6 addresses. |
| settings.addresses = ReadAdapterDnsAddresses(); |
| if (!settings.addresses.get()) |
| return absl::nullopt; |
| |
| RegistryReader tcpip_reader(kTcpipPath); |
| RegistryReader tcpip6_reader(kTcpip6Path); |
| RegistryReader dnscache_reader(kDnscachePath); |
| RegistryReader policy_reader(kPolicyPath); |
| RegistryReader primary_dns_suffix_reader(kPrimaryDnsSuffixPath); |
| |
| absl::optional<std::wstring> reg_string; |
| if (!policy_reader.ReadString(L"SearchList", ®_string)) |
| return absl::nullopt; |
| settings.policy_search_list = std::move(reg_string); |
| |
| if (!tcpip_reader.ReadString(L"SearchList", ®_string)) |
| return absl::nullopt; |
| settings.tcpip_search_list = std::move(reg_string); |
| |
| if (!tcpip_reader.ReadString(L"Domain", ®_string)) |
| return absl::nullopt; |
| settings.tcpip_domain = std::move(reg_string); |
| |
| WinDnsSystemSettings::DevolutionSetting devolution_setting; |
| if (!ReadDevolutionSetting(policy_reader, &devolution_setting)) |
| return absl::nullopt; |
| settings.policy_devolution = devolution_setting; |
| |
| if (!ReadDevolutionSetting(dnscache_reader, &devolution_setting)) |
| return absl::nullopt; |
| settings.dnscache_devolution = devolution_setting; |
| |
| if (!ReadDevolutionSetting(tcpip_reader, &devolution_setting)) |
| return absl::nullopt; |
| settings.tcpip_devolution = devolution_setting; |
| |
| absl::optional<DWORD> reg_dword; |
| if (!policy_reader.ReadDword(L"AppendToMultiLabelName", ®_dword)) |
| return absl::nullopt; |
| settings.append_to_multi_label_name = reg_dword; |
| |
| if (!primary_dns_suffix_reader.ReadString(L"PrimaryDnsSuffix", ®_string)) |
| return absl::nullopt; |
| settings.primary_dns_suffix = std::move(reg_string); |
| |
| base::win::RegistryKeyIterator nrpt_rules(HKEY_LOCAL_MACHINE, kNrptPath); |
| base::win::RegistryKeyIterator cs_nrpt_rules(HKEY_LOCAL_MACHINE, |
| kControlSetNrptPath); |
| settings.have_name_resolution_policy = |
| (nrpt_rules.SubkeyCount() > 0 || cs_nrpt_rules.SubkeyCount() > 0); |
| |
| base::win::RegistryKeyIterator dns_connections(HKEY_LOCAL_MACHINE, |
| kDnsConnectionsPath); |
| base::win::RegistryKeyIterator dns_connections_proxies( |
| HKEY_LOCAL_MACHINE, kDnsConnectionsProxies); |
| settings.have_proxy = (dns_connections.SubkeyCount() > 0 || |
| dns_connections_proxies.SubkeyCount() > 0); |
| |
| return settings; |
| } |
| |
| } // namespace net |