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?
56 base::LazyInstance
<WlanApi
>::Leaky lazy_wlanapi
=
57 LAZY_INSTANCE_INITIALIZER
;
59 WlanApi
& WlanApi::GetInstance() {
60 return lazy_wlanapi
.Get();
63 WlanApi::WlanApi() : initialized(false) {
64 // Use an absolute path to load the DLL to avoid DLL preloading attacks.
65 static const wchar_t* const kDLL
= L
"%WINDIR%\\system32\\wlanapi.dll";
66 wchar_t path
[MAX_PATH
] = {0};
67 ExpandEnvironmentStrings(kDLL
, path
, arraysize(path
));
68 module
= ::LoadLibraryEx(path
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
72 open_handle_func
= reinterpret_cast<WlanOpenHandleFunc
>(
73 ::GetProcAddress(module
, "WlanOpenHandle"));
74 enum_interfaces_func
= reinterpret_cast<WlanEnumInterfacesFunc
>(
75 ::GetProcAddress(module
, "WlanEnumInterfaces"));
76 query_interface_func
= reinterpret_cast<WlanQueryInterfaceFunc
>(
77 ::GetProcAddress(module
, "WlanQueryInterface"));
78 set_interface_func
= reinterpret_cast<WlanSetInterfaceFunc
>(
79 ::GetProcAddress(module
, "WlanSetInterface"));
80 free_memory_func
= reinterpret_cast<WlanFreeMemoryFunc
>(
81 ::GetProcAddress(module
, "WlanFreeMemory"));
82 close_handle_func
= reinterpret_cast<WlanCloseHandleFunc
>(
83 ::GetProcAddress(module
, "WlanCloseHandle"));
84 initialized
= open_handle_func
&& enum_interfaces_func
&&
85 query_interface_func
&& set_interface_func
&&
86 free_memory_func
&& close_handle_func
;
89 bool GetNetworkListImpl(NetworkInterfaceList
* networks
,
92 const IP_ADAPTER_ADDRESSES
* adapters
) {
93 for (const IP_ADAPTER_ADDRESSES
* adapter
= adapters
; adapter
!= NULL
;
94 adapter
= adapter
->Next
) {
95 // Ignore the loopback device.
96 if (adapter
->IfType
== IF_TYPE_SOFTWARE_LOOPBACK
) {
100 if (adapter
->OperStatus
!= IfOperStatusUp
) {
104 // Ignore any HOST side vmware adapters with a description like:
105 // VMware Virtual Ethernet Adapter for VMnet1
106 // but don't ignore any GUEST side adapters with a description like:
107 // VMware Accelerated AMD PCNet Adapter #2
108 if ((policy
& EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES
) &&
109 strstr(adapter
->AdapterName
, "VMnet") != NULL
) {
113 for (IP_ADAPTER_UNICAST_ADDRESS
* address
= adapter
->FirstUnicastAddress
;
114 address
; address
= address
->Next
) {
115 int family
= address
->Address
.lpSockaddr
->sa_family
;
116 if (family
== AF_INET
|| family
== AF_INET6
) {
118 if (endpoint
.FromSockAddr(address
->Address
.lpSockaddr
,
119 address
->Address
.iSockaddrLength
)) {
120 // XP has no OnLinkPrefixLength field.
121 size_t prefix_length
= is_xp
? 0 : address
->OnLinkPrefixLength
;
123 // Prior to Windows Vista the FirstPrefix pointed to the list with
124 // single prefix for each IP address assigned to the adapter.
125 // Order of FirstPrefix does not match order of FirstUnicastAddress,
126 // so we need to find corresponding prefix.
127 for (IP_ADAPTER_PREFIX
* prefix
= adapter
->FirstPrefix
; prefix
;
128 prefix
= prefix
->Next
) {
129 int prefix_family
= prefix
->Address
.lpSockaddr
->sa_family
;
130 IPEndPoint network_endpoint
;
131 if (prefix_family
== family
&&
132 network_endpoint
.FromSockAddr(prefix
->Address
.lpSockaddr
,
133 prefix
->Address
.iSockaddrLength
) &&
134 IPNumberMatchesPrefix(endpoint
.address(),
135 network_endpoint
.address(),
136 prefix
->PrefixLength
)) {
138 std::max
<size_t>(prefix_length
, prefix
->PrefixLength
);
143 // If the duplicate address detection (DAD) state is not changed to
144 // Preferred, skip this address.
145 if (address
->DadState
!= IpDadStatePreferred
) {
150 (family
== AF_INET
) ? adapter
->IfIndex
: adapter
->Ipv6IfIndex
;
152 // From http://technet.microsoft.com/en-us/ff568768(v=vs.60).aspx, the
153 // way to identify a temporary IPv6 Address is to check if
154 // PrefixOrigin is equal to IpPrefixOriginRouterAdvertisement and
155 // SuffixOrigin equal to IpSuffixOriginRandom.
156 int ip_address_attributes
= IP_ADDRESS_ATTRIBUTE_NONE
;
157 if (family
== AF_INET6
) {
158 if (address
->PrefixOrigin
== IpPrefixOriginRouterAdvertisement
&&
159 address
->SuffixOrigin
== IpSuffixOriginRandom
) {
160 ip_address_attributes
|= IP_ADDRESS_ATTRIBUTE_TEMPORARY
;
162 if (address
->PreferredLifetime
== 0) {
163 ip_address_attributes
|= IP_ADDRESS_ATTRIBUTE_DEPRECATED
;
166 networks
->push_back(NetworkInterface(
167 adapter
->AdapterName
,
168 base::SysWideToNativeMB(adapter
->FriendlyName
), index
,
169 GetNetworkInterfaceType(adapter
->IfType
), endpoint
.address(),
170 prefix_length
, ip_address_attributes
));
178 } // namespace internal
180 bool GetNetworkList(NetworkInterfaceList
* networks
, int policy
) {
181 bool is_xp
= base::win::GetVersion() < base::win::VERSION_VISTA
;
183 ULONG flags
= is_xp
? GAA_FLAG_INCLUDE_PREFIX
: 0;
184 // GetAdaptersAddresses() may require IO operations.
185 base::ThreadRestrictions::AssertIOAllowed();
186 ULONG result
= GetAdaptersAddresses(AF_UNSPEC
, flags
, NULL
, NULL
, &len
);
187 if (result
!= ERROR_BUFFER_OVERFLOW
) {
188 // There are 0 networks.
191 scoped_ptr
<char[]> buf(new char[len
]);
192 IP_ADAPTER_ADDRESSES
* adapters
=
193 reinterpret_cast<IP_ADAPTER_ADDRESSES
*>(buf
.get());
194 result
= GetAdaptersAddresses(AF_UNSPEC
, flags
, NULL
, adapters
, &len
);
195 if (result
!= NO_ERROR
) {
196 LOG(ERROR
) << "GetAdaptersAddresses failed: " << result
;
200 return internal::GetNetworkListImpl(networks
, policy
, is_xp
, adapters
);
203 WifiPHYLayerProtocol
GetWifiPHYLayerProtocol() {
204 const internal::WlanApi
& wlanapi
= internal::WlanApi::GetInstance();
205 if (!wlanapi
.initialized
)
206 return WIFI_PHY_LAYER_PROTOCOL_NONE
;
208 internal::WlanHandle client
;
209 DWORD cur_version
= 0;
210 const DWORD kMaxClientVersion
= 2;
212 // TODO(rtenneti): Remove ScopedTracker below once crbug.com/422516 is
214 tracked_objects::ScopedTracker
tracking_profile(
215 FROM_HERE_WITH_EXPLICIT_FUNCTION("422516 OpenHandle()"));
216 DWORD result
= wlanapi
.OpenHandle(kMaxClientVersion
, &cur_version
, &client
);
217 if (result
!= ERROR_SUCCESS
)
218 return WIFI_PHY_LAYER_PROTOCOL_NONE
;
221 WLAN_INTERFACE_INFO_LIST
* interface_list_ptr
= NULL
;
223 wlanapi
.enum_interfaces_func(client
.Get(), NULL
, &interface_list_ptr
);
224 if (result
!= ERROR_SUCCESS
)
225 return WIFI_PHY_LAYER_PROTOCOL_NONE
;
226 scoped_ptr
<WLAN_INTERFACE_INFO_LIST
, internal::WlanApiDeleter
> interface_list(
229 // Assume at most one connected wifi interface.
230 WLAN_INTERFACE_INFO
* info
= NULL
;
231 for (unsigned i
= 0; i
< interface_list
->dwNumberOfItems
; ++i
) {
232 if (interface_list
->InterfaceInfo
[i
].isState
==
233 wlan_interface_state_connected
) {
234 info
= &interface_list
->InterfaceInfo
[i
];
240 return WIFI_PHY_LAYER_PROTOCOL_NONE
;
242 WLAN_CONNECTION_ATTRIBUTES
* conn_info_ptr
;
243 DWORD conn_info_size
= 0;
244 WLAN_OPCODE_VALUE_TYPE op_code
;
245 result
= wlanapi
.query_interface_func(
246 client
.Get(), &info
->InterfaceGuid
, wlan_intf_opcode_current_connection
,
247 NULL
, &conn_info_size
, reinterpret_cast<VOID
**>(&conn_info_ptr
),
249 if (result
!= ERROR_SUCCESS
)
250 return WIFI_PHY_LAYER_PROTOCOL_UNKNOWN
;
251 scoped_ptr
<WLAN_CONNECTION_ATTRIBUTES
, internal::WlanApiDeleter
> conn_info(
254 switch (conn_info
->wlanAssociationAttributes
.dot11PhyType
) {
255 case dot11_phy_type_fhss
:
256 return WIFI_PHY_LAYER_PROTOCOL_ANCIENT
;
257 case dot11_phy_type_dsss
:
258 return WIFI_PHY_LAYER_PROTOCOL_B
;
259 case dot11_phy_type_irbaseband
:
260 return WIFI_PHY_LAYER_PROTOCOL_ANCIENT
;
261 case dot11_phy_type_ofdm
:
262 return WIFI_PHY_LAYER_PROTOCOL_A
;
263 case dot11_phy_type_hrdsss
:
264 return WIFI_PHY_LAYER_PROTOCOL_B
;
265 case dot11_phy_type_erp
:
266 return WIFI_PHY_LAYER_PROTOCOL_G
;
267 case dot11_phy_type_ht
:
268 return WIFI_PHY_LAYER_PROTOCOL_N
;
270 return WIFI_PHY_LAYER_PROTOCOL_UNKNOWN
;
274 // Note: There is no need to explicitly set the options back
275 // as the OS will automatically set them back when the WlanHandle
277 class WifiOptionSetter
: public ScopedWifiOptions
{
279 WifiOptionSetter(int options
) {
280 const internal::WlanApi
& wlanapi
= internal::WlanApi::GetInstance();
281 if (!wlanapi
.initialized
)
284 DWORD cur_version
= 0;
285 const DWORD kMaxClientVersion
= 2;
286 DWORD result
= wlanapi
.OpenHandle(
287 kMaxClientVersion
, &cur_version
, &client_
);
288 if (result
!= ERROR_SUCCESS
)
291 WLAN_INTERFACE_INFO_LIST
* interface_list_ptr
= NULL
;
292 result
= wlanapi
.enum_interfaces_func(client_
.Get(), NULL
,
293 &interface_list_ptr
);
294 if (result
!= ERROR_SUCCESS
)
296 scoped_ptr
<WLAN_INTERFACE_INFO_LIST
, internal::WlanApiDeleter
>
297 interface_list(interface_list_ptr
);
299 for (unsigned i
= 0; i
< interface_list
->dwNumberOfItems
; ++i
) {
300 WLAN_INTERFACE_INFO
* info
= &interface_list
->InterfaceInfo
[i
];
301 if (options
& WIFI_OPTIONS_DISABLE_SCAN
) {
303 wlanapi
.set_interface_func(client_
.Get(),
304 &info
->InterfaceGuid
,
305 wlan_intf_opcode_background_scan_enabled
,
310 if (options
& WIFI_OPTIONS_MEDIA_STREAMING_MODE
) {
312 wlanapi
.set_interface_func(client_
.Get(),
313 &info
->InterfaceGuid
,
314 wlan_intf_opcode_media_streaming_mode
,
323 internal::WlanHandle client_
;
326 scoped_ptr
<ScopedWifiOptions
> SetWifiOptions(int options
) {
327 return scoped_ptr
<ScopedWifiOptions
>(new WifiOptionSetter(options
));
330 std::string
GetWifiSSID() {