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