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