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