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 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) 23 #include <SystemConfiguration/SystemConfiguration.h> 24 #include <CoreFoundation/CoreFoundation.h> 25 #include <CoreServices/CoreServices.h> 26 #include <Security/Security.h> 27 #include "macconversion.h" 28 #endif 29 30 #include <map> 31 32 #include "webrtc/base/fileutils.h" 33 #include "webrtc/base/httpcommon.h" 34 #include "webrtc/base/httpcommon-inl.h" 35 #include "webrtc/base/pathutils.h" 36 #include "webrtc/base/stringutils.h" 37 38 #if defined(WEBRTC_WIN) 39 #define _TRY_WINHTTP 1 40 #define _TRY_JSPROXY 0 41 #define _TRY_WM_FINDPROXY 0 42 #define _TRY_IE_LAN_SETTINGS 1 43 #endif // WEBRTC_WIN 44 45 // For all platforms try Firefox. 46 #define _TRY_FIREFOX 1 47 48 // Use profiles.ini to find the correct profile for this user. 49 // If not set, we'll just look for the default one. 50 #define USE_FIREFOX_PROFILES_INI 1 51 52 static const size_t kMaxLineLength = 1024; 53 static const char kFirefoxPattern[] = "Firefox"; 54 static const char kInternetExplorerPattern[] = "MSIE"; 55 56 struct StringMap { 57 public: 58 void Add(const char * name, const char * value) { map_[name] = value; } 59 const std::string& Get(const char * name, const char * def = "") const { 60 std::map<std::string, std::string>::const_iterator it = 61 map_.find(name); 62 if (it != map_.end()) 63 return it->second; 64 def_ = def; 65 return def_; 66 } 67 bool IsSet(const char * name) const { 68 return (map_.find(name) != map_.end()); 69 } 70 private: 71 std::map<std::string, std::string> map_; 72 mutable std::string def_; 73 }; 74 75 enum UserAgent { 76 UA_FIREFOX, 77 UA_INTERNETEXPLORER, 78 UA_OTHER, 79 UA_UNKNOWN 80 }; 81 82 #if _TRY_WINHTTP 83 //#include <winhttp.h> 84 // Note: From winhttp.h 85 86 const char WINHTTP[] = "winhttp"; 87 88 typedef LPVOID HINTERNET; 89 90 typedef struct { 91 DWORD dwAccessType; // see WINHTTP_ACCESS_* types below 92 LPWSTR lpszProxy; // proxy server list 93 LPWSTR lpszProxyBypass; // proxy bypass list 94 } WINHTTP_PROXY_INFO, * LPWINHTTP_PROXY_INFO; 95 96 typedef struct { 97 DWORD dwFlags; 98 DWORD dwAutoDetectFlags; 99 LPCWSTR lpszAutoConfigUrl; 100 LPVOID lpvReserved; 101 DWORD dwReserved; 102 BOOL fAutoLogonIfChallenged; 103 } WINHTTP_AUTOPROXY_OPTIONS; 104 105 typedef struct { 106 BOOL fAutoDetect; 107 LPWSTR lpszAutoConfigUrl; 108 LPWSTR lpszProxy; 109 LPWSTR lpszProxyBypass; 110 } WINHTTP_CURRENT_USER_IE_PROXY_CONFIG; 111 112 extern "C" { 113 typedef HINTERNET (WINAPI * pfnWinHttpOpen) 114 ( 115 IN LPCWSTR pwszUserAgent, 116 IN DWORD dwAccessType, 117 IN LPCWSTR pwszProxyName OPTIONAL, 118 IN LPCWSTR pwszProxyBypass OPTIONAL, 119 IN DWORD dwFlags 120 ); 121 typedef BOOL (STDAPICALLTYPE * pfnWinHttpCloseHandle) 122 ( 123 IN HINTERNET hInternet 124 ); 125 typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetProxyForUrl) 126 ( 127 IN HINTERNET hSession, 128 IN LPCWSTR lpcwszUrl, 129 IN WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions, 130 OUT WINHTTP_PROXY_INFO * pProxyInfo 131 ); 132 typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetIEProxyConfig) 133 ( 134 IN OUT WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * pProxyConfig 135 ); 136 137 } // extern "C" 138 139 #define WINHTTP_AUTOPROXY_AUTO_DETECT 0x00000001 140 #define WINHTTP_AUTOPROXY_CONFIG_URL 0x00000002 141 #define WINHTTP_AUTOPROXY_RUN_INPROCESS 0x00010000 142 #define WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY 0x00020000 143 #define WINHTTP_AUTO_DETECT_TYPE_DHCP 0x00000001 144 #define WINHTTP_AUTO_DETECT_TYPE_DNS_A 0x00000002 145 #define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0 146 #define WINHTTP_ACCESS_TYPE_NO_PROXY 1 147 #define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3 148 #define WINHTTP_NO_PROXY_NAME NULL 149 #define WINHTTP_NO_PROXY_BYPASS NULL 150 151 #endif // _TRY_WINHTTP 152 153 #if _TRY_JSPROXY 154 extern "C" { 155 typedef BOOL (STDAPICALLTYPE * pfnInternetGetProxyInfo) 156 ( 157 LPCSTR lpszUrl, 158 DWORD dwUrlLength, 159 LPSTR lpszUrlHostName, 160 DWORD dwUrlHostNameLength, 161 LPSTR * lplpszProxyHostName, 162 LPDWORD lpdwProxyHostNameLength 163 ); 164 } // extern "C" 165 #endif // _TRY_JSPROXY 166 167 #if _TRY_WM_FINDPROXY 168 #include <comutil.h> 169 #include <wmnetsourcecreator.h> 170 #include <wmsinternaladminnetsource.h> 171 #endif // _TRY_WM_FINDPROXY 172 173 #if _TRY_IE_LAN_SETTINGS 174 #include <wininet.h> 175 #include <string> 176 #endif // _TRY_IE_LAN_SETTINGS 177 178 namespace rtc { 179 180 ////////////////////////////////////////////////////////////////////// 181 // Utility Functions 182 ////////////////////////////////////////////////////////////////////// 183 184 #if defined(WEBRTC_WIN) 185 #ifdef _UNICODE 186 187 typedef std::wstring tstring; 188 std::string Utf8String(const tstring& str) { return ToUtf8(str); } 189 190 #else // !_UNICODE 191 192 typedef std::string tstring; 193 std::string Utf8String(const tstring& str) { return str; } 194 195 #endif // !_UNICODE 196 #endif // WEBRTC_WIN 197 198 bool ProxyItemMatch(const Url<char>& url, char * item, size_t len) { 199 // hostname:443 200 if (char * port = ::strchr(item, ':')) { 201 *port++ = '\0'; 202 if (url.port() != atol(port)) { 203 return false; 204 } 205 } 206 207 // A.B.C.D or A.B.C.D/24 208 int a, b, c, d, m; 209 int match = sscanf(item, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &m); 210 if (match >= 4) { 211 uint32 ip = ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | 212 (d & 0xFF); 213 if ((match < 5) || (m > 32)) 214 m = 32; 215 else if (m < 0) 216 m = 0; 217 uint32 mask = (m == 0) ? 0 : (~0UL) << (32 - m); 218 SocketAddress addr(url.host(), 0); 219 // TODO: Support IPv6 proxyitems. This code block is IPv4 only anyway. 220 return !addr.IsUnresolved() && 221 ((addr.ipaddr().v4AddressAsHostOrderInteger() & mask) == (ip & mask)); 222 } 223 224 // .foo.com 225 if (*item == '.') { 226 size_t hostlen = url.host().length(); 227 return (hostlen > len) 228 && (stricmp(url.host().c_str() + (hostlen - len), item) == 0); 229 } 230 231 // localhost or www.*.com 232 if (!string_match(url.host().c_str(), item)) 233 return false; 234 235 return true; 236 } 237 238 bool ProxyListMatch(const Url<char>& url, const std::string& proxy_list, 239 char sep) { 240 const size_t BUFSIZE = 256; 241 char buffer[BUFSIZE]; 242 const char* list = proxy_list.c_str(); 243 while (*list) { 244 // Remove leading space 245 if (isspace(*list)) { 246 ++list; 247 continue; 248 } 249 // Break on separator 250 size_t len; 251 const char * start = list; 252 if (const char * end = ::strchr(list, sep)) { 253 len = (end - list); 254 list += len + 1; 255 } else { 256 len = strlen(list); 257 list += len; 258 } 259 // Remove trailing space 260 while ((len > 0) && isspace(start[len-1])) 261 --len; 262 // Check for oversized entry 263 if (len >= BUFSIZE) 264 continue; 265 memcpy(buffer, start, len); 266 buffer[len] = 0; 267 if (!ProxyItemMatch(url, buffer, len)) 268 continue; 269 return true; 270 } 271 return false; 272 } 273 274 bool Better(ProxyType lhs, const ProxyType rhs) { 275 // PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN 276 const int PROXY_VALUE[5] = { 0, 2, 3, 1 }; 277 return (PROXY_VALUE[lhs] > PROXY_VALUE[rhs]); 278 } 279 280 bool ParseProxy(const std::string& saddress, ProxyInfo* proxy) { 281 const size_t kMaxAddressLength = 1024; 282 // Allow semicolon, space, or tab as an address separator 283 const char* const kAddressSeparator = " ;\t"; 284 285 ProxyType ptype; 286 std::string host; 287 uint16 port; 288 289 const char* address = saddress.c_str(); 290 while (*address) { 291 size_t len; 292 const char * start = address; 293 if (const char * sep = strchr(address, kAddressSeparator)) { 294 len = (sep - address); 295 address += len + 1; 296 while (*address != '\0' && ::strchr(kAddressSeparator, *address)) { 297 address += 1; 298 } 299 } else { 300 len = strlen(address); 301 address += len; 302 } 303 304 if (len > kMaxAddressLength - 1) { 305 LOG(LS_WARNING) << "Proxy address too long [" << start << "]"; 306 continue; 307 } 308 309 char buffer[kMaxAddressLength]; 310 memcpy(buffer, start, len); 311 buffer[len] = 0; 312 313 char * colon = ::strchr(buffer, ':'); 314 if (!colon) { 315 LOG(LS_WARNING) << "Proxy address without port [" << buffer << "]"; 316 continue; 317 } 318 319 *colon = 0; 320 char * endptr; 321 port = static_cast<uint16>(strtol(colon + 1, &endptr, 0)); 322 if (*endptr != 0) { 323 LOG(LS_WARNING) << "Proxy address with invalid port [" << buffer << "]"; 324 continue; 325 } 326 327 if (char * equals = ::strchr(buffer, '=')) { 328 *equals = 0; 329 host = equals + 1; 330 if (_stricmp(buffer, "socks") == 0) { 331 ptype = PROXY_SOCKS5; 332 } else if (_stricmp(buffer, "https") == 0) { 333 ptype = PROXY_HTTPS; 334 } else { 335 LOG(LS_WARNING) << "Proxy address with unknown protocol [" 336 << buffer << "]"; 337 ptype = PROXY_UNKNOWN; 338 } 339 } else { 340 host = buffer; 341 ptype = PROXY_UNKNOWN; 342 } 343 344 if (Better(ptype, proxy->type)) { 345 proxy->type = ptype; 346 proxy->address.SetIP(host); 347 proxy->address.SetPort(port); 348 } 349 } 350 351 return proxy->type != PROXY_NONE; 352 } 353 354 UserAgent GetAgent(const char* agent) { 355 if (agent) { 356 std::string agent_str(agent); 357 if (agent_str.find(kFirefoxPattern) != std::string::npos) { 358 return UA_FIREFOX; 359 } else if (agent_str.find(kInternetExplorerPattern) != std::string::npos) { 360 return UA_INTERNETEXPLORER; 361 } else if (agent_str.empty()) { 362 return UA_UNKNOWN; 363 } 364 } 365 return UA_OTHER; 366 } 367 368 bool EndsWith(const std::string& a, const std::string& b) { 369 if (b.size() > a.size()) { 370 return false; 371 } 372 int result = a.compare(a.size() - b.size(), b.size(), b); 373 return result == 0; 374 } 375 376 bool GetFirefoxProfilePath(Pathname* path) { 377 #if defined(WEBRTC_WIN) 378 wchar_t w_path[MAX_PATH]; 379 if (SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, w_path) != 380 S_OK) { 381 LOG(LS_ERROR) << "SHGetFolderPath failed"; 382 return false; 383 } 384 path->SetFolder(ToUtf8(w_path, wcslen(w_path))); 385 path->AppendFolder("Mozilla"); 386 path->AppendFolder("Firefox"); 387 #elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) 388 FSRef fr; 389 if (0 != FSFindFolder(kUserDomain, kApplicationSupportFolderType, 390 kCreateFolder, &fr)) { 391 LOG(LS_ERROR) << "FSFindFolder failed"; 392 return false; 393 } 394 char buffer[NAME_MAX + 1]; 395 if (0 != FSRefMakePath(&fr, reinterpret_cast<uint8*>(buffer), 396 ARRAY_SIZE(buffer))) { 397 LOG(LS_ERROR) << "FSRefMakePath failed"; 398 return false; 399 } 400 path->SetFolder(std::string(buffer)); 401 path->AppendFolder("Firefox"); 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 ASSERT(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 scoped_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 scoped_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(WEBRTC_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 // We created the dictionary with something that had the 1171 // word 'copy' in it, so we have to release it, according 1172 // to the Carbon memory management standards. 1173 CFRelease(proxyDict); 1174 } else { 1175 LOG(LS_ERROR) << "SCDynamicStoreCopyProxies failed"; 1176 } 1177 1178 return result; 1179 } 1180 #endif // WEBRTC_MAC && !defined(WEBRTC_IOS) 1181 1182 bool AutoDetectProxySettings(const char* agent, const char* url, 1183 ProxyInfo* proxy) { 1184 #if defined(WEBRTC_WIN) 1185 return WinHttpAutoDetectProxyForUrl(agent, url, proxy); 1186 #else 1187 LOG(LS_WARNING) << "Proxy auto-detection not implemented for this platform"; 1188 return false; 1189 #endif 1190 } 1191 1192 bool GetSystemDefaultProxySettings(const char* agent, const char* url, 1193 ProxyInfo* proxy) { 1194 #if defined(WEBRTC_WIN) 1195 return GetIeProxySettings(agent, url, proxy); 1196 #elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) 1197 return GetMacProxySettings(proxy); 1198 #else 1199 // TODO: Get System settings if browser is not firefox. 1200 return GetFirefoxProxySettings(url, proxy); 1201 #endif 1202 } 1203 1204 bool GetProxySettingsForUrl(const char* agent, const char* url, 1205 ProxyInfo* proxy, bool long_operation) { 1206 UserAgent a = GetAgent(agent); 1207 bool result; 1208 switch (a) { 1209 case UA_FIREFOX: { 1210 result = GetFirefoxProxySettings(url, proxy); 1211 break; 1212 } 1213 #if defined(WEBRTC_WIN) 1214 case UA_INTERNETEXPLORER: 1215 result = GetIeProxySettings(agent, url, proxy); 1216 break; 1217 case UA_UNKNOWN: 1218 // Agent not defined, check default browser. 1219 if (IsDefaultBrowserFirefox()) { 1220 result = GetFirefoxProxySettings(url, proxy); 1221 } else { 1222 result = GetIeProxySettings(agent, url, proxy); 1223 } 1224 break; 1225 #endif // WEBRTC_WIN 1226 default: 1227 result = GetSystemDefaultProxySettings(agent, url, proxy); 1228 break; 1229 } 1230 1231 // TODO: Consider using the 'long_operation' parameter to 1232 // decide whether to do the auto detection. 1233 if (result && (proxy->autodetect || 1234 !proxy->autoconfig_url.empty())) { 1235 // Use WinHTTP to auto detect proxy for us. 1236 result = AutoDetectProxySettings(agent, url, proxy); 1237 if (!result) { 1238 // Either auto detection is not supported or we simply didn't 1239 // find any proxy, reset type. 1240 proxy->type = rtc::PROXY_NONE; 1241 } 1242 } 1243 return result; 1244 } 1245 1246 } // namespace rtc 1247