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