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