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/net_util.h" 6 7 #include <iphlpapi.h> 8 #include <wlanapi.h> 9 10 #include <algorithm> 11 12 #include "base/files/file_path.h" 13 #include "base/lazy_instance.h" 14 #include "base/memory/scoped_ptr.h" 15 #include "base/strings/string_piece.h" 16 #include "base/strings/string_util.h" 17 #include "base/strings/sys_string_conversions.h" 18 #include "base/strings/utf_string_conversions.h" 19 #include "base/threading/thread_restrictions.h" 20 #include "base/win/scoped_handle.h" 21 #include "base/win/windows_version.h" 22 #include "net/base/escape.h" 23 #include "net/base/ip_endpoint.h" 24 #include "net/base/net_errors.h" 25 #include "net/base/net_util_win.h" 26 #include "url/gurl.h" 27 28 namespace net { 29 30 namespace { 31 32 // Converts Windows defined types to NetworkInterfaceType. 33 NetworkChangeNotifier::ConnectionType GetNetworkInterfaceType(DWORD ifType) { 34 // Bail out for pre-Vista versions of Windows which are documented to give 35 // inaccurate results like returning Ethernet for WiFi. 36 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366058.aspx 37 if (base::win::GetVersion() < base::win::VERSION_VISTA) 38 return NetworkChangeNotifier::CONNECTION_UNKNOWN; 39 40 NetworkChangeNotifier::ConnectionType type = 41 NetworkChangeNotifier::CONNECTION_UNKNOWN; 42 if (ifType == IF_TYPE_ETHERNET_CSMACD) { 43 type = NetworkChangeNotifier::CONNECTION_ETHERNET; 44 } else if (ifType == IF_TYPE_IEEE80211) { 45 type = NetworkChangeNotifier::CONNECTION_WIFI; 46 } 47 // TODO(mallinath) - Cellular? 48 return type; 49 } 50 51 } // namespace 52 53 namespace internal { 54 55 base::LazyInstance<WlanApi>::Leaky lazy_wlanapi = 56 LAZY_INSTANCE_INITIALIZER; 57 58 WlanApi& WlanApi::GetInstance() { 59 return lazy_wlanapi.Get(); 60 } 61 62 WlanApi::WlanApi() : initialized(false) { 63 // Use an absolute path to load the DLL to avoid DLL preloading attacks. 64 static const wchar_t* const kDLL = L"%WINDIR%\\system32\\wlanapi.dll"; 65 wchar_t path[MAX_PATH] = {0}; 66 ExpandEnvironmentStrings(kDLL, path, arraysize(path)); 67 module = ::LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); 68 if (!module) 69 return; 70 71 open_handle_func = reinterpret_cast<WlanOpenHandleFunc>( 72 ::GetProcAddress(module, "WlanOpenHandle")); 73 enum_interfaces_func = reinterpret_cast<WlanEnumInterfacesFunc>( 74 ::GetProcAddress(module, "WlanEnumInterfaces")); 75 query_interface_func = reinterpret_cast<WlanQueryInterfaceFunc>( 76 ::GetProcAddress(module, "WlanQueryInterface")); 77 set_interface_func = reinterpret_cast<WlanSetInterfaceFunc>( 78 ::GetProcAddress(module, "WlanSetInterface")); 79 free_memory_func = reinterpret_cast<WlanFreeMemoryFunc>( 80 ::GetProcAddress(module, "WlanFreeMemory")); 81 close_handle_func = reinterpret_cast<WlanCloseHandleFunc>( 82 ::GetProcAddress(module, "WlanCloseHandle")); 83 initialized = open_handle_func && enum_interfaces_func && 84 query_interface_func && set_interface_func && 85 free_memory_func && close_handle_func; 86 } 87 88 } // namespace internal 89 90 bool GetNetworkList(NetworkInterfaceList* networks, int policy) { 91 // GetAdaptersAddresses() may require IO operations. 92 base::ThreadRestrictions::AssertIOAllowed(); 93 bool is_xp = base::win::GetVersion() < base::win::VERSION_VISTA; 94 ULONG len = 0; 95 ULONG flags = is_xp ? GAA_FLAG_INCLUDE_PREFIX : 0; 96 // First get number of networks. 97 ULONG result = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, NULL, &len); 98 if (result != ERROR_BUFFER_OVERFLOW) { 99 // There are 0 networks. 100 return true; 101 } 102 scoped_ptr<char[]> buf(new char[len]); 103 IP_ADAPTER_ADDRESSES *adapters = 104 reinterpret_cast<IP_ADAPTER_ADDRESSES *>(buf.get()); 105 result = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapters, &len); 106 if (result != NO_ERROR) { 107 LOG(ERROR) << "GetAdaptersAddresses failed: " << result; 108 return false; 109 } 110 111 // These two variables are used below when this method is asked to pick a 112 // IPv6 address which has the shortest lifetime. 113 ULONG ipv6_valid_lifetime = 0; 114 scoped_ptr<NetworkInterface> ipv6_address; 115 116 for (IP_ADAPTER_ADDRESSES *adapter = adapters; adapter != NULL; 117 adapter = adapter->Next) { 118 // Ignore the loopback device. 119 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) { 120 continue; 121 } 122 123 if (adapter->OperStatus != IfOperStatusUp) { 124 continue; 125 } 126 127 // Ignore any HOST side vmware adapters with a description like: 128 // VMware Virtual Ethernet Adapter for VMnet1 129 // but don't ignore any GUEST side adapters with a description like: 130 // VMware Accelerated AMD PCNet Adapter #2 131 if (policy == EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES && 132 strstr(adapter->AdapterName, "VMnet") != NULL) { 133 continue; 134 } 135 136 for (IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress; 137 address; address = address->Next) { 138 int family = address->Address.lpSockaddr->sa_family; 139 if (family == AF_INET || family == AF_INET6) { 140 IPEndPoint endpoint; 141 if (endpoint.FromSockAddr(address->Address.lpSockaddr, 142 address->Address.iSockaddrLength)) { 143 // XP has no OnLinkPrefixLength field. 144 size_t net_prefix = is_xp ? 0 : address->OnLinkPrefixLength; 145 if (is_xp) { 146 // Prior to Windows Vista the FirstPrefix pointed to the list with 147 // single prefix for each IP address assigned to the adapter. 148 // Order of FirstPrefix does not match order of FirstUnicastAddress, 149 // so we need to find corresponding prefix. 150 for (IP_ADAPTER_PREFIX* prefix = adapter->FirstPrefix; prefix; 151 prefix = prefix->Next) { 152 int prefix_family = prefix->Address.lpSockaddr->sa_family; 153 IPEndPoint network_endpoint; 154 if (prefix_family == family && 155 network_endpoint.FromSockAddr(prefix->Address.lpSockaddr, 156 prefix->Address.iSockaddrLength) && 157 IPNumberMatchesPrefix(endpoint.address(), 158 network_endpoint.address(), 159 prefix->PrefixLength)) { 160 net_prefix = std::max<size_t>(net_prefix, prefix->PrefixLength); 161 } 162 } 163 } 164 uint32 index = 165 (family == AF_INET) ? adapter->IfIndex : adapter->Ipv6IfIndex; 166 // Pick one IPv6 address with least valid lifetime. 167 // The reason we are checking |ValidLifeftime| as there is no other 168 // way identifying the interface type. Usually (and most likely) temp 169 // IPv6 will have a shorter ValidLifetime value then the permanent 170 // interface. 171 if (family == AF_INET6 && 172 (policy & INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE)) { 173 if (ipv6_valid_lifetime == 0 || 174 ipv6_valid_lifetime > address->ValidLifetime) { 175 ipv6_valid_lifetime = address->ValidLifetime; 176 ipv6_address.reset(new NetworkInterface( 177 adapter->AdapterName, 178 base::SysWideToNativeMB(adapter->FriendlyName), 179 index, 180 GetNetworkInterfaceType(adapter->IfType), 181 endpoint.address(), 182 net_prefix, 183 IP_ADDRESS_ATTRIBUTE_NONE)); 184 continue; 185 } 186 } 187 networks->push_back( 188 NetworkInterface(adapter->AdapterName, 189 base::SysWideToNativeMB(adapter->FriendlyName), 190 index, 191 GetNetworkInterfaceType(adapter->IfType), 192 endpoint.address(), 193 net_prefix, 194 IP_ADDRESS_ATTRIBUTE_NONE)); 195 } 196 } 197 } 198 } 199 200 if (ipv6_address.get()) { 201 networks->push_back(*(ipv6_address.get())); 202 } 203 return true; 204 } 205 206 WifiPHYLayerProtocol GetWifiPHYLayerProtocol() { 207 const internal::WlanApi& wlanapi = internal::WlanApi::GetInstance(); 208 if (!wlanapi.initialized) 209 return WIFI_PHY_LAYER_PROTOCOL_NONE; 210 211 internal::WlanHandle client; 212 DWORD cur_version = 0; 213 const DWORD kMaxClientVersion = 2; 214 DWORD result = wlanapi.OpenHandle(kMaxClientVersion, &cur_version, &client); 215 if (result != ERROR_SUCCESS) 216 return WIFI_PHY_LAYER_PROTOCOL_NONE; 217 218 WLAN_INTERFACE_INFO_LIST* interface_list_ptr = NULL; 219 result = wlanapi.enum_interfaces_func(client.Get(), NULL, 220 &interface_list_ptr); 221 if (result != ERROR_SUCCESS) 222 return WIFI_PHY_LAYER_PROTOCOL_NONE; 223 scoped_ptr<WLAN_INTERFACE_INFO_LIST, internal::WlanApiDeleter> interface_list( 224 interface_list_ptr); 225 226 // Assume at most one connected wifi interface. 227 WLAN_INTERFACE_INFO* info = NULL; 228 for (unsigned i = 0; i < interface_list->dwNumberOfItems; ++i) { 229 if (interface_list->InterfaceInfo[i].isState == 230 wlan_interface_state_connected) { 231 info = &interface_list->InterfaceInfo[i]; 232 break; 233 } 234 } 235 236 if (info == NULL) 237 return WIFI_PHY_LAYER_PROTOCOL_NONE; 238 239 WLAN_CONNECTION_ATTRIBUTES* conn_info_ptr; 240 DWORD conn_info_size = 0; 241 WLAN_OPCODE_VALUE_TYPE op_code; 242 result = wlanapi.query_interface_func( 243 client.Get(), &info->InterfaceGuid, wlan_intf_opcode_current_connection, 244 NULL, &conn_info_size, reinterpret_cast<VOID**>(&conn_info_ptr), 245 &op_code); 246 if (result != ERROR_SUCCESS) 247 return WIFI_PHY_LAYER_PROTOCOL_UNKNOWN; 248 scoped_ptr<WLAN_CONNECTION_ATTRIBUTES, internal::WlanApiDeleter> conn_info( 249 conn_info_ptr); 250 251 switch (conn_info->wlanAssociationAttributes.dot11PhyType) { 252 case dot11_phy_type_fhss: 253 return WIFI_PHY_LAYER_PROTOCOL_ANCIENT; 254 case dot11_phy_type_dsss: 255 return WIFI_PHY_LAYER_PROTOCOL_B; 256 case dot11_phy_type_irbaseband: 257 return WIFI_PHY_LAYER_PROTOCOL_ANCIENT; 258 case dot11_phy_type_ofdm: 259 return WIFI_PHY_LAYER_PROTOCOL_A; 260 case dot11_phy_type_hrdsss: 261 return WIFI_PHY_LAYER_PROTOCOL_B; 262 case dot11_phy_type_erp: 263 return WIFI_PHY_LAYER_PROTOCOL_G; 264 case dot11_phy_type_ht: 265 return WIFI_PHY_LAYER_PROTOCOL_N; 266 default: 267 return WIFI_PHY_LAYER_PROTOCOL_UNKNOWN; 268 } 269 } 270 271 // Note: There is no need to explicitly set the options back 272 // as the OS will automatically set them back when the WlanHandle 273 // is closed. 274 class WifiOptionSetter : public ScopedWifiOptions { 275 public: 276 WifiOptionSetter(int options) { 277 const internal::WlanApi& wlanapi = internal::WlanApi::GetInstance(); 278 if (!wlanapi.initialized) 279 return; 280 281 DWORD cur_version = 0; 282 const DWORD kMaxClientVersion = 2; 283 DWORD result = wlanapi.OpenHandle( 284 kMaxClientVersion, &cur_version, &client_); 285 if (result != ERROR_SUCCESS) 286 return; 287 288 WLAN_INTERFACE_INFO_LIST* interface_list_ptr = NULL; 289 result = wlanapi.enum_interfaces_func(client_.Get(), NULL, 290 &interface_list_ptr); 291 if (result != ERROR_SUCCESS) 292 return; 293 scoped_ptr<WLAN_INTERFACE_INFO_LIST, internal::WlanApiDeleter> 294 interface_list(interface_list_ptr); 295 296 for (unsigned i = 0; i < interface_list->dwNumberOfItems; ++i) { 297 WLAN_INTERFACE_INFO* info = &interface_list->InterfaceInfo[i]; 298 if (options & WIFI_OPTIONS_DISABLE_SCAN) { 299 BOOL data = false; 300 wlanapi.set_interface_func(client_.Get(), 301 &info->InterfaceGuid, 302 wlan_intf_opcode_background_scan_enabled, 303 sizeof(data), 304 &data, 305 NULL); 306 } 307 if (options & WIFI_OPTIONS_MEDIA_STREAMING_MODE) { 308 BOOL data = true; 309 wlanapi.set_interface_func(client_.Get(), 310 &info->InterfaceGuid, 311 wlan_intf_opcode_media_streaming_mode, 312 sizeof(data), 313 &data, 314 NULL); 315 } 316 } 317 } 318 319 private: 320 internal::WlanHandle client_; 321 }; 322 323 scoped_ptr<ScopedWifiOptions> SetWifiOptions(int options) { 324 return scoped_ptr<ScopedWifiOptions>(new WifiOptionSetter(options)); 325 } 326 327 } // namespace net 328