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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "net/base/network_interfaces_win.h"
11
12 #include <algorithm>
13 #include <memory>
14 #include <string_view>
15
16 #include "base/containers/heap_array.h"
17 #include "base/files/file_path.h"
18 #include "base/lazy_instance.h"
19 #include "base/strings/escape.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/sys_string_conversions.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/threading/scoped_blocking_call.h"
24 #include "base/threading/scoped_thread_priority.h"
25 #include "base/win/scoped_handle.h"
26 #include "net/base/ip_endpoint.h"
27 #include "net/base/net_errors.h"
28 #include "url/gurl.h"
29
30 namespace net {
31
32 namespace {
33
34 // Converts Windows defined types to NetworkInterfaceType.
GetNetworkInterfaceType(DWORD ifType)35 NetworkChangeNotifier::ConnectionType GetNetworkInterfaceType(DWORD ifType) {
36 NetworkChangeNotifier::ConnectionType type =
37 NetworkChangeNotifier::CONNECTION_UNKNOWN;
38 if (ifType == IF_TYPE_ETHERNET_CSMACD) {
39 type = NetworkChangeNotifier::CONNECTION_ETHERNET;
40 } else if (ifType == IF_TYPE_IEEE80211) {
41 type = NetworkChangeNotifier::CONNECTION_WIFI;
42 }
43 // TODO(mallinath) - Cellular?
44 return type;
45 }
46
47 // Returns scoped_ptr to WLAN_CONNECTION_ATTRIBUTES. The scoped_ptr may hold a
48 // NULL pointer if WLAN_CONNECTION_ATTRIBUTES is unavailable.
49 std::unique_ptr<WLAN_CONNECTION_ATTRIBUTES, internal::WlanApiDeleter>
GetConnectionAttributes()50 GetConnectionAttributes() {
51 const internal::WlanApi& wlanapi = internal::WlanApi::GetInstance();
52 std::unique_ptr<WLAN_CONNECTION_ATTRIBUTES, internal::WlanApiDeleter>
53 wlan_connection_attributes;
54 if (!wlanapi.initialized)
55 return wlan_connection_attributes;
56
57 internal::WlanHandle client;
58 DWORD cur_version = 0;
59 const DWORD kMaxClientVersion = 2;
60 DWORD result = wlanapi.OpenHandle(kMaxClientVersion, &cur_version, &client);
61 if (result != ERROR_SUCCESS)
62 return wlan_connection_attributes;
63
64 WLAN_INTERFACE_INFO_LIST* interface_list_ptr = nullptr;
65 result =
66 wlanapi.enum_interfaces_func(client.Get(), nullptr, &interface_list_ptr);
67 if (result != ERROR_SUCCESS)
68 return wlan_connection_attributes;
69 std::unique_ptr<WLAN_INTERFACE_INFO_LIST, internal::WlanApiDeleter>
70 interface_list(interface_list_ptr);
71
72 // Assume at most one connected wifi interface.
73 WLAN_INTERFACE_INFO* info = nullptr;
74 for (unsigned i = 0; i < interface_list->dwNumberOfItems; ++i) {
75 if (interface_list->InterfaceInfo[i].isState ==
76 wlan_interface_state_connected) {
77 info = &interface_list->InterfaceInfo[i];
78 break;
79 }
80 }
81
82 if (info == nullptr)
83 return wlan_connection_attributes;
84
85 WLAN_CONNECTION_ATTRIBUTES* conn_info_ptr = nullptr;
86 DWORD conn_info_size = 0;
87 WLAN_OPCODE_VALUE_TYPE op_code;
88 result = wlanapi.query_interface_func(
89 client.Get(), &info->InterfaceGuid, wlan_intf_opcode_current_connection,
90 nullptr, &conn_info_size, reinterpret_cast<VOID**>(&conn_info_ptr),
91 &op_code);
92 wlan_connection_attributes.reset(conn_info_ptr);
93 if (result == ERROR_SUCCESS)
94 DCHECK(conn_info_ptr);
95 else
96 wlan_connection_attributes.reset();
97 return wlan_connection_attributes;
98 }
99
100 } // namespace
101
102 namespace internal {
103
104 base::LazyInstance<WlanApi>::Leaky lazy_wlanapi =
105 LAZY_INSTANCE_INITIALIZER;
106
GetInstance()107 WlanApi& WlanApi::GetInstance() {
108 return lazy_wlanapi.Get();
109 }
110
WlanApi()111 WlanApi::WlanApi() : initialized(false) {
112 // Mitigate the issues caused by loading DLLs on a background thread
113 // (http://crbug/973868).
114 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
115
116 HMODULE module =
117 ::LoadLibraryEx(L"wlanapi.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
118 if (!module)
119 return;
120
121 open_handle_func = reinterpret_cast<WlanOpenHandleFunc>(
122 ::GetProcAddress(module, "WlanOpenHandle"));
123 enum_interfaces_func = reinterpret_cast<WlanEnumInterfacesFunc>(
124 ::GetProcAddress(module, "WlanEnumInterfaces"));
125 query_interface_func = reinterpret_cast<WlanQueryInterfaceFunc>(
126 ::GetProcAddress(module, "WlanQueryInterface"));
127 set_interface_func = reinterpret_cast<WlanSetInterfaceFunc>(
128 ::GetProcAddress(module, "WlanSetInterface"));
129 free_memory_func = reinterpret_cast<WlanFreeMemoryFunc>(
130 ::GetProcAddress(module, "WlanFreeMemory"));
131 close_handle_func = reinterpret_cast<WlanCloseHandleFunc>(
132 ::GetProcAddress(module, "WlanCloseHandle"));
133 initialized = open_handle_func && enum_interfaces_func &&
134 query_interface_func && set_interface_func &&
135 free_memory_func && close_handle_func;
136 }
137
GetNetworkListImpl(NetworkInterfaceList * networks,int policy,const IP_ADAPTER_ADDRESSES * adapters)138 bool GetNetworkListImpl(NetworkInterfaceList* networks,
139 int policy,
140 const IP_ADAPTER_ADDRESSES* adapters) {
141 for (const IP_ADAPTER_ADDRESSES* adapter = adapters; adapter != nullptr;
142 adapter = adapter->Next) {
143 // Ignore the loopback device.
144 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) {
145 continue;
146 }
147
148 if (adapter->OperStatus != IfOperStatusUp) {
149 continue;
150 }
151
152 // Ignore any HOST side vmware adapters with a description like:
153 // VMware Virtual Ethernet Adapter for VMnet1
154 // but don't ignore any GUEST side adapters with a description like:
155 // VMware Accelerated AMD PCNet Adapter #2
156 if ((policy & EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES) &&
157 strstr(adapter->AdapterName, "VMnet") != nullptr) {
158 continue;
159 }
160
161 std::optional<Eui48MacAddress> mac_address;
162 mac_address.emplace();
163 if (adapter->PhysicalAddressLength == mac_address->size()) {
164 std::copy_n(reinterpret_cast<const uint8_t*>(adapter->PhysicalAddress),
165 mac_address->size(), mac_address->begin());
166 } else {
167 mac_address.reset();
168 }
169
170 for (IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress;
171 address; address = address->Next) {
172 int family = address->Address.lpSockaddr->sa_family;
173 if (family == AF_INET || family == AF_INET6) {
174 IPEndPoint endpoint;
175 if (endpoint.FromSockAddr(address->Address.lpSockaddr,
176 address->Address.iSockaddrLength)) {
177 size_t prefix_length = address->OnLinkPrefixLength;
178
179 // If the duplicate address detection (DAD) state is not changed to
180 // Preferred, skip this address.
181 if (address->DadState != IpDadStatePreferred) {
182 continue;
183 }
184
185 uint32_t index =
186 (family == AF_INET) ? adapter->IfIndex : adapter->Ipv6IfIndex;
187
188 // From http://technet.microsoft.com/en-us/ff568768(v=vs.60).aspx, the
189 // way to identify a temporary IPv6 Address is to check if
190 // PrefixOrigin is equal to IpPrefixOriginRouterAdvertisement and
191 // SuffixOrigin equal to IpSuffixOriginRandom.
192 int ip_address_attributes = IP_ADDRESS_ATTRIBUTE_NONE;
193 if (family == AF_INET6) {
194 if (address->PrefixOrigin == IpPrefixOriginRouterAdvertisement &&
195 address->SuffixOrigin == IpSuffixOriginRandom) {
196 ip_address_attributes |= IP_ADDRESS_ATTRIBUTE_TEMPORARY;
197 }
198 if (address->PreferredLifetime == 0) {
199 ip_address_attributes |= IP_ADDRESS_ATTRIBUTE_DEPRECATED;
200 }
201 }
202 networks->push_back(NetworkInterface(
203 adapter->AdapterName,
204 base::SysWideToNativeMB(adapter->FriendlyName), index,
205 GetNetworkInterfaceType(adapter->IfType), endpoint.address(),
206 prefix_length, ip_address_attributes, mac_address));
207 }
208 }
209 }
210 }
211 return true;
212 }
213
214 } // namespace internal
215
GetNetworkList(NetworkInterfaceList * networks,int policy)216 bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
217 // Max number of times to retry GetAdaptersAddresses due to
218 // ERROR_BUFFER_OVERFLOW. If GetAdaptersAddresses returns this indefinitely
219 // due to an unforseen reason, we don't want to be stuck in an endless loop.
220 static constexpr int MAX_GETADAPTERSADDRESSES_TRIES = 10;
221 // Use an initial buffer size of 15KB, as recommended by MSDN. See:
222 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx
223 static constexpr int INITIAL_BUFFER_SIZE = 15000;
224
225 ULONG len = INITIAL_BUFFER_SIZE;
226 ULONG flags = 0;
227 // Initial buffer allocated on stack.
228 char initial_buf[INITIAL_BUFFER_SIZE];
229 // Dynamic buffer in case initial buffer isn't large enough.
230 base::HeapArray<char> buf;
231
232 IP_ADAPTER_ADDRESSES* adapters = nullptr;
233 {
234 // GetAdaptersAddresses() may require IO operations.
235 base::ScopedBlockingCall scoped_blocking_call(
236 FROM_HERE, base::BlockingType::MAY_BLOCK);
237
238 adapters = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(&initial_buf);
239 ULONG result =
240 GetAdaptersAddresses(AF_UNSPEC, flags, nullptr, adapters, &len);
241
242 // If we get ERROR_BUFFER_OVERFLOW, call GetAdaptersAddresses in a loop,
243 // because the required size may increase between successive calls,
244 // resulting in ERROR_BUFFER_OVERFLOW multiple times.
245 for (int tries = 1; result == ERROR_BUFFER_OVERFLOW &&
246 tries < MAX_GETADAPTERSADDRESSES_TRIES;
247 ++tries) {
248 buf = base::HeapArray<char>::Uninit(len);
249 adapters = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(buf.data());
250 result = GetAdaptersAddresses(AF_UNSPEC, flags, nullptr, adapters, &len);
251 }
252
253 if (result == ERROR_NO_DATA) {
254 // There are 0 networks.
255 return true;
256 } else if (result != NO_ERROR) {
257 LOG(ERROR) << "GetAdaptersAddresses failed: " << result;
258 return false;
259 }
260 }
261
262 return internal::GetNetworkListImpl(networks, policy, adapters);
263 }
264
265 // Note: There is no need to explicitly set the options back
266 // as the OS will automatically set them back when the WlanHandle
267 // is closed.
268 class WifiOptionSetter : public ScopedWifiOptions {
269 public:
WifiOptionSetter(int options)270 WifiOptionSetter(int options) {
271 const internal::WlanApi& wlanapi = internal::WlanApi::GetInstance();
272 if (!wlanapi.initialized)
273 return;
274
275 DWORD cur_version = 0;
276 const DWORD kMaxClientVersion = 2;
277 DWORD result = wlanapi.OpenHandle(
278 kMaxClientVersion, &cur_version, &client_);
279 if (result != ERROR_SUCCESS)
280 return;
281
282 WLAN_INTERFACE_INFO_LIST* interface_list_ptr = nullptr;
283 result = wlanapi.enum_interfaces_func(client_.Get(), nullptr,
284 &interface_list_ptr);
285 if (result != ERROR_SUCCESS)
286 return;
287 std::unique_ptr<WLAN_INTERFACE_INFO_LIST, internal::WlanApiDeleter>
288 interface_list(interface_list_ptr);
289
290 for (unsigned i = 0; i < interface_list->dwNumberOfItems; ++i) {
291 WLAN_INTERFACE_INFO* info = &interface_list->InterfaceInfo[i];
292 if (options & WIFI_OPTIONS_DISABLE_SCAN) {
293 BOOL data = false;
294 wlanapi.set_interface_func(client_.Get(), &info->InterfaceGuid,
295 wlan_intf_opcode_background_scan_enabled,
296 sizeof(data), &data, nullptr);
297 }
298 if (options & WIFI_OPTIONS_MEDIA_STREAMING_MODE) {
299 BOOL data = true;
300 wlanapi.set_interface_func(client_.Get(), &info->InterfaceGuid,
301 wlan_intf_opcode_media_streaming_mode,
302 sizeof(data), &data, nullptr);
303 }
304 }
305 }
306
307 private:
308 internal::WlanHandle client_;
309 };
310
SetWifiOptions(int options)311 std::unique_ptr<ScopedWifiOptions> SetWifiOptions(int options) {
312 return std::make_unique<WifiOptionSetter>(options);
313 }
314
GetWifiSSID()315 std::string GetWifiSSID() {
316 auto conn_info = GetConnectionAttributes();
317
318 if (!conn_info.get())
319 return "";
320
321 const DOT11_SSID dot11_ssid = conn_info->wlanAssociationAttributes.dot11Ssid;
322 return std::string(reinterpret_cast<const char*>(dot11_ssid.ucSSID),
323 dot11_ssid.uSSIDLength);
324 }
325
326 } // namespace net
327