xref: /aosp_15_r20/external/cronet/net/dns/public/win_dns_system_settings.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
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/public/win_dns_system_settings.h"
6 
7 #include <sysinfoapi.h>
8 
9 #include <algorithm>
10 #include <memory>
11 #include <string>
12 #include <vector>
13 
14 #include <optional>
15 #include "base/compiler_specific.h"
16 #include "base/functional/bind.h"
17 #include "base/location.h"
18 #include "base/logging.h"
19 #include "base/memory/free_deleter.h"
20 #include "base/sequence_checker.h"
21 #include "base/strings/string_piece.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/task/single_thread_task_runner.h"
26 #include "base/threading/scoped_blocking_call.h"
27 #include "base/types/expected.h"
28 #include "base/win/registry.h"
29 #include "base/win/scoped_handle.h"
30 #include "base/win/windows_types.h"
31 #include "net/base/ip_address.h"
32 #include "net/base/ip_endpoint.h"
33 #include "net/dns/public/dns_protocol.h"
34 
35 namespace net {
36 
37 namespace {
38 
39 // Registry key paths.
40 const wchar_t kTcpipPath[] =
41     L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
42 const wchar_t kTcpip6Path[] =
43     L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters";
44 const wchar_t kDnscachePath[] =
45     L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters";
46 const wchar_t kPolicyPath[] =
47     L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient";
48 const wchar_t kPrimaryDnsSuffixPath[] =
49     L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient";
50 const wchar_t kNrptPath[] =
51     L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig";
52 const wchar_t kControlSetNrptPath[] =
53     L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\"
54     L"DnsPolicyConfig";
55 const wchar_t kDnsConnectionsPath[] =
56     L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\"
57     L"DnsConnections";
58 const wchar_t kDnsConnectionsProxies[] =
59     L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\"
60     L"DnsConnectionsProxies";
61 
62 // Convenience for reading values using RegKey.
63 class RegistryReader {
64  public:
RegistryReader(const wchar_t key[])65   explicit RegistryReader(const wchar_t key[]) {
66     // Ignoring the result. |key_.Valid()| will catch failures.
67     (void)key_.Open(HKEY_LOCAL_MACHINE, key, KEY_QUERY_VALUE);
68   }
69 
70   RegistryReader(const RegistryReader&) = delete;
71   RegistryReader& operator=(const RegistryReader&) = delete;
72 
~RegistryReader()73   ~RegistryReader() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
74 
75   // Returns `false` if any error occurs, but not if the value is unset.
ReadString(const wchar_t name[],std::optional<std::wstring> * output) const76   bool ReadString(const wchar_t name[],
77                   std::optional<std::wstring>* output) const {
78     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
79     std::wstring reg_string;
80     if (!key_.Valid()) {
81       // Assume that if the |key_| is invalid then the key is missing.
82       *output = std::nullopt;
83       return true;
84     }
85     LONG result = key_.ReadValue(name, &reg_string);
86     if (result == ERROR_SUCCESS) {
87       *output = std::move(reg_string);
88       return true;
89     }
90 
91     if (result == ERROR_FILE_NOT_FOUND) {
92       *output = std::nullopt;
93       return true;
94     }
95 
96     return false;
97   }
98 
99   // Returns `false` if any error occurs, but not if the value is unset.
ReadDword(const wchar_t name[],std::optional<DWORD> * output) const100   bool ReadDword(const wchar_t name[], std::optional<DWORD>* output) const {
101     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
102 
103     DWORD reg_dword;
104     if (!key_.Valid()) {
105       // Assume that if the |key_| is invalid then the key is missing.
106       *output = std::nullopt;
107       return true;
108     }
109 
110     LONG result = key_.ReadValueDW(name, &reg_dword);
111     if (result == ERROR_SUCCESS) {
112       *output = reg_dword;
113       return true;
114     }
115 
116     if (result == ERROR_FILE_NOT_FOUND) {
117       *output = std::nullopt;
118       return true;
119     }
120 
121     return false;
122   }
123 
124  private:
125   base::win::RegKey key_;
126 
127   SEQUENCE_CHECKER(sequence_checker_);
128 };
129 
130 // Wrapper for GetAdaptersAddresses to get DNS addresses.
131 // Returns nullptr if failed.
132 std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter>
ReadAdapterDnsAddresses()133 ReadAdapterDnsAddresses() {
134   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
135                                                 base::BlockingType::MAY_BLOCK);
136 
137   std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> out;
138   ULONG len = 15000;  // As recommended by MSDN for GetAdaptersAddresses.
139   UINT rv = ERROR_BUFFER_OVERFLOW;
140   // Try up to three times.
141   for (unsigned tries = 0; (tries < 3) && (rv == ERROR_BUFFER_OVERFLOW);
142        tries++) {
143     out.reset(static_cast<PIP_ADAPTER_ADDRESSES>(malloc(len)));
144     memset(out.get(), 0, len);
145     rv = GetAdaptersAddresses(AF_UNSPEC,
146                               GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_UNICAST |
147                               GAA_FLAG_SKIP_MULTICAST |
148                               GAA_FLAG_SKIP_FRIENDLY_NAME,
149                               nullptr, out.get(), &len);
150   }
151   if (rv != NO_ERROR)
152     out.reset();
153   return out;
154 }
155 
156 // Returns `false` if any error occurs, but not if the value is unset.
ReadDevolutionSetting(const RegistryReader & reader,WinDnsSystemSettings::DevolutionSetting * output)157 bool ReadDevolutionSetting(const RegistryReader& reader,
158                            WinDnsSystemSettings::DevolutionSetting* output) {
159   std::optional<DWORD> enabled;
160   std::optional<DWORD> level;
161   if (!reader.ReadDword(L"UseDomainNameDevolution", &enabled) ||
162       !reader.ReadDword(L"DomainNameDevolutionLevel", &level)) {
163     return false;
164   }
165 
166   *output = {enabled, level};
167   return true;
168 }
169 
170 }  // namespace
171 
172 WinDnsSystemSettings::WinDnsSystemSettings() = default;
173 WinDnsSystemSettings::~WinDnsSystemSettings() = default;
174 
175 WinDnsSystemSettings::DevolutionSetting::DevolutionSetting() = default;
DevolutionSetting(std::optional<DWORD> enabled,std::optional<DWORD> level)176 WinDnsSystemSettings::DevolutionSetting::DevolutionSetting(
177     std::optional<DWORD> enabled,
178     std::optional<DWORD> level)
179     : enabled(enabled), level(level) {}
180 WinDnsSystemSettings::DevolutionSetting::DevolutionSetting(
181     const DevolutionSetting&) = default;
182 WinDnsSystemSettings::DevolutionSetting&
183 WinDnsSystemSettings::DevolutionSetting::operator=(
184     const WinDnsSystemSettings::DevolutionSetting&) = default;
185 WinDnsSystemSettings::DevolutionSetting::~DevolutionSetting() = default;
186 
187 WinDnsSystemSettings::WinDnsSystemSettings(WinDnsSystemSettings&&) = default;
188 WinDnsSystemSettings& WinDnsSystemSettings::operator=(WinDnsSystemSettings&&) =
189     default;
190 
191 // static
IsStatelessDiscoveryAddress(const IPAddress & address)192 bool WinDnsSystemSettings::IsStatelessDiscoveryAddress(
193     const IPAddress& address) {
194   if (!address.IsIPv6())
195     return false;
196   const uint8_t kPrefix[] = {0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
197                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
198   return IPAddressStartsWith(address, kPrefix) && (address.bytes().back() < 4);
199 }
200 
201 std::optional<std::vector<IPEndPoint>>
GetAllNameservers()202 WinDnsSystemSettings::GetAllNameservers() {
203   std::vector<IPEndPoint> nameservers;
204   for (const IP_ADAPTER_ADDRESSES* adapter = addresses.get();
205        adapter != nullptr; adapter = adapter->Next) {
206     for (const IP_ADAPTER_DNS_SERVER_ADDRESS* address =
207              adapter->FirstDnsServerAddress;
208          address != nullptr; address = address->Next) {
209       IPEndPoint ipe;
210       if (ipe.FromSockAddr(address->Address.lpSockaddr,
211                            address->Address.iSockaddrLength)) {
212         if (IsStatelessDiscoveryAddress(ipe.address()))
213           continue;
214         // Override unset port.
215         if (!ipe.port())
216           ipe = IPEndPoint(ipe.address(), dns_protocol::kDefaultPort);
217         nameservers.push_back(ipe);
218       } else {
219         return std::nullopt;
220       }
221     }
222   }
223   return nameservers;
224 }
225 
226 base::expected<WinDnsSystemSettings, ReadWinSystemDnsSettingsError>
ReadWinSystemDnsSettings()227 ReadWinSystemDnsSettings() {
228   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
229                                                 base::BlockingType::MAY_BLOCK);
230   WinDnsSystemSettings settings;
231 
232   // Filled in by GetAdapterAddresses. Note that the alternative
233   // GetNetworkParams does not include IPv6 addresses.
234   settings.addresses = ReadAdapterDnsAddresses();
235   if (!settings.addresses.get()) {
236     return base::unexpected(
237         ReadWinSystemDnsSettingsError::kReadAdapterDnsAddressesFailed);
238   }
239 
240   RegistryReader tcpip_reader(kTcpipPath);
241   RegistryReader tcpip6_reader(kTcpip6Path);
242   RegistryReader dnscache_reader(kDnscachePath);
243   RegistryReader policy_reader(kPolicyPath);
244   RegistryReader primary_dns_suffix_reader(kPrimaryDnsSuffixPath);
245 
246   std::optional<std::wstring> reg_string;
247   if (!policy_reader.ReadString(L"SearchList", &reg_string)) {
248     return base::unexpected(
249         ReadWinSystemDnsSettingsError::kReadPolicySearchListFailed);
250   }
251   settings.policy_search_list = std::move(reg_string);
252 
253   if (!tcpip_reader.ReadString(L"SearchList", &reg_string)) {
254     return base::unexpected(
255         ReadWinSystemDnsSettingsError::kReadTcpipSearchListFailed);
256   }
257   settings.tcpip_search_list = std::move(reg_string);
258 
259   if (!tcpip_reader.ReadString(L"Domain", &reg_string)) {
260     return base::unexpected(
261         ReadWinSystemDnsSettingsError::kReadTcpipDomainFailed);
262   }
263   settings.tcpip_domain = std::move(reg_string);
264 
265   WinDnsSystemSettings::DevolutionSetting devolution_setting;
266   if (!ReadDevolutionSetting(policy_reader, &devolution_setting)) {
267     return base::unexpected(
268         ReadWinSystemDnsSettingsError::kReadPolicyDevolutionSettingFailed);
269   }
270   settings.policy_devolution = devolution_setting;
271 
272   if (!ReadDevolutionSetting(dnscache_reader, &devolution_setting)) {
273     return base::unexpected(
274         ReadWinSystemDnsSettingsError::kReadDnscacheDevolutionSettingFailed);
275   }
276   settings.dnscache_devolution = devolution_setting;
277 
278   if (!ReadDevolutionSetting(tcpip_reader, &devolution_setting)) {
279     return base::unexpected(
280         ReadWinSystemDnsSettingsError::kReadTcpipDevolutionSettingFailed);
281   }
282   settings.tcpip_devolution = devolution_setting;
283 
284   std::optional<DWORD> reg_dword;
285   if (!policy_reader.ReadDword(L"AppendToMultiLabelName", &reg_dword)) {
286     return base::unexpected(
287         ReadWinSystemDnsSettingsError::kReadPolicyAppendToMultiLabelNameFailed);
288   }
289   settings.append_to_multi_label_name = reg_dword;
290 
291   if (!primary_dns_suffix_reader.ReadString(L"PrimaryDnsSuffix", &reg_string)) {
292     return base::unexpected(
293         ReadWinSystemDnsSettingsError::kReadPrimaryDnsSuffixPathFailed);
294   }
295   settings.primary_dns_suffix = std::move(reg_string);
296 
297   base::win::RegistryKeyIterator nrpt_rules(HKEY_LOCAL_MACHINE, kNrptPath);
298   base::win::RegistryKeyIterator cs_nrpt_rules(HKEY_LOCAL_MACHINE,
299                                                kControlSetNrptPath);
300   settings.have_name_resolution_policy =
301       (nrpt_rules.SubkeyCount() > 0 || cs_nrpt_rules.SubkeyCount() > 0);
302 
303   base::win::RegistryKeyIterator dns_connections(HKEY_LOCAL_MACHINE,
304                                                  kDnsConnectionsPath);
305   base::win::RegistryKeyIterator dns_connections_proxies(
306       HKEY_LOCAL_MACHINE, kDnsConnectionsProxies);
307   settings.have_proxy = (dns_connections.SubkeyCount() > 0 ||
308                          dns_connections_proxies.SubkeyCount() > 0);
309 
310   return settings;
311 }
312 
313 }  // namespace net
314