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