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