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