Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / net / base / network_interfaces_win.cc
blob6cb8fec7a063d71d96c31d84ea9c497bf42ad85f
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.
5 #include "net/base/net_util.h"
7 #include <iphlpapi.h>
8 #include <wlanapi.h>
10 #include <algorithm>
12 #include "base/files/file_path.h"
13 #include "base/lazy_instance.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/profiler/scoped_tracker.h"
16 #include "base/strings/string_piece.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/sys_string_conversions.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/threading/thread_restrictions.h"
21 #include "base/win/scoped_handle.h"
22 #include "base/win/windows_version.h"
23 #include "net/base/escape.h"
24 #include "net/base/ip_endpoint.h"
25 #include "net/base/net_errors.h"
26 #include "net/base/network_interfaces_win.h"
27 #include "url/gurl.h"
29 namespace net {
31 namespace {
33 // Converts Windows defined types to NetworkInterfaceType.
34 NetworkChangeNotifier::ConnectionType GetNetworkInterfaceType(DWORD ifType) {
35 // Bail out for pre-Vista versions of Windows which are documented to give
36 // inaccurate results like returning Ethernet for WiFi.
37 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366058.aspx
38 if (base::win::GetVersion() < base::win::VERSION_VISTA)
39 return NetworkChangeNotifier::CONNECTION_UNKNOWN;
41 NetworkChangeNotifier::ConnectionType type =
42 NetworkChangeNotifier::CONNECTION_UNKNOWN;
43 if (ifType == IF_TYPE_ETHERNET_CSMACD) {
44 type = NetworkChangeNotifier::CONNECTION_ETHERNET;
45 } else if (ifType == IF_TYPE_IEEE80211) {
46 type = NetworkChangeNotifier::CONNECTION_WIFI;
48 // TODO(mallinath) - Cellular?
49 return type;
52 // Returns scoped_ptr to WLAN_CONNECTION_ATTRIBUTES. The scoped_ptr may hold a
53 // NULL pointer if WLAN_CONNECTION_ATTRIBUTES is unavailable.
54 scoped_ptr<WLAN_CONNECTION_ATTRIBUTES, internal::WlanApiDeleter>
55 GetConnectionAttributes() {
56 const internal::WlanApi& wlanapi = internal::WlanApi::GetInstance();
57 if (!wlanapi.initialized)
58 return scoped_ptr<WLAN_CONNECTION_ATTRIBUTES, internal::WlanApiDeleter>();
60 internal::WlanHandle client;
61 DWORD cur_version = 0;
62 const DWORD kMaxClientVersion = 2;
64 // TODO(rtenneti): Remove ScopedTracker below once crbug.com/422516 is
65 // fixed.
66 tracked_objects::ScopedTracker tracking_profile(
67 FROM_HERE_WITH_EXPLICIT_FUNCTION("422516 OpenHandle()"));
68 DWORD result = wlanapi.OpenHandle(kMaxClientVersion, &cur_version, &client);
69 if (result != ERROR_SUCCESS)
70 return scoped_ptr<WLAN_CONNECTION_ATTRIBUTES, internal::WlanApiDeleter>();
73 WLAN_INTERFACE_INFO_LIST* interface_list_ptr = NULL;
74 DWORD result =
75 wlanapi.enum_interfaces_func(client.Get(), NULL, &interface_list_ptr);
76 if (result != ERROR_SUCCESS)
77 return scoped_ptr<WLAN_CONNECTION_ATTRIBUTES, internal::WlanApiDeleter>();
78 scoped_ptr<WLAN_INTERFACE_INFO_LIST, internal::WlanApiDeleter> interface_list(
79 interface_list_ptr);
81 // Assume at most one connected wifi interface.
82 WLAN_INTERFACE_INFO* info = NULL;
83 for (unsigned i = 0; i < interface_list->dwNumberOfItems; ++i) {
84 if (interface_list->InterfaceInfo[i].isState ==
85 wlan_interface_state_connected) {
86 info = &interface_list->InterfaceInfo[i];
87 break;
91 if (info == NULL)
92 return scoped_ptr<WLAN_CONNECTION_ATTRIBUTES, internal::WlanApiDeleter>();
94 WLAN_CONNECTION_ATTRIBUTES* conn_info_ptr = nullptr;
95 DWORD conn_info_size = 0;
96 WLAN_OPCODE_VALUE_TYPE op_code;
97 result = wlanapi.query_interface_func(
98 client.Get(), &info->InterfaceGuid, wlan_intf_opcode_current_connection,
99 NULL, &conn_info_size, reinterpret_cast<VOID**>(&conn_info_ptr),
100 &op_code);
101 if (result != ERROR_SUCCESS)
102 return scoped_ptr<WLAN_CONNECTION_ATTRIBUTES, internal::WlanApiDeleter>();
104 DCHECK(conn_info_ptr);
105 return scoped_ptr<WLAN_CONNECTION_ATTRIBUTES, internal::WlanApiDeleter>(
106 conn_info_ptr);
109 } // namespace
111 namespace internal {
113 base::LazyInstance<WlanApi>::Leaky lazy_wlanapi =
114 LAZY_INSTANCE_INITIALIZER;
116 WlanApi& WlanApi::GetInstance() {
117 return lazy_wlanapi.Get();
120 WlanApi::WlanApi() : initialized(false) {
121 // Use an absolute path to load the DLL to avoid DLL preloading attacks.
122 static const wchar_t* const kDLL = L"%WINDIR%\\system32\\wlanapi.dll";
123 wchar_t path[MAX_PATH] = {0};
124 ExpandEnvironmentStrings(kDLL, path, arraysize(path));
125 module = ::LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
126 if (!module)
127 return;
129 open_handle_func = reinterpret_cast<WlanOpenHandleFunc>(
130 ::GetProcAddress(module, "WlanOpenHandle"));
131 enum_interfaces_func = reinterpret_cast<WlanEnumInterfacesFunc>(
132 ::GetProcAddress(module, "WlanEnumInterfaces"));
133 query_interface_func = reinterpret_cast<WlanQueryInterfaceFunc>(
134 ::GetProcAddress(module, "WlanQueryInterface"));
135 set_interface_func = reinterpret_cast<WlanSetInterfaceFunc>(
136 ::GetProcAddress(module, "WlanSetInterface"));
137 free_memory_func = reinterpret_cast<WlanFreeMemoryFunc>(
138 ::GetProcAddress(module, "WlanFreeMemory"));
139 close_handle_func = reinterpret_cast<WlanCloseHandleFunc>(
140 ::GetProcAddress(module, "WlanCloseHandle"));
141 initialized = open_handle_func && enum_interfaces_func &&
142 query_interface_func && set_interface_func &&
143 free_memory_func && close_handle_func;
146 bool GetNetworkListImpl(NetworkInterfaceList* networks,
147 int policy,
148 bool is_xp,
149 const IP_ADAPTER_ADDRESSES* adapters) {
150 for (const IP_ADAPTER_ADDRESSES* adapter = adapters; adapter != NULL;
151 adapter = adapter->Next) {
152 // Ignore the loopback device.
153 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) {
154 continue;
157 if (adapter->OperStatus != IfOperStatusUp) {
158 continue;
161 // Ignore any HOST side vmware adapters with a description like:
162 // VMware Virtual Ethernet Adapter for VMnet1
163 // but don't ignore any GUEST side adapters with a description like:
164 // VMware Accelerated AMD PCNet Adapter #2
165 if ((policy & EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES) &&
166 strstr(adapter->AdapterName, "VMnet") != NULL) {
167 continue;
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 // XP has no OnLinkPrefixLength field.
178 size_t prefix_length = is_xp ? 0 : address->OnLinkPrefixLength;
179 if (is_xp) {
180 // Prior to Windows Vista the FirstPrefix pointed to the list with
181 // single prefix for each IP address assigned to the adapter.
182 // Order of FirstPrefix does not match order of FirstUnicastAddress,
183 // so we need to find corresponding prefix.
184 for (IP_ADAPTER_PREFIX* prefix = adapter->FirstPrefix; prefix;
185 prefix = prefix->Next) {
186 int prefix_family = prefix->Address.lpSockaddr->sa_family;
187 IPEndPoint network_endpoint;
188 if (prefix_family == family &&
189 network_endpoint.FromSockAddr(prefix->Address.lpSockaddr,
190 prefix->Address.iSockaddrLength) &&
191 IPNumberMatchesPrefix(endpoint.address(),
192 network_endpoint.address(),
193 prefix->PrefixLength)) {
194 prefix_length =
195 std::max<size_t>(prefix_length, prefix->PrefixLength);
200 // If the duplicate address detection (DAD) state is not changed to
201 // Preferred, skip this address.
202 if (address->DadState != IpDadStatePreferred) {
203 continue;
206 uint32_t index =
207 (family == AF_INET) ? adapter->IfIndex : adapter->Ipv6IfIndex;
209 // From http://technet.microsoft.com/en-us/ff568768(v=vs.60).aspx, the
210 // way to identify a temporary IPv6 Address is to check if
211 // PrefixOrigin is equal to IpPrefixOriginRouterAdvertisement and
212 // SuffixOrigin equal to IpSuffixOriginRandom.
213 int ip_address_attributes = IP_ADDRESS_ATTRIBUTE_NONE;
214 if (family == AF_INET6) {
215 if (address->PrefixOrigin == IpPrefixOriginRouterAdvertisement &&
216 address->SuffixOrigin == IpSuffixOriginRandom) {
217 ip_address_attributes |= IP_ADDRESS_ATTRIBUTE_TEMPORARY;
219 if (address->PreferredLifetime == 0) {
220 ip_address_attributes |= IP_ADDRESS_ATTRIBUTE_DEPRECATED;
223 networks->push_back(NetworkInterface(
224 adapter->AdapterName,
225 base::SysWideToNativeMB(adapter->FriendlyName), index,
226 GetNetworkInterfaceType(adapter->IfType), endpoint.address(),
227 prefix_length, ip_address_attributes));
232 return true;
235 } // namespace internal
237 bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
238 bool is_xp = base::win::GetVersion() < base::win::VERSION_VISTA;
239 ULONG len = 0;
240 ULONG flags = is_xp ? GAA_FLAG_INCLUDE_PREFIX : 0;
241 // GetAdaptersAddresses() may require IO operations.
242 base::ThreadRestrictions::AssertIOAllowed();
243 ULONG result = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, NULL, &len);
244 if (result != ERROR_BUFFER_OVERFLOW) {
245 // There are 0 networks.
246 return true;
248 scoped_ptr<char[]> buf(new char[len]);
249 IP_ADAPTER_ADDRESSES* adapters =
250 reinterpret_cast<IP_ADAPTER_ADDRESSES*>(buf.get());
251 result = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapters, &len);
252 if (result != NO_ERROR) {
253 LOG(ERROR) << "GetAdaptersAddresses failed: " << result;
254 return false;
257 return internal::GetNetworkListImpl(networks, policy, is_xp, adapters);
260 WifiPHYLayerProtocol GetWifiPHYLayerProtocol() {
261 auto conn_info = GetConnectionAttributes();
263 if (!conn_info.get())
264 return WIFI_PHY_LAYER_PROTOCOL_NONE;
266 switch (conn_info->wlanAssociationAttributes.dot11PhyType) {
267 case dot11_phy_type_fhss:
268 return WIFI_PHY_LAYER_PROTOCOL_ANCIENT;
269 case dot11_phy_type_dsss:
270 return WIFI_PHY_LAYER_PROTOCOL_B;
271 case dot11_phy_type_irbaseband:
272 return WIFI_PHY_LAYER_PROTOCOL_ANCIENT;
273 case dot11_phy_type_ofdm:
274 return WIFI_PHY_LAYER_PROTOCOL_A;
275 case dot11_phy_type_hrdsss:
276 return WIFI_PHY_LAYER_PROTOCOL_B;
277 case dot11_phy_type_erp:
278 return WIFI_PHY_LAYER_PROTOCOL_G;
279 case dot11_phy_type_ht:
280 return WIFI_PHY_LAYER_PROTOCOL_N;
281 default:
282 return WIFI_PHY_LAYER_PROTOCOL_UNKNOWN;
286 // Note: There is no need to explicitly set the options back
287 // as the OS will automatically set them back when the WlanHandle
288 // is closed.
289 class WifiOptionSetter : public ScopedWifiOptions {
290 public:
291 WifiOptionSetter(int options) {
292 const internal::WlanApi& wlanapi = internal::WlanApi::GetInstance();
293 if (!wlanapi.initialized)
294 return;
296 DWORD cur_version = 0;
297 const DWORD kMaxClientVersion = 2;
298 DWORD result = wlanapi.OpenHandle(
299 kMaxClientVersion, &cur_version, &client_);
300 if (result != ERROR_SUCCESS)
301 return;
303 WLAN_INTERFACE_INFO_LIST* interface_list_ptr = NULL;
304 result = wlanapi.enum_interfaces_func(client_.Get(), NULL,
305 &interface_list_ptr);
306 if (result != ERROR_SUCCESS)
307 return;
308 scoped_ptr<WLAN_INTERFACE_INFO_LIST, internal::WlanApiDeleter>
309 interface_list(interface_list_ptr);
311 for (unsigned i = 0; i < interface_list->dwNumberOfItems; ++i) {
312 WLAN_INTERFACE_INFO* info = &interface_list->InterfaceInfo[i];
313 if (options & WIFI_OPTIONS_DISABLE_SCAN) {
314 BOOL data = false;
315 wlanapi.set_interface_func(client_.Get(),
316 &info->InterfaceGuid,
317 wlan_intf_opcode_background_scan_enabled,
318 sizeof(data),
319 &data,
320 NULL);
322 if (options & WIFI_OPTIONS_MEDIA_STREAMING_MODE) {
323 BOOL data = true;
324 wlanapi.set_interface_func(client_.Get(),
325 &info->InterfaceGuid,
326 wlan_intf_opcode_media_streaming_mode,
327 sizeof(data),
328 &data,
329 NULL);
334 private:
335 internal::WlanHandle client_;
338 scoped_ptr<ScopedWifiOptions> SetWifiOptions(int options) {
339 return scoped_ptr<ScopedWifiOptions>(new WifiOptionSetter(options));
342 std::string GetWifiSSID() {
343 auto conn_info = GetConnectionAttributes();
345 if (!conn_info.get())
346 return "";
348 const DOT11_SSID dot11_ssid = conn_info->wlanAssociationAttributes.dot11Ssid;
349 return std::string(reinterpret_cast<const char*>(dot11_ssid.ucSSID),
350 dot11_ssid.uSSIDLength);
353 } // namespace net