3 * Copyright 2004--2005, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
28 #include "talk/base/proxydetect.h"
31 #include "talk/base/win32.h"
40 #include <SystemConfiguration/SystemConfiguration.h>
41 #include <CoreFoundation/CoreFoundation.h>
42 #include <CoreServices/CoreServices.h>
43 #include <Security/Security.h>
44 #include "macconversion.h"
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"
56 #define _TRY_WINHTTP 1
57 #define _TRY_JSPROXY 0
58 #define _TRY_WM_FINDPROXY 0
59 #define _TRY_IE_LAN_SETTINGS 1
62 // For all platforms try Firefox.
63 #define _TRY_FIREFOX 1
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
69 static const size_t kMaxLineLength = 1024;
70 static const char kFirefoxPattern[] = "Firefox";
71 static const char kInternetExplorerPattern[] = "MSIE";
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 =
84 bool IsSet(const char * name) const {
85 return (map_.find(name) != map_.end());
88 std::map<std::string, std::string> map_;
89 mutable std::string def_;
100 //#include <winhttp.h>
101 // Note: From winhttp.h
103 const char WINHTTP[] = "winhttp";
105 typedef LPVOID HINTERNET;
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;
115 DWORD dwAutoDetectFlags;
116 LPCWSTR lpszAutoConfigUrl;
119 BOOL fAutoLogonIfChallenged;
120 } WINHTTP_AUTOPROXY_OPTIONS;
124 LPWSTR lpszAutoConfigUrl;
126 LPWSTR lpszProxyBypass;
127 } WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
130 typedef HINTERNET (WINAPI * pfnWinHttpOpen)
132 IN LPCWSTR pwszUserAgent,
133 IN DWORD dwAccessType,
134 IN LPCWSTR pwszProxyName OPTIONAL,
135 IN LPCWSTR pwszProxyBypass OPTIONAL,
138 typedef BOOL (STDAPICALLTYPE * pfnWinHttpCloseHandle)
140 IN HINTERNET hInternet
142 typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetProxyForUrl)
144 IN HINTERNET hSession,
145 IN LPCWSTR lpcwszUrl,
146 IN WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions,
147 OUT WINHTTP_PROXY_INFO * pProxyInfo
149 typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetIEProxyConfig)
151 IN OUT WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * pProxyConfig
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
168 #endif // _TRY_WINHTTP
172 typedef BOOL (STDAPICALLTYPE * pfnInternetGetProxyInfo)
176 LPSTR lpszUrlHostName,
177 DWORD dwUrlHostNameLength,
178 LPSTR * lplpszProxyHostName,
179 LPDWORD lpdwProxyHostNameLength
182 #endif // _TRY_JSPROXY
184 #if _TRY_WM_FINDPROXY
186 #include <wmnetsourcecreator.h>
187 #include <wmsinternaladminnetsource.h>
188 #endif // _TRY_WM_FINDPROXY
190 #if _TRY_IE_LAN_SETTINGS
193 #endif // _TRY_IE_LAN_SETTINGS
195 namespace talk_base {
197 //////////////////////////////////////////////////////////////////////
199 //////////////////////////////////////////////////////////////////////
204 typedef std::wstring tstring;
205 std::string Utf8String(const tstring& str) { return ToUtf8(str); }
209 typedef std::string tstring;
210 std::string Utf8String(const tstring& str) { return str; }
215 bool ProxyItemMatch(const Url<char>& url, char * item, size_t len) {
217 if (char * port = ::strchr(item, ':')) {
219 if (url.port() != atol(port)) {
224 // A.B.C.D or A.B.C.D/24
226 int match = sscanf(item, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &m);
228 uint32 ip = ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) |
230 if ((match < 5) || (m > 32))
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));
243 size_t hostlen = url.host().length();
244 return (hostlen > len)
245 && (stricmp(url.host().c_str() + (hostlen - len), item) == 0);
248 // localhost or www.*.com
249 if (!string_match(url.host().c_str(), item))
255 bool ProxyListMatch(const Url<char>& url, const std::string& proxy_list,
257 const size_t BUFSIZE = 256;
258 char buffer[BUFSIZE];
259 const char* list = proxy_list.c_str();
261 // Remove leading space
262 if (isspace(*list)) {
266 // Break on separator
268 const char * start = list;
269 if (const char * end = ::strchr(list, sep)) {
276 // Remove trailing space
277 while ((len > 0) && isspace(start[len-1]))
279 // Check for oversized entry
282 memcpy(buffer, start, len);
284 if (!ProxyItemMatch(url, buffer, len))
291 bool 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]);
297 bool 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";
306 const char* address = saddress.c_str();
309 const char * start = address;
310 if (const char * sep = strchr(address, kAddressSeparator)) {
311 len = (sep - address);
313 while (*address != '\0' && ::strchr(kAddressSeparator, *address)) {
317 len = strlen(address);
321 if (len > kMaxAddressLength - 1) {
322 LOG(LS_WARNING) << "Proxy address too long [" << start << "]";
326 char buffer[kMaxAddressLength];
327 memcpy(buffer, start, len);
330 char * colon = ::strchr(buffer, ':');
332 LOG(LS_WARNING) << "Proxy address without port [" << buffer << "]";
338 port = static_cast<uint16>(strtol(colon + 1, &endptr, 0));
340 LOG(LS_WARNING) << "Proxy address with invalid port [" << buffer << "]";
344 if (char * equals = ::strchr(buffer, '=')) {
347 if (_stricmp(buffer, "socks") == 0) {
348 ptype = PROXY_SOCKS5;
349 } else if (_stricmp(buffer, "https") == 0) {
352 LOG(LS_WARNING) << "Proxy address with unknown protocol ["
354 ptype = PROXY_UNKNOWN;
358 ptype = PROXY_UNKNOWN;
361 if (Better(ptype, proxy->type)) {
363 proxy->address.SetIP(host);
364 proxy->address.SetPort(port);
368 return proxy->type != PROXY_NONE;
371 UserAgent GetAgent(const char* agent) {
373 std::string agent_str(agent);
374 if (agent_str.find(kFirefoxPattern) != std::string::npos) {
376 } else if (agent_str.find(kInternetExplorerPattern) != std::string::npos) {
377 return UA_INTERNETEXPLORER;
378 } else if (agent_str.empty()) {
385 bool EndsWith(const std::string& a, const std::string& b) {
386 if (b.size() > a.size()) {
389 int result = a.compare(a.size() - b.size(), b.size(), b);
393 bool GetFirefoxProfilePath(Pathname* path) {
395 wchar_t w_path[MAX_PATH];
396 if (SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, w_path) !=
398 LOG(LS_ERROR) << "SHGetFolderPath failed";
401 path->SetFolder(ToUtf8(w_path, wcslen(w_path)));
402 path->AppendFolder("Mozilla");
403 path->AppendFolder("Firefox");
406 if (0 != FSFindFolder(kUserDomain, kApplicationSupportFolderType,
407 kCreateFolder, &fr)) {
408 LOG(LS_ERROR) << "FSFindFolder failed";
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";
417 path->SetFolder(std::string(buffer));
418 path->AppendFolder("Firefox");
420 char* user_home = getenv("HOME");
421 if (user_home == NULL) {
424 path->SetFolder(std::string(user_home));
425 path->AppendFolder(".mozilla");
426 path->AppendFolder("firefox");
431 bool GetDefaultFirefoxProfile(Pathname* profile_path) {
432 ASSERT(NULL != profile_path);
434 if (!GetFirefoxProfilePath(&path)) {
438 #if USE_FIREFOX_PROFILES_INI
442 // Path=Profiles/2de53ejb.default
445 // Note: we are looking for the first entry with "Default=1", or the last
447 path.SetFilename("profiles.ini");
448 scoped_ptr<FileStream> fs(Filesystem::OpenFile(path, "r"));
453 bool relative = true;
455 while (fs->ReadLine(&line) == SR_SUCCESS) {
456 if (line.length() == 0) {
459 if (line.at(0) == '[') {
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) {
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()) {
484 if (candidate.empty()) {
487 profile_path->SetPathname(candidate.pathname());
489 #else // !USE_FIREFOX_PROFILES_INI
490 path.AppendFolder("Profiles");
491 DirectoryIterator* it = Filesystem::IterateDirectory();
493 std::string extension(".default");
494 while (!EndsWith(it->Name(), extension)) {
500 profile_path->SetPathname(path);
501 profile->AppendFolder("Profiles");
502 profile->AppendFolder(it->Name());
505 #endif // !USE_FIREFOX_PROFILES_INI
510 bool ReadFirefoxPrefs(const Pathname& filename,
512 StringMap* settings) {
513 scoped_ptr<FileStream> fs(Filesystem::OpenFile(filename, "r"));
515 LOG(LS_ERROR) << "Failed to open file: " << filename.pathname();
520 while (fs->ReadLine(&line) == SR_SUCCESS) {
521 size_t prefix_len = strlen(prefix);
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) {
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);
536 char* name = buffer + nstart;
537 name[nend - nstart] = 0;
538 if ((vend - vstart >= 2) && (buffer[vstart] == '"')) {
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);
548 LOG_F(LS_WARNING) << "Unparsed pref [" << buffer << "]";
555 bool GetFirefoxProxySettings(const char* url, ProxyInfo* proxy) {
558 bool success = false;
559 if (GetDefaultFirefoxProfile(&path)) {
561 path.SetFilename("prefs.js");
562 if (ReadFirefoxPrefs(path, "network.proxy.", &settings)) {
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
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()));
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;
602 #ifdef WIN32 // Windows specific implementation for reading Internet
603 // Explorer proxy settings.
605 void LogGetProxyFault() {
606 LOG_GLEM(LERROR, WINHTTP) << "WinHttpGetProxyForUrl faulted!!";
609 BOOL 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
617 BOOL success = FALSE;
619 #if (_HAS_EXCEPTIONS == 0)
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
628 success = pWHGPFU(hWinHttp, url, options, info);
629 #endif // (_HAS_EXCEPTIONS == 0)
634 bool IsDefaultBrowserFirefox() {
636 LONG result = RegOpenKeyEx(HKEY_CLASSES_ROOT, L"http\\shell\\open\\command",
638 if (ERROR_SUCCESS != result)
641 wchar_t* value = NULL;
643 result = RegQueryValueEx(key, L"", 0, &type, NULL, &size);
644 if (REG_SZ != type) {
645 result = ERROR_ACCESS_DENIED; // Any error is fine
646 } else if (ERROR_SUCCESS == result) {
647 value = new wchar_t[size+1];
648 BYTE* buffer = reinterpret_cast<BYTE*>(value);
649 result = RegQueryValueEx(key, L"", 0, &type, buffer, &size);
653 bool success = false;
654 if (ERROR_SUCCESS == result) {
656 for (size_t i = 0; i < size; ++i) {
657 value[i] = tolowercase(value[i]);
659 success = (NULL != strstr(value, L"firefox.exe"));
665 bool 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.";
671 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG iecfg;
672 memset(&iecfg, 0, sizeof(iecfg));
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.
682 if (iecfg.fAutoDetect) {
683 proxy->autodetect = true;
685 if (iecfg.lpszAutoConfigUrl) {
686 proxy->autoconfig_url = ToUtf8(iecfg.lpszAutoConfigUrl);
687 GlobalFree(iecfg.lpszAutoConfigUrl);
689 if (iecfg.lpszProxyBypass) {
690 proxy->bypass_list = ToUtf8(iecfg.lpszProxyBypass);
691 GlobalFree(iecfg.lpszProxyBypass);
693 if (iecfg.lpszProxy) {
694 if (!ProxyListMatch(purl, proxy->bypass_list, ';')) {
695 ParseProxy(ToUtf8(iecfg.lpszProxy), proxy);
697 GlobalFree(iecfg.lpszProxy);
700 FreeLibrary(winhttp_handle);
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
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.
714 bool WinHttpAutoDetectProxyForUrl(const char* agent, const char* url,
718 HMODULE winhttp_handle = LoadLibrary(L"winhttp.dll");
719 if (winhttp_handle == NULL) {
720 LOG(LS_ERROR) << "Failed to load winhttp.dll.";
723 pfnWinHttpOpen pWHO =
724 reinterpret_cast<pfnWinHttpOpen>(GetProcAddress(winhttp_handle,
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,
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;
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);
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;
761 std::wstring autoconfig_url16((ToUtf16)(proxy->autoconfig_url));
762 options.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL;
763 options.lpszAutoConfigUrl = autoconfig_url16.c_str();
765 result = MyWinHttpGetProxyForUrl(
766 pWHGPFU, hWinHttp, ToUtf16(url).c_str(), &options, &info);
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);
778 proxy->bypass_list.clear();
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);
785 GlobalFree(info.lpszProxy);
788 // We could not find any proxy for this url.
789 LOG(LS_INFO) << "No proxy detected for " << url;
794 LOG(LS_ERROR) << "Failed loading WinHTTP functions.";
797 FreeLibrary(winhttp_handle);
801 #if 0 // Below functions currently not used.
803 bool GetJsProxySettings(const char* url, ProxyInfo* proxy) {
805 bool success = false;
807 if (HMODULE hModJS = LoadLibrary(_T("jsproxy.dll"))) {
808 pfnInternetGetProxyInfo pIGPI =
809 reinterpret_cast<pfnInternetGetProxyInfo>(
810 GetProcAddress(hModJS, "InternetGetProxyInfo"));
812 char proxy[256], host[256];
813 memset(proxy, 0, sizeof(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;
822 LOG_GLE(INFO) << "InternetGetProxyInfo";
830 bool GetWmProxySettings(const char* url, ProxyInfo* proxy) {
832 bool success = false;
834 INSNetSourceCreator * nsc = 0;
835 HRESULT hr = CoCreateInstance(CLSID_ClientNetManager, 0, CLSCTX_ALL,
836 IID_INSNetSourceCreator, (LPVOID *) &nsc);
838 if (SUCCEEDED(hr = nsc->Initialize())) {
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());
847 BOOL bProxyEnabled = FALSE;
848 DWORD port, context = 0;
849 if (SUCCEEDED(hr = ians->FindProxyForURL(
850 L"http", host, &bProxyEnabled, &proxy, &port, &context))) {
853 _bstr_t sproxy = proxy;
854 proxy->ptype = PT_HTTPS;
855 proxy->host = sproxy;
859 SysFreeString(proxy);
860 if (FAILED(hr = ians->ShutdownProxyContext(context))) {
861 LOG(LS_INFO) << "IWMSInternalAdminNetSource::ShutdownProxyContext"
867 VariantClear(&dispatch);
868 if (FAILED(hr = nsc->Shutdown())) {
869 LOG(LS_INFO) << "INSNetSourceCreator::Shutdown failed: " << hr;
877 bool GetIePerConnectionProxySettings(const char* url, ProxyInfo* proxy) {
879 bool success = false;
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));
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);
894 if (!InternetQueryOption(0, INTERNET_OPTION_PER_CONNECTION_OPTION, &list,
896 LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError();
897 } else if ((options[0].Value.dwValue & PROXY_TYPE_PROXY) != 0) {
899 if (!ProxyListMatch(purl, nonnull(options[2].Value.pszValue), _T(';'))) {
900 ParseProxy(nonnull(options[1].Value.pszValue), proxy);
902 } else if ((options[0].Value.dwValue & PROXY_TYPE_DIRECT) != 0) {
905 LOG(LS_INFO) << "unknown internet access type: "
906 << options[0].Value.dwValue;
908 if (options[1].Value.pszValue) {
909 GlobalFree(options[1].Value.pszValue);
911 if (options[2].Value.pszValue) {
912 GlobalFree(options[2].Value.pszValue);
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.
922 bool GetIeLanProxySettings(const char* url, ProxyInfo* proxy) {
924 bool success = false;
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);
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) {
935 } else if (info->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
937 if (!ProxyListMatch(purl, nonnull(reinterpret_cast<const char*>(
938 info->lpszProxyBypass)), ' ')) {
939 ParseProxy(nonnull(reinterpret_cast<const char*>(info->lpszProxy)),
943 LOG(LS_INFO) << "unknown internet access type: " << info->dwAccessType;
948 bool GetIeProxySettings(const char* agent, const char* url, ProxyInfo* proxy) {
949 bool success = GetWinHttpProxySettings(url, proxy);
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);
961 #ifdef OSX // OSX specific implementation for reading system wide
964 bool p_getProxyInfoForTypeFromDictWithKeys(ProxyInfo* proxy,
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.
973 // we use this as a scratch variable for determining if operations
975 bool converted = false;
977 // the data we need to construct the SocketAddress for the proxy.
978 std::string hostname;
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;
989 enabledCFNum = (CFNumberRef)CFDictionaryGetValue(proxyDict, enabledKey);
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);
996 portCFNum = (CFNumberRef)CFDictionaryGetValue(proxyDict, portKey);
997 converted = p_convertCFNumberToInt(portCFNum, &port);
999 // we have something enabled, with a hostname and a port.
1000 // That's sufficient to set up the proxy info.
1002 proxy->address.SetIP(hostname);
1003 proxy->address.SetPort(port);
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.
1019 bool GetMacProxySettingsFromDictionary(ProxyInfo* proxy,
1020 const CFDictionaryRef proxyDict) {
1021 // the function result.
1022 bool gotProxy = false;
1025 // first we see if there's a SOCKS proxy in place.
1026 gotProxy = p_getProxyInfoForTypeFromDictWithKeys(proxy,
1029 kSCPropNetProxiesSOCKSEnable,
1030 kSCPropNetProxiesSOCKSProxy,
1031 kSCPropNetProxiesSOCKSPort);
1034 // okay, no SOCKS proxy, let's look for https.
1035 gotProxy = p_getProxyInfoForTypeFromDictWithKeys(proxy,
1038 kSCPropNetProxiesHTTPSEnable,
1039 kSCPropNetProxiesHTTPSProxy,
1040 kSCPropNetProxiesHTTPSPort);
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);
1053 // TODO(hughv) Update keychain functions. They work on 10.8, but are depricated.
1054 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1055 bool 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.
1060 // Ask the keychain for an internet password search for the given protocol.
1062 SecKeychainAttributeList attrList;
1064 SecKeychainAttribute attributes[3];
1065 attrList.attr = attributes;
1067 attributes[0].tag = kSecProtocolItemAttr;
1068 attributes[0].length = sizeof(SecProtocolType);
1069 SecProtocolType protocol;
1070 switch (proxy->type) {
1072 protocol = kSecProtocolTypeHTTPS;
1075 protocol = kSecProtocolTypeSOCKS;
1078 LOG(LS_ERROR) << "asked for proxy password for unknown proxy type.";
1082 attributes[0].data = &protocol;
1084 UInt32 port = proxy->address.port();
1085 attributes[1].tag = kSecPortItemAttr;
1086 attributes[1].length = sizeof(UInt32);
1087 attributes[1].data = &port;
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());
1095 LOG(LS_INFO) << "trying to get proxy username/password";
1096 SecKeychainSearchRef sref;
1097 oss = SecKeychainSearchCreateFromAttributes(NULL,
1098 kSecInternetPasswordItemClass,
1101 LOG(LS_INFO) << "SecKeychainSearchCreateFromAttributes was good";
1102 // Get the first item, if there is one.
1103 SecKeychainItemRef iref;
1104 oss = SecKeychainSearchCopyNext(sref, &iref);
1106 LOG(LS_INFO) << "...looks like we have the username/password data";
1107 // If there is, get the username and the password.
1109 SecKeychainAttributeInfo attribsToGet;
1110 attribsToGet.count = 1;
1111 UInt32 tag = kSecAccountItemAttr;
1112 UInt32 format = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
1115 SecKeychainAttributeList *localList;
1117 attribsToGet.tag = &tag;
1118 attribsToGet.format = &format;
1119 OSStatus copyres = SecKeychainItemCopyAttributesAndData(iref,
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);
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.
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;
1146 LOG(LS_ERROR) << "got keychain entry with no username";
1150 LOG(LS_ERROR) << "couldn't copy info from keychain.";
1153 SecKeychainItemFreeAttributesAndData(localList, data);
1154 } else if (errSecItemNotFound == oss) {
1155 LOG(LS_INFO) << "...username/password info not found";
1157 // oooh, neither 0 nor itemNotFound.
1158 LOG(LS_ERROR) << "Couldn't get keychain information, error code" << oss;
1161 } else if (errSecItemNotFound == oss) { // noop
1163 // oooh, neither 0 nor itemNotFound.
1164 LOG(LS_ERROR) << "Couldn't get keychain information, error code" << oss;
1172 bool 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;
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);
1184 result = p_putPasswordInProxyInfo(proxy);
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);
1192 LOG(LS_ERROR) << "SCDynamicStoreCopyProxies failed";
1199 bool AutoDetectProxySettings(const char* agent, const char* url,
1202 return WinHttpAutoDetectProxyForUrl(agent, url, proxy);
1204 LOG(LS_WARNING) << "Proxy auto-detection not implemented for this platform";
1209 bool GetSystemDefaultProxySettings(const char* agent, const char* url,
1212 return GetIeProxySettings(agent, url, proxy);
1214 return GetMacProxySettings(proxy);
1216 // TODO: Get System settings if browser is not firefox.
1217 return GetFirefoxProxySettings(url, proxy);
1221 bool GetProxySettingsForUrl(const char* agent, const char* url,
1222 ProxyInfo* proxy, bool long_operation) {
1223 UserAgent a = GetAgent(agent);
1227 result = GetFirefoxProxySettings(url, proxy);
1231 case UA_INTERNETEXPLORER:
1232 result = GetIeProxySettings(agent, url, proxy);
1235 // Agent not defined, check default browser.
1236 if (IsDefaultBrowserFirefox()) {
1237 result = GetFirefoxProxySettings(url, proxy);
1239 result = GetIeProxySettings(agent, url, proxy);
1244 result = GetSystemDefaultProxySettings(agent, url, proxy);
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);
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;
1263 } // namespace talk_base