henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2004 The WebRTC Project Authors. All rights reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
| 11 | #include "webrtc/base/proxydetect.h" |
| 12 | |
| 13 | #if defined(WEBRTC_WIN) |
| 14 | #include "webrtc/base/win32.h" |
| 15 | #include <shlobj.h> |
Yuriy Shevchuk | de2382d | 2015-05-21 11:50:59 | [diff] [blame] | 16 | #endif // WEBRTC_WIN |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 17 | |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 18 | #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) |
| 19 | #include <SystemConfiguration/SystemConfiguration.h> |
| 20 | #include <CoreFoundation/CoreFoundation.h> |
| 21 | #include <CoreServices/CoreServices.h> |
| 22 | #include <Security/Security.h> |
| 23 | #include "macconversion.h" |
| 24 | #endif |
| 25 | |
Yuriy Shevchuk | de2382d | 2015-05-21 11:50:59 | [diff] [blame] | 26 | #ifdef WEBRTC_IOS |
| 27 | #include <CFNetwork/CFNetwork.h> |
| 28 | #include "macconversion.h" |
| 29 | #endif |
| 30 | |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 31 | #include <map> |
jbauch | 1286d0e | 2016-04-26 10:13:22 | [diff] [blame] | 32 | #include <memory> |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 33 | |
tfarina | 9163860 | 2015-11-11 07:44:30 | [diff] [blame] | 34 | #include "webrtc/base/arraysize.h" |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 35 | #include "webrtc/base/fileutils.h" |
| 36 | #include "webrtc/base/httpcommon.h" |
| 37 | #include "webrtc/base/httpcommon-inl.h" |
| 38 | #include "webrtc/base/pathutils.h" |
| 39 | #include "webrtc/base/stringutils.h" |
| 40 | |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 41 | #define _TRY_JSPROXY 0 |
| 42 | #define _TRY_WM_FINDPROXY 0 |
kwiberg | 15b966d | 2016-09-29 00:42:01 | [diff] [blame^] | 43 | |
| 44 | #if defined(WEBRTC_WIN) |
| 45 | #define _TRY_WINHTTP 1 |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 46 | #define _TRY_IE_LAN_SETTINGS 1 |
kwiberg | 15b966d | 2016-09-29 00:42:01 | [diff] [blame^] | 47 | #else |
| 48 | #define _TRY_WINHTTP 0 |
| 49 | #define _TRY_IE_LAN_SETTINGS 0 |
Yuriy Shevchuk | de2382d | 2015-05-21 11:50:59 | [diff] [blame] | 50 | #endif // WEBRTC_WIN |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 51 | |
| 52 | // For all platforms try Firefox. |
| 53 | #define _TRY_FIREFOX 1 |
| 54 | |
| 55 | // Use profiles.ini to find the correct profile for this user. |
| 56 | // If not set, we'll just look for the default one. |
| 57 | #define USE_FIREFOX_PROFILES_INI 1 |
| 58 | |
| 59 | static const size_t kMaxLineLength = 1024; |
| 60 | static const char kFirefoxPattern[] = "Firefox"; |
| 61 | static const char kInternetExplorerPattern[] = "MSIE"; |
| 62 | |
| 63 | struct StringMap { |
| 64 | public: |
| 65 | void Add(const char * name, const char * value) { map_[name] = value; } |
| 66 | const std::string& Get(const char * name, const char * def = "") const { |
| 67 | std::map<std::string, std::string>::const_iterator it = |
| 68 | map_.find(name); |
| 69 | if (it != map_.end()) |
| 70 | return it->second; |
| 71 | def_ = def; |
| 72 | return def_; |
| 73 | } |
| 74 | bool IsSet(const char * name) const { |
| 75 | return (map_.find(name) != map_.end()); |
| 76 | } |
| 77 | private: |
| 78 | std::map<std::string, std::string> map_; |
| 79 | mutable std::string def_; |
| 80 | }; |
| 81 | |
| 82 | enum UserAgent { |
| 83 | UA_FIREFOX, |
| 84 | UA_INTERNETEXPLORER, |
| 85 | UA_OTHER, |
| 86 | UA_UNKNOWN |
| 87 | }; |
| 88 | |
| 89 | #if _TRY_WINHTTP |
| 90 | //#include <winhttp.h> |
| 91 | // Note: From winhttp.h |
| 92 | |
| 93 | const char WINHTTP[] = "winhttp"; |
| 94 | |
| 95 | typedef LPVOID HINTERNET; |
| 96 | |
| 97 | typedef struct { |
| 98 | DWORD dwAccessType; // see WINHTTP_ACCESS_* types below |
| 99 | LPWSTR lpszProxy; // proxy server list |
| 100 | LPWSTR lpszProxyBypass; // proxy bypass list |
| 101 | } WINHTTP_PROXY_INFO, * LPWINHTTP_PROXY_INFO; |
| 102 | |
| 103 | typedef struct { |
| 104 | DWORD dwFlags; |
| 105 | DWORD dwAutoDetectFlags; |
| 106 | LPCWSTR lpszAutoConfigUrl; |
| 107 | LPVOID lpvReserved; |
| 108 | DWORD dwReserved; |
| 109 | BOOL fAutoLogonIfChallenged; |
| 110 | } WINHTTP_AUTOPROXY_OPTIONS; |
| 111 | |
| 112 | typedef struct { |
| 113 | BOOL fAutoDetect; |
| 114 | LPWSTR lpszAutoConfigUrl; |
| 115 | LPWSTR lpszProxy; |
| 116 | LPWSTR lpszProxyBypass; |
| 117 | } WINHTTP_CURRENT_USER_IE_PROXY_CONFIG; |
| 118 | |
| 119 | extern "C" { |
| 120 | typedef HINTERNET (WINAPI * pfnWinHttpOpen) |
| 121 | ( |
| 122 | IN LPCWSTR pwszUserAgent, |
| 123 | IN DWORD dwAccessType, |
| 124 | IN LPCWSTR pwszProxyName OPTIONAL, |
| 125 | IN LPCWSTR pwszProxyBypass OPTIONAL, |
| 126 | IN DWORD dwFlags |
| 127 | ); |
| 128 | typedef BOOL (STDAPICALLTYPE * pfnWinHttpCloseHandle) |
| 129 | ( |
| 130 | IN HINTERNET hInternet |
| 131 | ); |
| 132 | typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetProxyForUrl) |
| 133 | ( |
| 134 | IN HINTERNET hSession, |
| 135 | IN LPCWSTR lpcwszUrl, |
| 136 | IN WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions, |
| 137 | OUT WINHTTP_PROXY_INFO * pProxyInfo |
| 138 | ); |
| 139 | typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetIEProxyConfig) |
| 140 | ( |
| 141 | IN OUT WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * pProxyConfig |
| 142 | ); |
| 143 | |
| 144 | } // extern "C" |
| 145 | |
| 146 | #define WINHTTP_AUTOPROXY_AUTO_DETECT 0x00000001 |
| 147 | #define WINHTTP_AUTOPROXY_CONFIG_URL 0x00000002 |
| 148 | #define WINHTTP_AUTOPROXY_RUN_INPROCESS 0x00010000 |
| 149 | #define WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY 0x00020000 |
| 150 | #define WINHTTP_AUTO_DETECT_TYPE_DHCP 0x00000001 |
| 151 | #define WINHTTP_AUTO_DETECT_TYPE_DNS_A 0x00000002 |
| 152 | #define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0 |
| 153 | #define WINHTTP_ACCESS_TYPE_NO_PROXY 1 |
| 154 | #define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3 |
| 155 | #define WINHTTP_NO_PROXY_NAME NULL |
| 156 | #define WINHTTP_NO_PROXY_BYPASS NULL |
| 157 | |
| 158 | #endif // _TRY_WINHTTP |
| 159 | |
| 160 | #if _TRY_JSPROXY |
| 161 | extern "C" { |
| 162 | typedef BOOL (STDAPICALLTYPE * pfnInternetGetProxyInfo) |
| 163 | ( |
| 164 | LPCSTR lpszUrl, |
| 165 | DWORD dwUrlLength, |
| 166 | LPSTR lpszUrlHostName, |
| 167 | DWORD dwUrlHostNameLength, |
| 168 | LPSTR * lplpszProxyHostName, |
| 169 | LPDWORD lpdwProxyHostNameLength |
| 170 | ); |
| 171 | } // extern "C" |
| 172 | #endif // _TRY_JSPROXY |
| 173 | |
| 174 | #if _TRY_WM_FINDPROXY |
| 175 | #include <comutil.h> |
| 176 | #include <wmnetsourcecreator.h> |
| 177 | #include <wmsinternaladminnetsource.h> |
| 178 | #endif // _TRY_WM_FINDPROXY |
| 179 | |
| 180 | #if _TRY_IE_LAN_SETTINGS |
| 181 | #include <wininet.h> |
| 182 | #include <string> |
| 183 | #endif // _TRY_IE_LAN_SETTINGS |
| 184 | |
| 185 | namespace rtc { |
| 186 | |
| 187 | ////////////////////////////////////////////////////////////////////// |
| 188 | // Utility Functions |
| 189 | ////////////////////////////////////////////////////////////////////// |
| 190 | |
| 191 | #if defined(WEBRTC_WIN) |
| 192 | #ifdef _UNICODE |
| 193 | |
| 194 | typedef std::wstring tstring; |
| 195 | std::string Utf8String(const tstring& str) { return ToUtf8(str); } |
| 196 | |
| 197 | #else // !_UNICODE |
| 198 | |
| 199 | typedef std::string tstring; |
| 200 | std::string Utf8String(const tstring& str) { return str; } |
| 201 | |
| 202 | #endif // !_UNICODE |
Yuriy Shevchuk | de2382d | 2015-05-21 11:50:59 | [diff] [blame] | 203 | #endif // WEBRTC_WIN |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 204 | |
| 205 | bool ProxyItemMatch(const Url<char>& url, char * item, size_t len) { |
| 206 | // hostname:443 |
| 207 | if (char * port = ::strchr(item, ':')) { |
| 208 | *port++ = '\0'; |
| 209 | if (url.port() != atol(port)) { |
| 210 | return false; |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | // A.B.C.D or A.B.C.D/24 |
| 215 | int a, b, c, d, m; |
| 216 | int match = sscanf(item, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &m); |
| 217 | if (match >= 4) { |
Peter Boström | 07e22e6 | 2015-10-07 10:23:21 | [diff] [blame] | 218 | uint32_t ip = ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | |
| 219 | (d & 0xFF); |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 220 | if ((match < 5) || (m > 32)) |
| 221 | m = 32; |
| 222 | else if (m < 0) |
| 223 | m = 0; |
Peter Boström | 07e22e6 | 2015-10-07 10:23:21 | [diff] [blame] | 224 | uint32_t mask = (m == 0) ? 0 : (~0UL) << (32 - m); |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 225 | SocketAddress addr(url.host(), 0); |
| 226 | // TODO: Support IPv6 proxyitems. This code block is IPv4 only anyway. |
tfarina | 00fe71f | 2015-11-03 00:20:22 | [diff] [blame] | 227 | return !addr.IsUnresolvedIP() && |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 228 | ((addr.ipaddr().v4AddressAsHostOrderInteger() & mask) == (ip & mask)); |
| 229 | } |
| 230 | |
| 231 | // .foo.com |
| 232 | if (*item == '.') { |
| 233 | size_t hostlen = url.host().length(); |
| 234 | return (hostlen > len) |
| 235 | && (stricmp(url.host().c_str() + (hostlen - len), item) == 0); |
| 236 | } |
| 237 | |
| 238 | // localhost or www.*.com |
| 239 | if (!string_match(url.host().c_str(), item)) |
| 240 | return false; |
| 241 | |
| 242 | return true; |
| 243 | } |
| 244 | |
| 245 | bool ProxyListMatch(const Url<char>& url, const std::string& proxy_list, |
| 246 | char sep) { |
| 247 | const size_t BUFSIZE = 256; |
| 248 | char buffer[BUFSIZE]; |
| 249 | const char* list = proxy_list.c_str(); |
| 250 | while (*list) { |
| 251 | // Remove leading space |
| 252 | if (isspace(*list)) { |
| 253 | ++list; |
| 254 | continue; |
| 255 | } |
| 256 | // Break on separator |
| 257 | size_t len; |
| 258 | const char * start = list; |
| 259 | if (const char * end = ::strchr(list, sep)) { |
| 260 | len = (end - list); |
| 261 | list += len + 1; |
| 262 | } else { |
| 263 | len = strlen(list); |
| 264 | list += len; |
| 265 | } |
| 266 | // Remove trailing space |
| 267 | while ((len > 0) && isspace(start[len-1])) |
| 268 | --len; |
| 269 | // Check for oversized entry |
| 270 | if (len >= BUFSIZE) |
| 271 | continue; |
| 272 | memcpy(buffer, start, len); |
| 273 | buffer[len] = 0; |
| 274 | if (!ProxyItemMatch(url, buffer, len)) |
| 275 | continue; |
| 276 | return true; |
| 277 | } |
| 278 | return false; |
| 279 | } |
| 280 | |
| 281 | bool Better(ProxyType lhs, const ProxyType rhs) { |
| 282 | // PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN |
| 283 | const int PROXY_VALUE[5] = { 0, 2, 3, 1 }; |
| 284 | return (PROXY_VALUE[lhs] > PROXY_VALUE[rhs]); |
| 285 | } |
| 286 | |
| 287 | bool ParseProxy(const std::string& saddress, ProxyInfo* proxy) { |
| 288 | const size_t kMaxAddressLength = 1024; |
| 289 | // Allow semicolon, space, or tab as an address separator |
| 290 | const char* const kAddressSeparator = " ;\t"; |
| 291 | |
| 292 | ProxyType ptype; |
| 293 | std::string host; |
Peter Boström | 07e22e6 | 2015-10-07 10:23:21 | [diff] [blame] | 294 | uint16_t port; |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 295 | |
| 296 | const char* address = saddress.c_str(); |
| 297 | while (*address) { |
| 298 | size_t len; |
| 299 | const char * start = address; |
| 300 | if (const char * sep = strchr(address, kAddressSeparator)) { |
| 301 | len = (sep - address); |
| 302 | address += len + 1; |
| 303 | while (*address != '\0' && ::strchr(kAddressSeparator, *address)) { |
| 304 | address += 1; |
| 305 | } |
| 306 | } else { |
| 307 | len = strlen(address); |
| 308 | address += len; |
| 309 | } |
| 310 | |
| 311 | if (len > kMaxAddressLength - 1) { |
| 312 | LOG(LS_WARNING) << "Proxy address too long [" << start << "]"; |
| 313 | continue; |
| 314 | } |
| 315 | |
| 316 | char buffer[kMaxAddressLength]; |
| 317 | memcpy(buffer, start, len); |
| 318 | buffer[len] = 0; |
| 319 | |
| 320 | char * colon = ::strchr(buffer, ':'); |
| 321 | if (!colon) { |
| 322 | LOG(LS_WARNING) << "Proxy address without port [" << buffer << "]"; |
| 323 | continue; |
| 324 | } |
| 325 | |
| 326 | *colon = 0; |
| 327 | char * endptr; |
Peter Boström | 07e22e6 | 2015-10-07 10:23:21 | [diff] [blame] | 328 | port = static_cast<uint16_t>(strtol(colon + 1, &endptr, 0)); |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 329 | if (*endptr != 0) { |
| 330 | LOG(LS_WARNING) << "Proxy address with invalid port [" << buffer << "]"; |
| 331 | continue; |
| 332 | } |
| 333 | |
| 334 | if (char * equals = ::strchr(buffer, '=')) { |
| 335 | *equals = 0; |
| 336 | host = equals + 1; |
| 337 | if (_stricmp(buffer, "socks") == 0) { |
| 338 | ptype = PROXY_SOCKS5; |
| 339 | } else if (_stricmp(buffer, "https") == 0) { |
| 340 | ptype = PROXY_HTTPS; |
| 341 | } else { |
| 342 | LOG(LS_WARNING) << "Proxy address with unknown protocol [" |
| 343 | << buffer << "]"; |
| 344 | ptype = PROXY_UNKNOWN; |
| 345 | } |
| 346 | } else { |
| 347 | host = buffer; |
| 348 | ptype = PROXY_UNKNOWN; |
| 349 | } |
| 350 | |
| 351 | if (Better(ptype, proxy->type)) { |
| 352 | proxy->type = ptype; |
| 353 | proxy->address.SetIP(host); |
| 354 | proxy->address.SetPort(port); |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | return proxy->type != PROXY_NONE; |
| 359 | } |
| 360 | |
| 361 | UserAgent GetAgent(const char* agent) { |
| 362 | if (agent) { |
| 363 | std::string agent_str(agent); |
| 364 | if (agent_str.find(kFirefoxPattern) != std::string::npos) { |
| 365 | return UA_FIREFOX; |
| 366 | } else if (agent_str.find(kInternetExplorerPattern) != std::string::npos) { |
| 367 | return UA_INTERNETEXPLORER; |
| 368 | } else if (agent_str.empty()) { |
| 369 | return UA_UNKNOWN; |
| 370 | } |
| 371 | } |
| 372 | return UA_OTHER; |
| 373 | } |
| 374 | |
| 375 | bool EndsWith(const std::string& a, const std::string& b) { |
| 376 | if (b.size() > a.size()) { |
| 377 | return false; |
| 378 | } |
| 379 | int result = a.compare(a.size() - b.size(), b.size(), b); |
| 380 | return result == 0; |
| 381 | } |
| 382 | |
| 383 | bool GetFirefoxProfilePath(Pathname* path) { |
| 384 | #if defined(WEBRTC_WIN) |
| 385 | wchar_t w_path[MAX_PATH]; |
| 386 | if (SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, w_path) != |
| 387 | S_OK) { |
| 388 | LOG(LS_ERROR) << "SHGetFolderPath failed"; |
| 389 | return false; |
| 390 | } |
| 391 | path->SetFolder(ToUtf8(w_path, wcslen(w_path))); |
| 392 | path->AppendFolder("Mozilla"); |
| 393 | path->AppendFolder("Firefox"); |
| 394 | #elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) |
| 395 | FSRef fr; |
| 396 | if (0 != FSFindFolder(kUserDomain, kApplicationSupportFolderType, |
| 397 | kCreateFolder, &fr)) { |
| 398 | LOG(LS_ERROR) << "FSFindFolder failed"; |
| 399 | return false; |
| 400 | } |
| 401 | char buffer[NAME_MAX + 1]; |
Peter Boström | 07e22e6 | 2015-10-07 10:23:21 | [diff] [blame] | 402 | if (0 != FSRefMakePath(&fr, reinterpret_cast<uint8_t*>(buffer), |
tfarina | 9163860 | 2015-11-11 07:44:30 | [diff] [blame] | 403 | arraysize(buffer))) { |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 404 | LOG(LS_ERROR) << "FSRefMakePath failed"; |
| 405 | return false; |
| 406 | } |
| 407 | path->SetFolder(std::string(buffer)); |
| 408 | path->AppendFolder("Firefox"); |
| 409 | #else |
| 410 | char* user_home = getenv("HOME"); |
| 411 | if (user_home == NULL) { |
| 412 | return false; |
| 413 | } |
| 414 | path->SetFolder(std::string(user_home)); |
| 415 | path->AppendFolder(".mozilla"); |
| 416 | path->AppendFolder("firefox"); |
Yuriy Shevchuk | de2382d | 2015-05-21 11:50:59 | [diff] [blame] | 417 | #endif // WEBRTC_WIN |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 418 | return true; |
| 419 | } |
| 420 | |
| 421 | bool GetDefaultFirefoxProfile(Pathname* profile_path) { |
| 422 | ASSERT(NULL != profile_path); |
| 423 | Pathname path; |
| 424 | if (!GetFirefoxProfilePath(&path)) { |
| 425 | return false; |
| 426 | } |
| 427 | |
| 428 | #if USE_FIREFOX_PROFILES_INI |
| 429 | // [Profile0] |
| 430 | // Name=default |
| 431 | // IsRelative=1 |
| 432 | // Path=Profiles/2de53ejb.default |
| 433 | // Default=1 |
| 434 | |
| 435 | // Note: we are looking for the first entry with "Default=1", or the last |
| 436 | // entry in the file |
| 437 | path.SetFilename("profiles.ini"); |
jbauch | 1286d0e | 2016-04-26 10:13:22 | [diff] [blame] | 438 | std::unique_ptr<FileStream> fs(Filesystem::OpenFile(path, "r")); |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 439 | if (!fs) { |
| 440 | return false; |
| 441 | } |
| 442 | Pathname candidate; |
| 443 | bool relative = true; |
| 444 | std::string line; |
| 445 | while (fs->ReadLine(&line) == SR_SUCCESS) { |
| 446 | if (line.length() == 0) { |
| 447 | continue; |
| 448 | } |
| 449 | if (line.at(0) == '[') { |
| 450 | relative = true; |
| 451 | candidate.clear(); |
| 452 | } else if (line.find("IsRelative=") == 0 && |
| 453 | line.length() >= 12) { |
| 454 | // TODO: The initial Linux public launch revealed a fairly |
| 455 | // high number of machines where IsRelative= did not have anything after |
| 456 | // it. Perhaps that is legal profiles.ini syntax? |
| 457 | relative = (line.at(11) != '0'); |
| 458 | } else if (line.find("Path=") == 0 && |
| 459 | line.length() >= 6) { |
| 460 | if (relative) { |
| 461 | candidate = path; |
| 462 | } else { |
| 463 | candidate.clear(); |
| 464 | } |
| 465 | candidate.AppendFolder(line.substr(5)); |
| 466 | } else if (line.find("Default=") == 0 && |
| 467 | line.length() >= 9) { |
| 468 | if ((line.at(8) != '0') && !candidate.empty()) { |
| 469 | break; |
| 470 | } |
| 471 | } |
| 472 | } |
| 473 | fs->Close(); |
| 474 | if (candidate.empty()) { |
| 475 | return false; |
| 476 | } |
| 477 | profile_path->SetPathname(candidate.pathname()); |
| 478 | |
| 479 | #else // !USE_FIREFOX_PROFILES_INI |
| 480 | path.AppendFolder("Profiles"); |
| 481 | DirectoryIterator* it = Filesystem::IterateDirectory(); |
| 482 | it->Iterate(path); |
| 483 | std::string extension(".default"); |
| 484 | while (!EndsWith(it->Name(), extension)) { |
| 485 | if (!it->Next()) { |
| 486 | return false; |
| 487 | } |
| 488 | } |
| 489 | |
| 490 | profile_path->SetPathname(path); |
| 491 | profile->AppendFolder("Profiles"); |
| 492 | profile->AppendFolder(it->Name()); |
| 493 | delete it; |
| 494 | |
| 495 | #endif // !USE_FIREFOX_PROFILES_INI |
| 496 | |
| 497 | return true; |
| 498 | } |
| 499 | |
| 500 | bool ReadFirefoxPrefs(const Pathname& filename, |
| 501 | const char * prefix, |
| 502 | StringMap* settings) { |
jbauch | 1286d0e | 2016-04-26 10:13:22 | [diff] [blame] | 503 | std::unique_ptr<FileStream> fs(Filesystem::OpenFile(filename, "r")); |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 504 | if (!fs) { |
| 505 | LOG(LS_ERROR) << "Failed to open file: " << filename.pathname(); |
| 506 | return false; |
| 507 | } |
| 508 | |
| 509 | std::string line; |
| 510 | while (fs->ReadLine(&line) == SR_SUCCESS) { |
| 511 | size_t prefix_len = strlen(prefix); |
| 512 | |
| 513 | // Skip blank lines and too long lines. |
| 514 | if ((line.length() == 0) || (line.length() > kMaxLineLength) |
| 515 | || (line.at(0) == '#') || line.compare(0, 2, "/*") == 0 |
| 516 | || line.compare(0, 2, " *") == 0) { |
| 517 | continue; |
| 518 | } |
| 519 | |
| 520 | char buffer[kMaxLineLength]; |
| 521 | strcpyn(buffer, sizeof(buffer), line.c_str()); |
| 522 | int nstart = 0, nend = 0, vstart = 0, vend = 0; |
| 523 | sscanf(buffer, "user_pref(\"%n%*[^\"]%n\", %n%*[^)]%n);", |
| 524 | &nstart, &nend, &vstart, &vend); |
| 525 | if (vend > 0) { |
| 526 | char* name = buffer + nstart; |
| 527 | name[nend - nstart] = 0; |
| 528 | if ((vend - vstart >= 2) && (buffer[vstart] == '"')) { |
| 529 | vstart += 1; |
| 530 | vend -= 1; |
| 531 | } |
| 532 | char* value = buffer + vstart; |
| 533 | value[vend - vstart] = 0; |
| 534 | if ((strncmp(name, prefix, prefix_len) == 0) && *value) { |
| 535 | settings->Add(name + prefix_len, value); |
| 536 | } |
| 537 | } else { |
| 538 | LOG_F(LS_WARNING) << "Unparsed pref [" << buffer << "]"; |
| 539 | } |
| 540 | } |
| 541 | fs->Close(); |
| 542 | return true; |
| 543 | } |
| 544 | |
| 545 | bool GetFirefoxProxySettings(const char* url, ProxyInfo* proxy) { |
| 546 | Url<char> purl(url); |
| 547 | Pathname path; |
| 548 | bool success = false; |
| 549 | if (GetDefaultFirefoxProfile(&path)) { |
| 550 | StringMap settings; |
| 551 | path.SetFilename("prefs.js"); |
| 552 | if (ReadFirefoxPrefs(path, "network.proxy.", &settings)) { |
| 553 | success = true; |
| 554 | proxy->bypass_list = |
| 555 | settings.Get("no_proxies_on", "localhost, 127.0.0.1"); |
| 556 | if (settings.Get("type") == "1") { |
| 557 | // User has manually specified a proxy, try to figure out what |
| 558 | // type it is. |
| 559 | if (ProxyListMatch(purl, proxy->bypass_list.c_str(), ',')) { |
| 560 | // Our url is in the list of url's to bypass proxy. |
| 561 | } else if (settings.Get("share_proxy_settings") == "true") { |
| 562 | proxy->type = PROXY_UNKNOWN; |
| 563 | proxy->address.SetIP(settings.Get("http")); |
| 564 | proxy->address.SetPort(atoi(settings.Get("http_port").c_str())); |
| 565 | } else if (settings.IsSet("socks")) { |
| 566 | proxy->type = PROXY_SOCKS5; |
| 567 | proxy->address.SetIP(settings.Get("socks")); |
| 568 | proxy->address.SetPort(atoi(settings.Get("socks_port").c_str())); |
| 569 | } else if (settings.IsSet("ssl")) { |
| 570 | proxy->type = PROXY_HTTPS; |
| 571 | proxy->address.SetIP(settings.Get("ssl")); |
| 572 | proxy->address.SetPort(atoi(settings.Get("ssl_port").c_str())); |
| 573 | } else if (settings.IsSet("http")) { |
| 574 | proxy->type = PROXY_HTTPS; |
| 575 | proxy->address.SetIP(settings.Get("http")); |
| 576 | proxy->address.SetPort(atoi(settings.Get("http_port").c_str())); |
| 577 | } |
| 578 | } else if (settings.Get("type") == "2") { |
| 579 | // Browser is configured to get proxy settings from a given url. |
| 580 | proxy->autoconfig_url = settings.Get("autoconfig_url").c_str(); |
| 581 | } else if (settings.Get("type") == "4") { |
| 582 | // Browser is configured to auto detect proxy config. |
| 583 | proxy->autodetect = true; |
| 584 | } else { |
| 585 | // No proxy set. |
| 586 | } |
| 587 | } |
| 588 | } |
| 589 | return success; |
| 590 | } |
| 591 | |
| 592 | #if defined(WEBRTC_WIN) // Windows specific implementation for reading Internet |
| 593 | // Explorer proxy settings. |
| 594 | |
| 595 | void LogGetProxyFault() { |
| 596 | LOG_GLEM(LERROR, WINHTTP) << "WinHttpGetProxyForUrl faulted!!"; |
| 597 | } |
| 598 | |
| 599 | BOOL MyWinHttpGetProxyForUrl(pfnWinHttpGetProxyForUrl pWHGPFU, |
| 600 | HINTERNET hWinHttp, LPCWSTR url, |
| 601 | WINHTTP_AUTOPROXY_OPTIONS *options, |
| 602 | WINHTTP_PROXY_INFO *info) { |
| 603 | // WinHttpGetProxyForUrl() can call plugins which can crash. |
| 604 | // In the case of McAfee scriptproxy.dll, it does crash in |
| 605 | // older versions. Try to catch crashes here and treat as an |
| 606 | // error. |
| 607 | BOOL success = FALSE; |
| 608 | |
| 609 | #if (_HAS_EXCEPTIONS == 0) |
| 610 | __try { |
| 611 | success = pWHGPFU(hWinHttp, url, options, info); |
| 612 | } __except(EXCEPTION_EXECUTE_HANDLER) { |
| 613 | // This is a separate function to avoid |
| 614 | // Visual C++ error 2712 when compiling with C++ EH |
| 615 | LogGetProxyFault(); |
| 616 | } |
| 617 | #else |
| 618 | success = pWHGPFU(hWinHttp, url, options, info); |
| 619 | #endif // (_HAS_EXCEPTIONS == 0) |
| 620 | |
| 621 | return success; |
| 622 | } |
| 623 | |
| 624 | bool IsDefaultBrowserFirefox() { |
| 625 | HKEY key; |
| 626 | LONG result = RegOpenKeyEx(HKEY_CLASSES_ROOT, L"http\\shell\\open\\command", |
| 627 | 0, KEY_READ, &key); |
| 628 | if (ERROR_SUCCESS != result) |
| 629 | return false; |
| 630 | |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 631 | DWORD size, type; |
henrike@webrtc.org | b2e746e | 2014-05-23 18:40:46 | [diff] [blame] | 632 | bool success = false; |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 633 | result = RegQueryValueEx(key, L"", 0, &type, NULL, &size); |
henrike@webrtc.org | b2e746e | 2014-05-23 18:40:46 | [diff] [blame] | 634 | if (result == ERROR_SUCCESS && type == REG_SZ) { |
| 635 | wchar_t* value = new wchar_t[size+1]; |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 636 | BYTE* buffer = reinterpret_cast<BYTE*>(value); |
| 637 | result = RegQueryValueEx(key, L"", 0, &type, buffer, &size); |
henrike@webrtc.org | b2e746e | 2014-05-23 18:40:46 | [diff] [blame] | 638 | if (result == ERROR_SUCCESS) { |
| 639 | // Size returned by RegQueryValueEx is in bytes, convert to number of |
| 640 | // wchar_t's. |
| 641 | size /= sizeof(value[0]); |
| 642 | value[size] = L'\0'; |
| 643 | for (size_t i = 0; i < size; ++i) { |
| 644 | value[i] = tolowercase(value[i]); |
| 645 | } |
| 646 | success = (NULL != strstr(value, L"firefox.exe")); |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 647 | } |
henrike@webrtc.org | b2e746e | 2014-05-23 18:40:46 | [diff] [blame] | 648 | delete[] value; |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 649 | } |
henrike@webrtc.org | b2e746e | 2014-05-23 18:40:46 | [diff] [blame] | 650 | |
| 651 | RegCloseKey(key); |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 652 | return success; |
| 653 | } |
| 654 | |
| 655 | bool GetWinHttpProxySettings(const char* url, ProxyInfo* proxy) { |
| 656 | HMODULE winhttp_handle = LoadLibrary(L"winhttp.dll"); |
| 657 | if (winhttp_handle == NULL) { |
| 658 | LOG(LS_ERROR) << "Failed to load winhttp.dll."; |
| 659 | return false; |
| 660 | } |
| 661 | WINHTTP_CURRENT_USER_IE_PROXY_CONFIG iecfg; |
| 662 | memset(&iecfg, 0, sizeof(iecfg)); |
| 663 | Url<char> purl(url); |
| 664 | pfnWinHttpGetIEProxyConfig pWHGIEPC = |
| 665 | reinterpret_cast<pfnWinHttpGetIEProxyConfig>( |
| 666 | GetProcAddress(winhttp_handle, |
| 667 | "WinHttpGetIEProxyConfigForCurrentUser")); |
| 668 | bool success = false; |
| 669 | if (pWHGIEPC && pWHGIEPC(&iecfg)) { |
| 670 | // We were read proxy config successfully. |
| 671 | success = true; |
| 672 | if (iecfg.fAutoDetect) { |
| 673 | proxy->autodetect = true; |
| 674 | } |
| 675 | if (iecfg.lpszAutoConfigUrl) { |
| 676 | proxy->autoconfig_url = ToUtf8(iecfg.lpszAutoConfigUrl); |
| 677 | GlobalFree(iecfg.lpszAutoConfigUrl); |
| 678 | } |
| 679 | if (iecfg.lpszProxyBypass) { |
| 680 | proxy->bypass_list = ToUtf8(iecfg.lpszProxyBypass); |
| 681 | GlobalFree(iecfg.lpszProxyBypass); |
| 682 | } |
| 683 | if (iecfg.lpszProxy) { |
| 684 | if (!ProxyListMatch(purl, proxy->bypass_list, ';')) { |
| 685 | ParseProxy(ToUtf8(iecfg.lpszProxy), proxy); |
| 686 | } |
| 687 | GlobalFree(iecfg.lpszProxy); |
| 688 | } |
| 689 | } |
| 690 | FreeLibrary(winhttp_handle); |
| 691 | return success; |
| 692 | } |
| 693 | |
| 694 | // Uses the WinHTTP API to auto detect proxy for the given url. Firefox and IE |
| 695 | // have slightly different option dialogs for proxy settings. In Firefox, |
| 696 | // either a location of a proxy configuration file can be specified or auto |
| 697 | // detection can be selected. In IE theese two options can be independently |
| 698 | // selected. For the case where both options are selected (only IE) we try to |
| 699 | // fetch the config file first, and if that fails we'll perform an auto |
| 700 | // detection. |
| 701 | // |
| 702 | // Returns true if we successfully performed an auto detection not depending on |
| 703 | // whether we found a proxy or not. Returns false on error. |
| 704 | bool WinHttpAutoDetectProxyForUrl(const char* agent, const char* url, |
| 705 | ProxyInfo* proxy) { |
| 706 | Url<char> purl(url); |
| 707 | bool success = true; |
| 708 | HMODULE winhttp_handle = LoadLibrary(L"winhttp.dll"); |
| 709 | if (winhttp_handle == NULL) { |
| 710 | LOG(LS_ERROR) << "Failed to load winhttp.dll."; |
| 711 | return false; |
| 712 | } |
| 713 | pfnWinHttpOpen pWHO = |
| 714 | reinterpret_cast<pfnWinHttpOpen>(GetProcAddress(winhttp_handle, |
| 715 | "WinHttpOpen")); |
| 716 | pfnWinHttpCloseHandle pWHCH = |
| 717 | reinterpret_cast<pfnWinHttpCloseHandle>( |
| 718 | GetProcAddress(winhttp_handle, "WinHttpCloseHandle")); |
| 719 | pfnWinHttpGetProxyForUrl pWHGPFU = |
| 720 | reinterpret_cast<pfnWinHttpGetProxyForUrl>( |
| 721 | GetProcAddress(winhttp_handle, "WinHttpGetProxyForUrl")); |
| 722 | if (pWHO && pWHCH && pWHGPFU) { |
| 723 | if (HINTERNET hWinHttp = pWHO(ToUtf16(agent).c_str(), |
| 724 | WINHTTP_ACCESS_TYPE_NO_PROXY, |
| 725 | WINHTTP_NO_PROXY_NAME, |
| 726 | WINHTTP_NO_PROXY_BYPASS, |
| 727 | 0)) { |
| 728 | BOOL result = FALSE; |
| 729 | WINHTTP_PROXY_INFO info; |
| 730 | memset(&info, 0, sizeof(info)); |
| 731 | if (proxy->autodetect) { |
| 732 | // Use DHCP and DNS to try to find any proxy to use. |
| 733 | WINHTTP_AUTOPROXY_OPTIONS options; |
| 734 | memset(&options, 0, sizeof(options)); |
| 735 | options.fAutoLogonIfChallenged = TRUE; |
| 736 | |
| 737 | options.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT; |
| 738 | options.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DHCP |
| 739 | | WINHTTP_AUTO_DETECT_TYPE_DNS_A; |
| 740 | result = MyWinHttpGetProxyForUrl( |
| 741 | pWHGPFU, hWinHttp, ToUtf16(url).c_str(), &options, &info); |
| 742 | } |
| 743 | if (!result && !proxy->autoconfig_url.empty()) { |
| 744 | // We have the location of a proxy config file. Download it and |
| 745 | // execute it to find proxy settings for our url. |
| 746 | WINHTTP_AUTOPROXY_OPTIONS options; |
| 747 | memset(&options, 0, sizeof(options)); |
| 748 | memset(&info, 0, sizeof(info)); |
| 749 | options.fAutoLogonIfChallenged = TRUE; |
| 750 | |
| 751 | std::wstring autoconfig_url16((ToUtf16)(proxy->autoconfig_url)); |
| 752 | options.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL; |
| 753 | options.lpszAutoConfigUrl = autoconfig_url16.c_str(); |
| 754 | |
| 755 | result = MyWinHttpGetProxyForUrl( |
| 756 | pWHGPFU, hWinHttp, ToUtf16(url).c_str(), &options, &info); |
| 757 | } |
| 758 | if (result) { |
| 759 | // Either the given auto config url was valid or auto |
| 760 | // detection found a proxy on this network. |
| 761 | if (info.lpszProxy) { |
| 762 | // TODO: Does this bypass list differ from the list |
| 763 | // retreived from GetWinHttpProxySettings earlier? |
| 764 | if (info.lpszProxyBypass) { |
| 765 | proxy->bypass_list = ToUtf8(info.lpszProxyBypass); |
| 766 | GlobalFree(info.lpszProxyBypass); |
| 767 | } else { |
| 768 | proxy->bypass_list.clear(); |
| 769 | } |
| 770 | if (!ProxyListMatch(purl, proxy->bypass_list, ';')) { |
| 771 | // Found proxy for this URL. If parsing the address turns |
| 772 | // out ok then we are successful. |
| 773 | success = ParseProxy(ToUtf8(info.lpszProxy), proxy); |
| 774 | } |
| 775 | GlobalFree(info.lpszProxy); |
| 776 | } |
| 777 | } else { |
| 778 | // We could not find any proxy for this url. |
| 779 | LOG(LS_INFO) << "No proxy detected for " << url; |
| 780 | } |
| 781 | pWHCH(hWinHttp); |
| 782 | } |
| 783 | } else { |
| 784 | LOG(LS_ERROR) << "Failed loading WinHTTP functions."; |
| 785 | success = false; |
| 786 | } |
| 787 | FreeLibrary(winhttp_handle); |
| 788 | return success; |
| 789 | } |
| 790 | |
| 791 | #if 0 // Below functions currently not used. |
| 792 | |
| 793 | bool GetJsProxySettings(const char* url, ProxyInfo* proxy) { |
| 794 | Url<char> purl(url); |
| 795 | bool success = false; |
| 796 | |
| 797 | if (HMODULE hModJS = LoadLibrary(_T("jsproxy.dll"))) { |
| 798 | pfnInternetGetProxyInfo pIGPI = |
| 799 | reinterpret_cast<pfnInternetGetProxyInfo>( |
| 800 | GetProcAddress(hModJS, "InternetGetProxyInfo")); |
| 801 | if (pIGPI) { |
| 802 | char proxy[256], host[256]; |
| 803 | memset(proxy, 0, sizeof(proxy)); |
| 804 | char * ptr = proxy; |
| 805 | DWORD proxylen = sizeof(proxy); |
| 806 | std::string surl = Utf8String(url); |
| 807 | DWORD hostlen = _snprintf(host, sizeof(host), "http%s://%S", |
| 808 | purl.secure() ? "s" : "", purl.server()); |
| 809 | if (pIGPI(surl.data(), surl.size(), host, hostlen, &ptr, &proxylen)) { |
| 810 | LOG(INFO) << "Proxy: " << proxy; |
| 811 | } else { |
| 812 | LOG_GLE(INFO) << "InternetGetProxyInfo"; |
| 813 | } |
| 814 | } |
| 815 | FreeLibrary(hModJS); |
| 816 | } |
| 817 | return success; |
| 818 | } |
| 819 | |
| 820 | bool GetWmProxySettings(const char* url, ProxyInfo* proxy) { |
| 821 | Url<char> purl(url); |
| 822 | bool success = false; |
| 823 | |
| 824 | INSNetSourceCreator * nsc = 0; |
| 825 | HRESULT hr = CoCreateInstance(CLSID_ClientNetManager, 0, CLSCTX_ALL, |
| 826 | IID_INSNetSourceCreator, (LPVOID *) &nsc); |
| 827 | if (SUCCEEDED(hr)) { |
| 828 | if (SUCCEEDED(hr = nsc->Initialize())) { |
| 829 | VARIANT dispatch; |
| 830 | VariantInit(&dispatch); |
| 831 | if (SUCCEEDED(hr = nsc->GetNetSourceAdminInterface(L"http", &dispatch))) { |
| 832 | IWMSInternalAdminNetSource * ians = 0; |
| 833 | if (SUCCEEDED(hr = dispatch.pdispVal->QueryInterface( |
| 834 | IID_IWMSInternalAdminNetSource, (LPVOID *) &ians))) { |
| 835 | _bstr_t host(purl.server()); |
| 836 | BSTR proxy = 0; |
| 837 | BOOL bProxyEnabled = FALSE; |
| 838 | DWORD port, context = 0; |
| 839 | if (SUCCEEDED(hr = ians->FindProxyForURL( |
| 840 | L"http", host, &bProxyEnabled, &proxy, &port, &context))) { |
| 841 | success = true; |
| 842 | if (bProxyEnabled) { |
| 843 | _bstr_t sproxy = proxy; |
| 844 | proxy->ptype = PT_HTTPS; |
| 845 | proxy->host = sproxy; |
| 846 | proxy->port = port; |
| 847 | } |
| 848 | } |
| 849 | SysFreeString(proxy); |
| 850 | if (FAILED(hr = ians->ShutdownProxyContext(context))) { |
| 851 | LOG(LS_INFO) << "IWMSInternalAdminNetSource::ShutdownProxyContext" |
| 852 | << "failed: " << hr; |
| 853 | } |
| 854 | ians->Release(); |
| 855 | } |
| 856 | } |
| 857 | VariantClear(&dispatch); |
| 858 | if (FAILED(hr = nsc->Shutdown())) { |
| 859 | LOG(LS_INFO) << "INSNetSourceCreator::Shutdown failed: " << hr; |
| 860 | } |
| 861 | } |
| 862 | nsc->Release(); |
| 863 | } |
| 864 | return success; |
| 865 | } |
| 866 | |
| 867 | bool GetIePerConnectionProxySettings(const char* url, ProxyInfo* proxy) { |
| 868 | Url<char> purl(url); |
| 869 | bool success = false; |
| 870 | |
| 871 | INTERNET_PER_CONN_OPTION_LIST list; |
| 872 | INTERNET_PER_CONN_OPTION options[3]; |
| 873 | memset(&list, 0, sizeof(list)); |
| 874 | memset(&options, 0, sizeof(options)); |
| 875 | |
| 876 | list.dwSize = sizeof(list); |
| 877 | list.dwOptionCount = 3; |
| 878 | list.pOptions = options; |
| 879 | options[0].dwOption = INTERNET_PER_CONN_FLAGS; |
| 880 | options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER; |
| 881 | options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS; |
| 882 | DWORD dwSize = sizeof(list); |
| 883 | |
| 884 | if (!InternetQueryOption(0, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, |
| 885 | &dwSize)) { |
| 886 | LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError(); |
| 887 | } else if ((options[0].Value.dwValue & PROXY_TYPE_PROXY) != 0) { |
| 888 | success = true; |
| 889 | if (!ProxyListMatch(purl, nonnull(options[2].Value.pszValue), _T(';'))) { |
| 890 | ParseProxy(nonnull(options[1].Value.pszValue), proxy); |
| 891 | } |
| 892 | } else if ((options[0].Value.dwValue & PROXY_TYPE_DIRECT) != 0) { |
| 893 | success = true; |
| 894 | } else { |
| 895 | LOG(LS_INFO) << "unknown internet access type: " |
| 896 | << options[0].Value.dwValue; |
| 897 | } |
| 898 | if (options[1].Value.pszValue) { |
| 899 | GlobalFree(options[1].Value.pszValue); |
| 900 | } |
| 901 | if (options[2].Value.pszValue) { |
| 902 | GlobalFree(options[2].Value.pszValue); |
| 903 | } |
| 904 | return success; |
| 905 | } |
| 906 | |
| 907 | #endif // 0 |
| 908 | |
| 909 | // Uses the InternetQueryOption function to retrieve proxy settings |
| 910 | // from the registry. This will only give us the 'static' settings, |
| 911 | // ie, not any information about auto config etc. |
| 912 | bool GetIeLanProxySettings(const char* url, ProxyInfo* proxy) { |
| 913 | Url<char> purl(url); |
| 914 | bool success = false; |
| 915 | |
| 916 | wchar_t buffer[1024]; |
| 917 | memset(buffer, 0, sizeof(buffer)); |
| 918 | INTERNET_PROXY_INFO * info = reinterpret_cast<INTERNET_PROXY_INFO *>(buffer); |
| 919 | DWORD dwSize = sizeof(buffer); |
| 920 | |
| 921 | if (!InternetQueryOption(0, INTERNET_OPTION_PROXY, info, &dwSize)) { |
| 922 | LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError(); |
| 923 | } else if (info->dwAccessType == INTERNET_OPEN_TYPE_DIRECT) { |
| 924 | success = true; |
| 925 | } else if (info->dwAccessType == INTERNET_OPEN_TYPE_PROXY) { |
| 926 | success = true; |
| 927 | if (!ProxyListMatch(purl, nonnull(reinterpret_cast<const char*>( |
| 928 | info->lpszProxyBypass)), ' ')) { |
| 929 | ParseProxy(nonnull(reinterpret_cast<const char*>(info->lpszProxy)), |
| 930 | proxy); |
| 931 | } |
| 932 | } else { |
| 933 | LOG(LS_INFO) << "unknown internet access type: " << info->dwAccessType; |
| 934 | } |
| 935 | return success; |
| 936 | } |
| 937 | |
| 938 | bool GetIeProxySettings(const char* agent, const char* url, ProxyInfo* proxy) { |
| 939 | bool success = GetWinHttpProxySettings(url, proxy); |
| 940 | if (!success) { |
| 941 | // TODO: Should always call this if no proxy were detected by |
| 942 | // GetWinHttpProxySettings? |
| 943 | // WinHttp failed. Try using the InternetOptionQuery method instead. |
| 944 | return GetIeLanProxySettings(url, proxy); |
| 945 | } |
| 946 | return true; |
| 947 | } |
| 948 | |
Yuriy Shevchuk | de2382d | 2015-05-21 11:50:59 | [diff] [blame] | 949 | #endif // WEBRTC_WIN |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 950 | |
| 951 | #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) // WEBRTC_MAC && !defined(WEBRTC_IOS) specific implementation for reading system wide |
| 952 | // proxy settings. |
| 953 | |
| 954 | bool p_getProxyInfoForTypeFromDictWithKeys(ProxyInfo* proxy, |
| 955 | ProxyType type, |
| 956 | const CFDictionaryRef proxyDict, |
| 957 | const CFStringRef enabledKey, |
| 958 | const CFStringRef hostKey, |
| 959 | const CFStringRef portKey) { |
| 960 | // whether or not we set up the proxy info. |
| 961 | bool result = false; |
| 962 | |
| 963 | // we use this as a scratch variable for determining if operations |
| 964 | // succeeded. |
| 965 | bool converted = false; |
| 966 | |
| 967 | // the data we need to construct the SocketAddress for the proxy. |
| 968 | std::string hostname; |
| 969 | int port; |
| 970 | |
| 971 | if ((proxyDict != NULL) && |
| 972 | (CFGetTypeID(proxyDict) == CFDictionaryGetTypeID())) { |
| 973 | // CoreFoundation stuff that we'll have to get from |
| 974 | // the dictionaries and interpret or convert into more usable formats. |
| 975 | CFNumberRef enabledCFNum; |
| 976 | CFNumberRef portCFNum; |
| 977 | CFStringRef hostCFStr; |
| 978 | |
| 979 | enabledCFNum = (CFNumberRef)CFDictionaryGetValue(proxyDict, enabledKey); |
| 980 | |
| 981 | if (p_isCFNumberTrue(enabledCFNum)) { |
| 982 | // let's see if we can get the address and port. |
| 983 | hostCFStr = (CFStringRef)CFDictionaryGetValue(proxyDict, hostKey); |
| 984 | converted = p_convertHostCFStringRefToCPPString(hostCFStr, hostname); |
| 985 | if (converted) { |
| 986 | portCFNum = (CFNumberRef)CFDictionaryGetValue(proxyDict, portKey); |
| 987 | converted = p_convertCFNumberToInt(portCFNum, &port); |
| 988 | if (converted) { |
| 989 | // we have something enabled, with a hostname and a port. |
| 990 | // That's sufficient to set up the proxy info. |
| 991 | proxy->type = type; |
| 992 | proxy->address.SetIP(hostname); |
| 993 | proxy->address.SetPort(port); |
| 994 | result = true; |
| 995 | } |
| 996 | } |
| 997 | } |
| 998 | } |
| 999 | |
| 1000 | return result; |
| 1001 | } |
| 1002 | |
| 1003 | // Looks for proxy information in the given dictionary, |
| 1004 | // return true if it found sufficient information to define one, |
| 1005 | // false otherwise. This is guaranteed to not change the values in proxy |
| 1006 | // unless a full-fledged proxy description was discovered in the dictionary. |
| 1007 | // However, at the present time this does not support username or password. |
| 1008 | // Checks first for a SOCKS proxy, then for HTTPS, then HTTP. |
| 1009 | bool GetMacProxySettingsFromDictionary(ProxyInfo* proxy, |
| 1010 | const CFDictionaryRef proxyDict) { |
| 1011 | // the function result. |
| 1012 | bool gotProxy = false; |
| 1013 | |
| 1014 | |
| 1015 | // first we see if there's a SOCKS proxy in place. |
| 1016 | gotProxy = p_getProxyInfoForTypeFromDictWithKeys(proxy, |
| 1017 | PROXY_SOCKS5, |
| 1018 | proxyDict, |
| 1019 | kSCPropNetProxiesSOCKSEnable, |
| 1020 | kSCPropNetProxiesSOCKSProxy, |
| 1021 | kSCPropNetProxiesSOCKSPort); |
| 1022 | |
| 1023 | if (!gotProxy) { |
| 1024 | // okay, no SOCKS proxy, let's look for https. |
| 1025 | gotProxy = p_getProxyInfoForTypeFromDictWithKeys(proxy, |
| 1026 | PROXY_HTTPS, |
| 1027 | proxyDict, |
| 1028 | kSCPropNetProxiesHTTPSEnable, |
| 1029 | kSCPropNetProxiesHTTPSProxy, |
| 1030 | kSCPropNetProxiesHTTPSPort); |
| 1031 | if (!gotProxy) { |
| 1032 | // Finally, try HTTP proxy. Note that flute doesn't |
| 1033 | // differentiate between HTTPS and HTTP, hence we are using the |
| 1034 | // same flute type here, ie. PROXY_HTTPS. |
| 1035 | gotProxy = p_getProxyInfoForTypeFromDictWithKeys( |
| 1036 | proxy, PROXY_HTTPS, proxyDict, kSCPropNetProxiesHTTPEnable, |
| 1037 | kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort); |
| 1038 | } |
| 1039 | } |
| 1040 | return gotProxy; |
| 1041 | } |
| 1042 | |
| 1043 | // TODO(hughv) Update keychain functions. They work on 10.8, but are depricated. |
| 1044 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
| 1045 | bool p_putPasswordInProxyInfo(ProxyInfo* proxy) { |
| 1046 | bool result = true; // by default we assume we're good. |
| 1047 | // for all we know there isn't any password. We'll set to false |
| 1048 | // if we find a problem. |
| 1049 | |
| 1050 | // Ask the keychain for an internet password search for the given protocol. |
| 1051 | OSStatus oss = 0; |
| 1052 | SecKeychainAttributeList attrList; |
| 1053 | attrList.count = 3; |
| 1054 | SecKeychainAttribute attributes[3]; |
| 1055 | attrList.attr = attributes; |
| 1056 | |
| 1057 | attributes[0].tag = kSecProtocolItemAttr; |
| 1058 | attributes[0].length = sizeof(SecProtocolType); |
| 1059 | SecProtocolType protocol; |
| 1060 | switch (proxy->type) { |
| 1061 | case PROXY_HTTPS : |
| 1062 | protocol = kSecProtocolTypeHTTPS; |
| 1063 | break; |
| 1064 | case PROXY_SOCKS5 : |
| 1065 | protocol = kSecProtocolTypeSOCKS; |
| 1066 | break; |
| 1067 | default : |
| 1068 | LOG(LS_ERROR) << "asked for proxy password for unknown proxy type."; |
| 1069 | result = false; |
| 1070 | break; |
| 1071 | } |
| 1072 | attributes[0].data = &protocol; |
| 1073 | |
| 1074 | UInt32 port = proxy->address.port(); |
| 1075 | attributes[1].tag = kSecPortItemAttr; |
| 1076 | attributes[1].length = sizeof(UInt32); |
| 1077 | attributes[1].data = &port; |
| 1078 | |
| 1079 | std::string ip = proxy->address.ipaddr().ToString(); |
| 1080 | attributes[2].tag = kSecServerItemAttr; |
| 1081 | attributes[2].length = ip.length(); |
| 1082 | attributes[2].data = const_cast<char*>(ip.c_str()); |
| 1083 | |
| 1084 | if (result) { |
| 1085 | LOG(LS_INFO) << "trying to get proxy username/password"; |
| 1086 | SecKeychainSearchRef sref; |
| 1087 | oss = SecKeychainSearchCreateFromAttributes(NULL, |
| 1088 | kSecInternetPasswordItemClass, |
| 1089 | &attrList, &sref); |
| 1090 | if (0 == oss) { |
| 1091 | LOG(LS_INFO) << "SecKeychainSearchCreateFromAttributes was good"; |
| 1092 | // Get the first item, if there is one. |
| 1093 | SecKeychainItemRef iref; |
| 1094 | oss = SecKeychainSearchCopyNext(sref, &iref); |
| 1095 | if (0 == oss) { |
| 1096 | LOG(LS_INFO) << "...looks like we have the username/password data"; |
| 1097 | // If there is, get the username and the password. |
| 1098 | |
| 1099 | SecKeychainAttributeInfo attribsToGet; |
| 1100 | attribsToGet.count = 1; |
| 1101 | UInt32 tag = kSecAccountItemAttr; |
| 1102 | UInt32 format = CSSM_DB_ATTRIBUTE_FORMAT_STRING; |
| 1103 | void *data; |
| 1104 | UInt32 length; |
| 1105 | SecKeychainAttributeList *localList; |
| 1106 | |
| 1107 | attribsToGet.tag = &tag; |
| 1108 | attribsToGet.format = &format; |
| 1109 | OSStatus copyres = SecKeychainItemCopyAttributesAndData(iref, |
| 1110 | &attribsToGet, |
| 1111 | NULL, |
| 1112 | &localList, |
| 1113 | &length, |
| 1114 | &data); |
| 1115 | if (0 == copyres) { |
| 1116 | LOG(LS_INFO) << "...and we can pull it out."; |
| 1117 | // now, we know from experimentation (sadly not from docs) |
| 1118 | // that the username is in the local attribute list, |
| 1119 | // and the password in the data, |
| 1120 | // both without null termination but with info on their length. |
| 1121 | // grab the password from the data. |
| 1122 | std::string password; |
| 1123 | password.append(static_cast<const char*>(data), length); |
| 1124 | |
| 1125 | // make the password into a CryptString |
| 1126 | // huh, at the time of writing, you can't. |
| 1127 | // so we'll skip that for now and come back to it later. |
| 1128 | |
| 1129 | // now put the username in the proxy. |
| 1130 | if (1 <= localList->attr->length) { |
| 1131 | proxy->username.append( |
| 1132 | static_cast<const char*>(localList->attr->data), |
| 1133 | localList->attr->length); |
| 1134 | LOG(LS_INFO) << "username is " << proxy->username; |
| 1135 | } else { |
| 1136 | LOG(LS_ERROR) << "got keychain entry with no username"; |
| 1137 | result = false; |
| 1138 | } |
| 1139 | } else { |
| 1140 | LOG(LS_ERROR) << "couldn't copy info from keychain."; |
| 1141 | result = false; |
| 1142 | } |
| 1143 | SecKeychainItemFreeAttributesAndData(localList, data); |
| 1144 | } else if (errSecItemNotFound == oss) { |
| 1145 | LOG(LS_INFO) << "...username/password info not found"; |
| 1146 | } else { |
| 1147 | // oooh, neither 0 nor itemNotFound. |
| 1148 | LOG(LS_ERROR) << "Couldn't get keychain information, error code" << oss; |
| 1149 | result = false; |
| 1150 | } |
| 1151 | } else if (errSecItemNotFound == oss) { // noop |
| 1152 | } else { |
| 1153 | // oooh, neither 0 nor itemNotFound. |
| 1154 | LOG(LS_ERROR) << "Couldn't get keychain information, error code" << oss; |
| 1155 | result = false; |
| 1156 | } |
| 1157 | } |
| 1158 | |
| 1159 | return result; |
| 1160 | } |
| 1161 | |
| 1162 | bool GetMacProxySettings(ProxyInfo* proxy) { |
| 1163 | // based on the Apple Technical Q&A QA1234 |
| 1164 | // http://developer.apple.com/qa/qa2001/qa1234.html |
| 1165 | CFDictionaryRef proxyDict = SCDynamicStoreCopyProxies(NULL); |
| 1166 | bool result = false; |
| 1167 | |
| 1168 | if (proxyDict != NULL) { |
| 1169 | // sending it off to another function makes it easier to unit test |
| 1170 | // since we can make our own dictionary to hand to that function. |
| 1171 | result = GetMacProxySettingsFromDictionary(proxy, proxyDict); |
| 1172 | |
| 1173 | if (result) { |
| 1174 | result = p_putPasswordInProxyInfo(proxy); |
| 1175 | } |
| 1176 | |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 1177 | CFRelease(proxyDict); |
| 1178 | } else { |
| 1179 | LOG(LS_ERROR) << "SCDynamicStoreCopyProxies failed"; |
| 1180 | } |
| 1181 | |
| 1182 | return result; |
| 1183 | } |
| 1184 | #endif // WEBRTC_MAC && !defined(WEBRTC_IOS) |
| 1185 | |
Yuriy Shevchuk | de2382d | 2015-05-21 11:50:59 | [diff] [blame] | 1186 | #ifdef WEBRTC_IOS |
| 1187 | // iOS has only http proxy |
| 1188 | bool GetiOSProxySettings(ProxyInfo* proxy) { |
| 1189 | |
| 1190 | bool result = false; |
| 1191 | |
| 1192 | CFDictionaryRef proxy_dict = CFNetworkCopySystemProxySettings(); |
| 1193 | if (!proxy_dict) { |
| 1194 | LOG(LS_ERROR) << "CFNetworkCopySystemProxySettings failed"; |
| 1195 | return false; |
| 1196 | } |
| 1197 | |
| 1198 | CFNumberRef proxiesHTTPEnable = (CFNumberRef)CFDictionaryGetValue( |
| 1199 | proxy_dict, kCFNetworkProxiesHTTPEnable); |
| 1200 | if (!p_isCFNumberTrue(proxiesHTTPEnable)) { |
| 1201 | CFRelease(proxy_dict); |
| 1202 | return false; |
| 1203 | } |
| 1204 | |
| 1205 | CFStringRef proxy_address = (CFStringRef)CFDictionaryGetValue( |
| 1206 | proxy_dict, kCFNetworkProxiesHTTPProxy); |
| 1207 | CFNumberRef proxy_port = (CFNumberRef)CFDictionaryGetValue( |
| 1208 | proxy_dict, kCFNetworkProxiesHTTPPort); |
| 1209 | |
| 1210 | // the data we need to construct the SocketAddress for the proxy. |
| 1211 | std::string hostname; |
| 1212 | int port; |
| 1213 | if (p_convertHostCFStringRefToCPPString(proxy_address, hostname) && |
| 1214 | p_convertCFNumberToInt(proxy_port, &port)) { |
| 1215 | // We have something enabled, with a hostname and a port. |
| 1216 | // That's sufficient to set up the proxy info. |
| 1217 | // Finally, try HTTP proxy. Note that flute doesn't |
| 1218 | // differentiate between HTTPS and HTTP, hence we are using the |
| 1219 | // same flute type here, ie. PROXY_HTTPS. |
| 1220 | proxy->type = PROXY_HTTPS; |
| 1221 | |
| 1222 | proxy->address.SetIP(hostname); |
| 1223 | proxy->address.SetPort(port); |
| 1224 | result = true; |
| 1225 | } |
| 1226 | |
Yuriy Shevchuk | de2382d | 2015-05-21 11:50:59 | [diff] [blame] | 1227 | CFRelease(proxy_dict); |
| 1228 | |
| 1229 | return result; |
| 1230 | } |
| 1231 | #endif // WEBRTC_IOS |
| 1232 | |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 1233 | bool AutoDetectProxySettings(const char* agent, const char* url, |
| 1234 | ProxyInfo* proxy) { |
| 1235 | #if defined(WEBRTC_WIN) |
| 1236 | return WinHttpAutoDetectProxyForUrl(agent, url, proxy); |
| 1237 | #else |
| 1238 | LOG(LS_WARNING) << "Proxy auto-detection not implemented for this platform"; |
| 1239 | return false; |
| 1240 | #endif |
| 1241 | } |
| 1242 | |
| 1243 | bool GetSystemDefaultProxySettings(const char* agent, const char* url, |
| 1244 | ProxyInfo* proxy) { |
| 1245 | #if defined(WEBRTC_WIN) |
| 1246 | return GetIeProxySettings(agent, url, proxy); |
| 1247 | #elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) |
| 1248 | return GetMacProxySettings(proxy); |
Yuriy Shevchuk | de2382d | 2015-05-21 11:50:59 | [diff] [blame] | 1249 | #elif defined(WEBRTC_IOS) |
| 1250 | return GetiOSProxySettings(proxy); |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 1251 | #else |
| 1252 | // TODO: Get System settings if browser is not firefox. |
| 1253 | return GetFirefoxProxySettings(url, proxy); |
| 1254 | #endif |
| 1255 | } |
| 1256 | |
| 1257 | bool GetProxySettingsForUrl(const char* agent, const char* url, |
| 1258 | ProxyInfo* proxy, bool long_operation) { |
| 1259 | UserAgent a = GetAgent(agent); |
| 1260 | bool result; |
| 1261 | switch (a) { |
| 1262 | case UA_FIREFOX: { |
| 1263 | result = GetFirefoxProxySettings(url, proxy); |
| 1264 | break; |
| 1265 | } |
| 1266 | #if defined(WEBRTC_WIN) |
| 1267 | case UA_INTERNETEXPLORER: |
| 1268 | result = GetIeProxySettings(agent, url, proxy); |
| 1269 | break; |
| 1270 | case UA_UNKNOWN: |
| 1271 | // Agent not defined, check default browser. |
| 1272 | if (IsDefaultBrowserFirefox()) { |
| 1273 | result = GetFirefoxProxySettings(url, proxy); |
| 1274 | } else { |
| 1275 | result = GetIeProxySettings(agent, url, proxy); |
| 1276 | } |
| 1277 | break; |
Yuriy Shevchuk | de2382d | 2015-05-21 11:50:59 | [diff] [blame] | 1278 | #endif // WEBRTC_WIN |
henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 | [diff] [blame] | 1279 | default: |
| 1280 | result = GetSystemDefaultProxySettings(agent, url, proxy); |
| 1281 | break; |
| 1282 | } |
| 1283 | |
| 1284 | // TODO: Consider using the 'long_operation' parameter to |
| 1285 | // decide whether to do the auto detection. |
| 1286 | if (result && (proxy->autodetect || |
| 1287 | !proxy->autoconfig_url.empty())) { |
| 1288 | // Use WinHTTP to auto detect proxy for us. |
| 1289 | result = AutoDetectProxySettings(agent, url, proxy); |
| 1290 | if (!result) { |
| 1291 | // Either auto detection is not supported or we simply didn't |
| 1292 | // find any proxy, reset type. |
| 1293 | proxy->type = rtc::PROXY_NONE; |
| 1294 | } |
| 1295 | } |
| 1296 | return result; |
| 1297 | } |
| 1298 | |
| 1299 | } // namespace rtc |