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