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 "url/gurl.h" 26 27 namespace { 28 29 struct WlanApi { 30 typedef DWORD (WINAPI *WlanOpenHandleFunc)( 31 DWORD, VOID*, DWORD*, HANDLE*); 32 typedef DWORD (WINAPI *WlanEnumInterfacesFunc)( 33 HANDLE, VOID*, WLAN_INTERFACE_INFO_LIST**); 34 typedef DWORD (WINAPI *WlanQueryInterfaceFunc)( 35 HANDLE, const GUID*, WLAN_INTF_OPCODE, VOID*, DWORD*, VOID**, 36 WLAN_OPCODE_VALUE_TYPE*); 37 typedef VOID (WINAPI *WlanFreeMemoryFunc)(VOID*); 38 typedef DWORD (WINAPI *WlanCloseHandleFunc)(HANDLE, VOID*); 39 40 WlanApi() : initialized(false) { 41 // Use an absolute path to load the DLL to avoid DLL preloading attacks. 42 static const wchar_t* const kDLL = L"%WINDIR%\\system32\\wlanapi.dll"; 43 wchar_t path[MAX_PATH] = {0}; 44 ExpandEnvironmentStrings(kDLL, path, arraysize(path)); 45 module = ::LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); 46 if (!module) 47 return; 48 49 open_handle_func = reinterpret_cast<WlanOpenHandleFunc>( 50 ::GetProcAddress(module, "WlanOpenHandle")); 51 enum_interfaces_func = reinterpret_cast<WlanEnumInterfacesFunc>( 52 ::GetProcAddress(module, "WlanEnumInterfaces")); 53 query_interface_func = reinterpret_cast<WlanQueryInterfaceFunc>( 54 ::GetProcAddress(module, "WlanQueryInterface")); 55 free_memory_func = reinterpret_cast<WlanFreeMemoryFunc>( 56 ::GetProcAddress(module, "WlanFreeMemory")); 57 close_handle_func = reinterpret_cast<WlanCloseHandleFunc>( 58 ::GetProcAddress(module, "WlanCloseHandle")); 59 initialized = open_handle_func && enum_interfaces_func && 60 query_interface_func && free_memory_func && 61 close_handle_func; 62 } 63 64 template <typename T> 65 DWORD OpenHandle(DWORD client_version, DWORD* cur_version, T* handle) const { 66 HANDLE temp_handle; 67 DWORD result = open_handle_func(client_version, NULL, cur_version, 68 &temp_handle); 69 if (result != ERROR_SUCCESS) 70 return result; 71 handle->Set(temp_handle); 72 return ERROR_SUCCESS; 73 } 74 75 HMODULE module; 76 WlanOpenHandleFunc open_handle_func; 77 WlanEnumInterfacesFunc enum_interfaces_func; 78 WlanQueryInterfaceFunc query_interface_func; 79 WlanFreeMemoryFunc free_memory_func; 80 WlanCloseHandleFunc close_handle_func; 81 bool initialized; 82 }; 83 84 } // namespace 85 86 namespace net { 87 88 bool FileURLToFilePath(const GURL& url, base::FilePath* file_path) { 89 *file_path = base::FilePath(); 90 std::wstring& file_path_str = const_cast<std::wstring&>(file_path->value()); 91 file_path_str.clear(); 92 93 if (!url.is_valid()) 94 return false; 95 96 std::string path; 97 std::string host = url.host(); 98 if (host.empty()) { 99 // URL contains no host, the path is the filename. In this case, the path 100 // will probably be preceeded with a slash, as in "/C:/foo.txt", so we 101 // trim out that here. 102 path = url.path(); 103 size_t first_non_slash = path.find_first_not_of("/\\"); 104 if (first_non_slash != std::string::npos && first_non_slash > 0) 105 path.erase(0, first_non_slash); 106 } else { 107 // URL contains a host: this means it's UNC. We keep the preceeding slash 108 // on the path. 109 path = "\\\\"; 110 path.append(host); 111 path.append(url.path()); 112 } 113 114 if (path.empty()) 115 return false; 116 std::replace(path.begin(), path.end(), '/', '\\'); 117 118 // GURL stores strings as percent-encoded UTF-8, this will undo if possible. 119 path = UnescapeURLComponent(path, 120 UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS); 121 122 if (!IsStringUTF8(path)) { 123 // Not UTF-8, assume encoding is native codepage and we're done. We know we 124 // are giving the conversion function a nonempty string, and it may fail if 125 // the given string is not in the current encoding and give us an empty 126 // string back. We detect this and report failure. 127 file_path_str = base::SysNativeMBToWide(path); 128 return !file_path_str.empty(); 129 } 130 file_path_str.assign(UTF8ToWide(path)); 131 132 // We used to try too hard and see if |path| made up entirely of 133 // the 1st 256 characters in the Unicode was a zero-extended UTF-16. 134 // If so, we converted it to 'Latin-1' and checked if the result was UTF-8. 135 // If the check passed, we converted the result to UTF-8. 136 // Otherwise, we treated the result as the native OS encoding. 137 // However, that led to http://crbug.com/4619 and http://crbug.com/14153 138 return true; 139 } 140 141 bool GetNetworkList(NetworkInterfaceList* networks) { 142 // GetAdaptersAddresses() may require IO operations. 143 base::ThreadRestrictions::AssertIOAllowed(); 144 bool is_xp = base::win::GetVersion() < base::win::VERSION_VISTA; 145 ULONG len = 0; 146 ULONG flags = is_xp ? GAA_FLAG_INCLUDE_PREFIX : 0; 147 // First get number of networks. 148 ULONG result = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, NULL, &len); 149 if (result != ERROR_BUFFER_OVERFLOW) { 150 // There are 0 networks. 151 return true; 152 } 153 scoped_ptr<char[]> buf(new char[len]); 154 IP_ADAPTER_ADDRESSES *adapters = 155 reinterpret_cast<IP_ADAPTER_ADDRESSES *>(buf.get()); 156 result = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapters, &len); 157 if (result != NO_ERROR) { 158 LOG(ERROR) << "GetAdaptersAddresses failed: " << result; 159 return false; 160 } 161 162 for (IP_ADAPTER_ADDRESSES *adapter = adapters; adapter != NULL; 163 adapter = adapter->Next) { 164 // Ignore the loopback device. 165 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) { 166 continue; 167 } 168 169 if (adapter->OperStatus != IfOperStatusUp) { 170 continue; 171 } 172 173 std::string name = adapter->AdapterName; 174 175 for (IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress; 176 address; address = address->Next) { 177 int family = address->Address.lpSockaddr->sa_family; 178 if (family == AF_INET || family == AF_INET6) { 179 IPEndPoint endpoint; 180 if (endpoint.FromSockAddr(address->Address.lpSockaddr, 181 address->Address.iSockaddrLength)) { 182 // XP has no OnLinkPrefixLength field. 183 size_t net_prefix = is_xp ? 0 : address->OnLinkPrefixLength; 184 if (is_xp) { 185 // Prior to Windows Vista the FirstPrefix pointed to the list with 186 // single prefix for each IP address assigned to the adapter. 187 // Order of FirstPrefix does not match order of FirstUnicastAddress, 188 // so we need to find corresponding prefix. 189 for (IP_ADAPTER_PREFIX* prefix = adapter->FirstPrefix; prefix; 190 prefix = prefix->Next) { 191 int prefix_family = prefix->Address.lpSockaddr->sa_family; 192 IPEndPoint network_endpoint; 193 if (prefix_family == family && 194 network_endpoint.FromSockAddr(prefix->Address.lpSockaddr, 195 prefix->Address.iSockaddrLength) && 196 IPNumberMatchesPrefix(endpoint.address(), 197 network_endpoint.address(), 198 prefix->PrefixLength)) { 199 net_prefix = std::max<size_t>(net_prefix, prefix->PrefixLength); 200 } 201 } 202 } 203 uint32 index = 204 (family == AF_INET) ? adapter->IfIndex : adapter->Ipv6IfIndex; 205 networks->push_back( 206 NetworkInterface(adapter->AdapterName, index, endpoint.address(), 207 net_prefix)); 208 } 209 } 210 } 211 } 212 213 return true; 214 } 215 216 WifiPHYLayerProtocol GetWifiPHYLayerProtocol() { 217 static base::LazyInstance<WlanApi>::Leaky lazy_wlanapi = 218 LAZY_INSTANCE_INITIALIZER; 219 220 struct WlanApiHandleTraits { 221 typedef HANDLE Handle; 222 223 static bool CloseHandle(HANDLE handle) { 224 return lazy_wlanapi.Get().close_handle_func(handle, NULL) == 225 ERROR_SUCCESS; 226 } 227 static bool IsHandleValid(HANDLE handle) { 228 return base::win::HandleTraits::IsHandleValid(handle); 229 } 230 static HANDLE NullHandle() { 231 return base::win::HandleTraits::NullHandle(); 232 } 233 }; 234 235 typedef base::win::GenericScopedHandle< 236 WlanApiHandleTraits, 237 base::win::DummyVerifierTraits> WlanHandle; 238 239 struct WlanApiDeleter { 240 inline void operator()(void* ptr) const { 241 lazy_wlanapi.Get().free_memory_func(ptr); 242 } 243 }; 244 245 const WlanApi& wlanapi = lazy_wlanapi.Get(); 246 if (!wlanapi.initialized) 247 return WIFI_PHY_LAYER_PROTOCOL_NONE; 248 249 WlanHandle client; 250 DWORD cur_version = 0; 251 const DWORD kMaxClientVersion = 2; 252 DWORD result = wlanapi.OpenHandle(kMaxClientVersion, &cur_version, &client); 253 if (result != ERROR_SUCCESS) 254 return WIFI_PHY_LAYER_PROTOCOL_NONE; 255 256 WLAN_INTERFACE_INFO_LIST* interface_list_ptr = NULL; 257 result = wlanapi.enum_interfaces_func(client, NULL, &interface_list_ptr); 258 if (result != ERROR_SUCCESS) 259 return WIFI_PHY_LAYER_PROTOCOL_NONE; 260 scoped_ptr<WLAN_INTERFACE_INFO_LIST, WlanApiDeleter> interface_list( 261 interface_list_ptr); 262 263 // Assume at most one connected wifi interface. 264 WLAN_INTERFACE_INFO* info = NULL; 265 for (unsigned i = 0; i < interface_list->dwNumberOfItems; ++i) { 266 if (interface_list->InterfaceInfo[i].isState == 267 wlan_interface_state_connected) { 268 info = &interface_list->InterfaceInfo[i]; 269 break; 270 } 271 } 272 273 if (info == NULL) 274 return WIFI_PHY_LAYER_PROTOCOL_NONE; 275 276 WLAN_CONNECTION_ATTRIBUTES* conn_info_ptr; 277 DWORD conn_info_size = 0; 278 WLAN_OPCODE_VALUE_TYPE op_code; 279 result = wlanapi.query_interface_func( 280 client, &info->InterfaceGuid, wlan_intf_opcode_current_connection, NULL, 281 &conn_info_size, reinterpret_cast<VOID**>(&conn_info_ptr), &op_code); 282 if (result != ERROR_SUCCESS) 283 return WIFI_PHY_LAYER_PROTOCOL_UNKNOWN; 284 scoped_ptr<WLAN_CONNECTION_ATTRIBUTES, WlanApiDeleter> conn_info( 285 conn_info_ptr); 286 287 switch (conn_info->wlanAssociationAttributes.dot11PhyType) { 288 case dot11_phy_type_fhss: 289 return WIFI_PHY_LAYER_PROTOCOL_ANCIENT; 290 case dot11_phy_type_dsss: 291 return WIFI_PHY_LAYER_PROTOCOL_B; 292 case dot11_phy_type_irbaseband: 293 return WIFI_PHY_LAYER_PROTOCOL_ANCIENT; 294 case dot11_phy_type_ofdm: 295 return WIFI_PHY_LAYER_PROTOCOL_A; 296 case dot11_phy_type_hrdsss: 297 return WIFI_PHY_LAYER_PROTOCOL_B; 298 case dot11_phy_type_erp: 299 return WIFI_PHY_LAYER_PROTOCOL_G; 300 case dot11_phy_type_ht: 301 return WIFI_PHY_LAYER_PROTOCOL_N; 302 default: 303 return WIFI_PHY_LAYER_PROTOCOL_UNKNOWN; 304 } 305 } 306 307 } // namespace net 308