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