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"
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"
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?
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
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
;
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(
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
];
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
),
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
>(
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
);
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
,
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
) {
157 if (adapter
->OperStatus
!= IfOperStatusUp
) {
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
) {
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
) {
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
;
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
)) {
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
) {
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
));
235 } // namespace internal
237 bool GetNetworkList(NetworkInterfaceList
* networks
, int policy
) {
238 bool is_xp
= base::win::GetVersion() < base::win::VERSION_VISTA
;
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.
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
;
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
;
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
289 class WifiOptionSetter
: public ScopedWifiOptions
{
291 WifiOptionSetter(int options
) {
292 const internal::WlanApi
& wlanapi
= internal::WlanApi::GetInstance();
293 if (!wlanapi
.initialized
)
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
)
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
)
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
) {
315 wlanapi
.set_interface_func(client_
.Get(),
316 &info
->InterfaceGuid
,
317 wlan_intf_opcode_background_scan_enabled
,
322 if (options
& WIFI_OPTIONS_MEDIA_STREAMING_MODE
) {
324 wlanapi
.set_interface_func(client_
.Get(),
325 &info
->InterfaceGuid
,
326 wlan_intf_opcode_media_streaming_mode
,
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())
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
);