• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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