Bug 1200802 - Accept RFC1929 SOCKS credentials in proxyInfo. r=michal
authorBen Bucksch <ben.bucksch@beonex.com>
Tue, 24 Nov 2015 22:56:00 +0100 (2015-11-24)
changeset 274211 76cee391a698d2dcea75600b34fc04ddff7ea000
parent 274210 82c89f573e53c851f912e4d8f64278e1a398c87d
child 274212 ebb6fb453ccaf38e34add4fa109dc01bedaaab31
push id29727
push usercbook@mozilla.com
push dateThu, 26 Nov 2015 15:54:54 +0000 (2015-11-26)
treeherdermozilla-central@74c7941a9e22 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmichal
bugs1200802
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1200802 - Accept RFC1929 SOCKS credentials in proxyInfo. r=michal
netwerk/base/nsIProtocolProxyService.idl
netwerk/base/nsIProxyInfo.idl
netwerk/base/nsProtocolProxyService.cpp
netwerk/base/nsProtocolProxyService.h
netwerk/base/nsProxyInfo.cpp
netwerk/base/nsProxyInfo.h
netwerk/base/nsSocketTransport2.cpp
netwerk/base/nsSocketTransport2.h
netwerk/protocol/http/TunnelUtils.cpp
netwerk/protocol/http/nsHttpConnectionInfo.cpp
netwerk/protocol/http/nsHttpConnectionInfo.h
netwerk/socket/nsISocketProvider.idl
netwerk/socket/nsSOCKSIOLayer.cpp
netwerk/socket/nsSOCKSIOLayer.h
netwerk/socket/nsSOCKSSocketProvider.cpp
netwerk/socket/nsUDPSocketProvider.cpp
security/manager/ssl/nsNSSIOLayer.cpp
security/manager/ssl/nsNSSIOLayer.h
security/manager/ssl/nsSSLSocketProvider.cpp
security/manager/ssl/nsTLSSocketProvider.cpp
--- a/netwerk/base/nsIProtocolProxyService.idl
+++ b/netwerk/base/nsIProtocolProxyService.idl
@@ -13,17 +13,17 @@ interface nsIProtocolProxyChannelFilter;
 interface nsIProxyInfo;
 interface nsIChannel;
 interface nsIURI;
 
 /**
  * nsIProtocolProxyService provides methods to access information about
  * various network proxies.
  */
-[scriptable, uuid(deec8e08-a246-443c-90b6-f655632d1abf)]
+[scriptable, uuid(ef57c8b6-e09d-4cd4-9222-2a5d2402e15d)]
 interface nsIProtocolProxyService : nsISupports
 {
     /** Flag 1 << 0 is unused **/
 
     /**
      * When the proxy configuration is manual this flag may be passed to the
      * resolve and asyncResolve methods to request to prefer the SOCKS proxy
      * to HTTP ones.
@@ -135,16 +135,55 @@ interface nsIProtocolProxyService : nsIS
      *        parameter may be null.
      */
     nsIProxyInfo newProxyInfo(in ACString aType, in AUTF8String aHost,
                               in long aPort, in unsigned long aFlags,
                               in unsigned long aFailoverTimeout,
                               in nsIProxyInfo aFailoverProxy);
 
     /**
+     * This method may be called to construct a nsIProxyInfo instance for
+     * with the specified username and password.
+     * Currently implemented for SOCKS proxies only.
+     * @param aType
+     *        The proxy type.  This is a string value that identifies the proxy
+     *        type.  Standard values include:
+     *          "socks"   - specifies a SOCKS version 5 proxy
+     *          "socks4"  - specifies a SOCKS version 4 proxy
+     *        The type name is case-insensitive.  Other string values may be
+     *        possible, and new types may be defined by a future version of
+     *        this interface.
+     * @param aHost
+     *        The proxy hostname or IP address.
+     * @param aPort
+     *        The proxy port.
+     * @param aUsername
+     *        The proxy username
+     * @param aPassword
+     *        The proxy password
+     * @param aFlags
+     *        Flags associated with this connection.  See nsIProxyInfo.idl
+     *        for currently defined flags.
+     * @param aFailoverTimeout
+     *        Specifies the length of time (in seconds) to ignore this proxy if
+     *        this proxy fails.  Pass UINT32_MAX to specify the default
+     *        timeout value, causing nsIProxyInfo::failoverTimeout to be
+     *        assigned the default value.
+     * @param aFailoverProxy
+     *        Specifies the next proxy to try if this proxy fails.  This
+     *        parameter may be null.
+     */
+    nsIProxyInfo newProxyInfoWithAuth(in ACString aType, in AUTF8String aHost,
+                                      in long aPort,
+                                      in ACString aUsername, in ACString aPassword,
+                                      in unsigned long aFlags,
+                                      in unsigned long aFailoverTimeout,
+                                      in nsIProxyInfo aFailoverProxy);
+
+    /**
      * If the proxy identified by aProxyInfo is unavailable for some reason,
      * this method may be called to access an alternate proxy that may be used
      * instead.  As a side-effect, this method may affect future result values
      * from resolve/asyncResolve as well as from getFailoverForProxy.
      *
      * @param aProxyInfo
      *        The proxy that was unavailable.
      * @param aURI
--- a/netwerk/base/nsIProxyInfo.idl
+++ b/netwerk/base/nsIProxyInfo.idl
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 /**
  * This interface identifies a proxy server.
  */
-[scriptable, uuid(9e557d99-7af0-4895-95b7-e6dba28c9ad9)]
+[scriptable, uuid(63fff172-2564-4138-96c6-3ae7d245fbed)]
 interface nsIProxyInfo : nsISupports
 {
   /**
    * This attribute specifies the hostname of the proxy server.
    */
   readonly attribute AUTF8String host;
 
   /**
@@ -46,16 +46,26 @@ interface nsIProxyInfo : nsISupports
 
   /**
    * This attribute specifies flags that were used by nsIProxyProtocolService when
    * creating this ProxyInfo element. 
    */
   readonly attribute unsigned long resolveFlags;
 
   /**
+   * Specifies a proxy username.
+   */
+  readonly attribute ACString username;
+
+  /**
+   * Specifies a proxy password.
+   */
+  readonly attribute ACString password;
+
+  /**
    * This attribute specifies the failover timeout in seconds for this proxy.
    * If a nsIProxyInfo is reported as failed via nsIProtocolProxyService::
    * getFailoverForProxy, then the failed proxy will not be used again for this
    * many seconds.
    */
   readonly attribute unsigned long failoverTimeout;
 
   /**
--- a/netwerk/base/nsProtocolProxyService.cpp
+++ b/netwerk/base/nsProtocolProxyService.cpp
@@ -1374,16 +1374,33 @@ NS_IMETHODIMP
 nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
                                      const nsACString &aHost,
                                      int32_t aPort,
                                      uint32_t aFlags,
                                      uint32_t aFailoverTimeout,
                                      nsIProxyInfo *aFailoverProxy,
                                      nsIProxyInfo **aResult)
 {
+    return NewProxyInfoWithAuth(aType, aHost, aPort,
+                                EmptyCString(), EmptyCString(),
+                                aFlags, aFailoverTimeout,
+                                aFailoverProxy, aResult);
+}
+
+NS_IMETHODIMP
+nsProtocolProxyService::NewProxyInfoWithAuth(const nsACString &aType,
+                                             const nsACString &aHost,
+                                             int32_t aPort,
+                                             const nsACString &aUsername,
+                                             const nsACString &aPassword,
+                                             uint32_t aFlags,
+                                             uint32_t aFailoverTimeout,
+                                             nsIProxyInfo *aFailoverProxy,
+                                             nsIProxyInfo **aResult)
+{
     static const char *types[] = {
         kProxyType_HTTP,
         kProxyType_HTTPS,
         kProxyType_SOCKS,
         kProxyType_SOCKS4,
         kProxyType_DIRECT
     };
 
@@ -1393,20 +1410,26 @@ nsProtocolProxyService::NewProxyInfo(con
     for (uint32_t i = 0; i < ArrayLength(types); ++i) {
         if (aType.LowerCaseEqualsASCII(types[i])) {
             type = types[i];
             break;
         }
     }
     NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
 
-    if (aPort <= 0)
-        aPort = -1;
+    // We have only implemented username/password for SOCKS proxies.
+    if ((!aUsername.IsEmpty() || !aPassword.IsEmpty()) &&
+        !aType.LowerCaseEqualsASCII(kProxyType_SOCKS) &&
+        !aType.LowerCaseEqualsASCII(kProxyType_SOCKS4)) {
+        return NS_ERROR_NOT_IMPLEMENTED;
+    }
 
-    return NewProxyInfo_Internal(type, aHost, aPort, aFlags, aFailoverTimeout,
+    return NewProxyInfo_Internal(type, aHost, aPort,
+                                 aUsername, aPassword,
+                                 aFlags, aFailoverTimeout,
                                  aFailoverProxy, 0, aResult);
 }
 
 NS_IMETHODIMP
 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo  *aProxy,
                                             nsIURI        *aURI,
                                             nsresult       aStatus,
                                             nsIProxyInfo **aResult)
@@ -1712,35 +1735,42 @@ nsProtocolProxyService::GetProtocolInfo(
     rv = handler->GetDefaultPort(&info->defaultPort);
     return rv;
 }
 
 nsresult
 nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
                                               const nsACString &aHost,
                                               int32_t aPort,
+                                              const nsACString &aUsername,
+                                              const nsACString &aPassword,
                                               uint32_t aFlags,
                                               uint32_t aFailoverTimeout,
                                               nsIProxyInfo *aFailoverProxy,
                                               uint32_t aResolveFlags,
                                               nsIProxyInfo **aResult)
 {
+    if (aPort <= 0)
+        aPort = -1;
+
     nsCOMPtr<nsProxyInfo> failover;
     if (aFailoverProxy) {
         failover = do_QueryInterface(aFailoverProxy);
         NS_ENSURE_ARG(failover);
     }
 
     nsProxyInfo *proxyInfo = new nsProxyInfo();
     if (!proxyInfo)
         return NS_ERROR_OUT_OF_MEMORY;
 
     proxyInfo->mType = aType;
     proxyInfo->mHost = aHost;
     proxyInfo->mPort = aPort;
+    proxyInfo->mUsername = aUsername;
+    proxyInfo->mPassword = aPassword;
     proxyInfo->mFlags = aFlags;
     proxyInfo->mResolveFlags = aResolveFlags;
     proxyInfo->mTimeout = aFailoverTimeout == UINT32_MAX
         ? mFailedProxyTimeout : aFailoverTimeout;
     failover.swap(proxyInfo->mNext);
 
     NS_ADDREF(*aResult = proxyInfo);
     return NS_OK;
@@ -1899,18 +1929,19 @@ nsProtocolProxyService::Resolve_Internal
         else
             type = kProxyType_SOCKS;
         port = mSOCKSProxyPort;
         if (mSOCKSProxyRemoteDNS)
             proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
     }
 
     if (type) {
-        rv = NewProxyInfo_Internal(type, *host, port, proxyFlags,
-                                   UINT32_MAX, nullptr, flags,
+        rv = NewProxyInfo_Internal(type, *host, port,
+                                   EmptyCString(), EmptyCString(),
+                                   proxyFlags, UINT32_MAX, nullptr, flags,
                                    result);
         if (NS_FAILED(rv))
             return rv;
     }
 
     return NS_OK;
 }
 
--- a/netwerk/base/nsProtocolProxyService.h
+++ b/netwerk/base/nsProtocolProxyService.h
@@ -172,30 +172,36 @@ protected:
      * that expects a string literal for the type.
      *
      * @param type
      *        The proxy type.
      * @param host
      *        The proxy host name (UTF-8 ok).
      * @param port
      *        The proxy port number.
+     * @param username
+     *        The username for the proxy (ASCII). May be "", but not null.
+     * @param password
+     *        The password for the proxy (ASCII). May be "", but not null.
      * @param flags
      *        The proxy flags (nsIProxyInfo::flags).
      * @param timeout
      *        The failover timeout for this proxy.
      * @param next
      *        The next proxy to try if this one fails.
      * @param aResolveFlags
      *        The flags passed to resolve (from nsIProtocolProxyService).
      * @param result
      *        The resulting nsIProxyInfo object.
      */
     nsresult NewProxyInfo_Internal(const char *type,
                                                const nsACString &host,
                                                int32_t port,
+                                               const nsACString &username,
+                                               const nsACString &password,
                                                uint32_t flags,
                                                uint32_t timeout,
                                                nsIProxyInfo *next,
                                                uint32_t aResolveFlags,
                                                nsIProxyInfo **result);
 
     /**
      * This method is an internal version of Resolve that does not query PAC.
--- a/netwerk/base/nsProxyInfo.cpp
+++ b/netwerk/base/nsProxyInfo.cpp
@@ -43,16 +43,30 @@ nsProxyInfo::GetFlags(uint32_t *result)
 NS_IMETHODIMP
 nsProxyInfo::GetResolveFlags(uint32_t *result)
 {
   *result = mResolveFlags;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsProxyInfo::GetUsername(nsACString &result)
+{
+  result = mUsername;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsProxyInfo::GetPassword(nsACString &result)
+{
+  result = mPassword;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsProxyInfo::GetFailoverTimeout(uint32_t *result)
 {
   *result = mTimeout;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProxyInfo::GetFailoverProxy(nsIProxyInfo **result)
--- a/netwerk/base/nsProxyInfo.h
+++ b/netwerk/base/nsProxyInfo.h
@@ -30,16 +30,18 @@ public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIPROXYINFO
 
   // Cheap accessors for use within Necko
   const nsCString &Host()  { return mHost; }
   int32_t          Port()  { return mPort; }
   const char      *Type()  { return mType; }
   uint32_t         Flags() { return mFlags; }
+  const nsCString &Username()  { return mUsername; }
+  const nsCString &Password()  { return mPassword; }
 
   bool IsDirect();
   bool IsHTTP();
   bool IsHTTPS();
   bool IsSOCKS();
 
 private:
   friend class nsProtocolProxyService;
@@ -55,16 +57,18 @@ private:
 
   ~nsProxyInfo()
   {
     NS_IF_RELEASE(mNext);
   }
 
   const char  *mType;  // pointer to statically allocated value
   nsCString    mHost;
+  nsCString    mUsername;
+  nsCString    mPassword;
   int32_t      mPort;
   uint32_t     mFlags;
   uint32_t     mResolveFlags;
   uint32_t     mTimeout;
   nsProxyInfo *mNext;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsProxyInfo, NS_PROXYINFO_IID)
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -808,16 +808,17 @@ nsSocketTransport::Init(const char **typ
         mPort = port;
     }
 
     if (proxyInfo) {
         mHttpsProxy = proxyInfo->IsHTTPS();
     }
 
     const char *proxyType = nullptr;
+    mProxyInfo = proxyInfo;
     if (proxyInfo) {
         mProxyPort = proxyInfo->Port();
         mProxyHost = proxyInfo->Host();
         // grab proxy type (looking for "socks" for example)
         proxyType = proxyInfo->Type();
         if (proxyType && (proxyInfo->IsHTTP() ||
                           proxyInfo->IsHTTPS() ||
                           proxyInfo->IsDirect() ||
@@ -1100,18 +1101,17 @@ nsSocketTransport::BuildSocket(PRFileDes
             do_GetService(kSocketProviderServiceCID, &rv);
         if (NS_FAILED(rv)) return rv;
 
         // by setting host to mOriginHost, instead of mHost we send the
         // SocketProvider (e.g. PSM) the origin hostname but can still do DNS
         // on an explicit alternate service host name
         const char *host       = mOriginHost.get();
         int32_t     port       = (int32_t) mOriginPort;
-        const char *proxyHost  = mProxyHost.IsEmpty() ? nullptr : mProxyHost.get();
-        int32_t     proxyPort  = (int32_t) mProxyPort;
+        nsCOMPtr<nsIProxyInfo> proxyInfo = mProxyInfo;
         uint32_t    controlFlags = 0;
 
         uint32_t i;
         for (i=0; i<mTypeCount; ++i) {
             nsCOMPtr<nsISocketProvider> provider;
 
             SOCKET_LOG(("  pushing io layer [%u:%s]\n", i, mTypes[i]));
 
@@ -1135,33 +1135,33 @@ nsSocketTransport::BuildSocket(PRFileDes
             if (i == 0) {
                 // if this is the first type, we'll want the 
                 // service to allocate a new socket
 
                 // when https proxying we want to just connect to the proxy as if
                 // it were the end host (i.e. expect the proxy's cert)
 
                 rv = provider->NewSocket(mNetAddr.raw.family,
-                                         mHttpsProxy ? proxyHost : host,
-                                         mHttpsProxy ? proxyPort : port,
-                                         proxyHost, proxyPort,
+                                         mHttpsProxy ? mProxyHost.get() : host,
+                                         mHttpsProxy ? mProxyPort : port,
+                                         proxyInfo,
                                          controlFlags, &fd,
                                          getter_AddRefs(secinfo));
 
                 if (NS_SUCCEEDED(rv) && !fd) {
                     NS_NOTREACHED("NewSocket succeeded but failed to create a PRFileDesc");
                     rv = NS_ERROR_UNEXPECTED;
                 }
             }
             else {
                 // the socket has already been allocated, 
                 // so we just want the service to add itself
                 // to the stack (such as pushing an io layer)
                 rv = provider->AddToSocket(mNetAddr.raw.family,
-                                           host, port, proxyHost, proxyPort,
+                                           host, port, proxyInfo,
                                            controlFlags, fd,
                                            getter_AddRefs(secinfo));
             }
             // controlFlags = 0; not used below this point...
             if (NS_FAILED(rv))
                 break;
 
             // if the service was ssl or starttls, we want to hold onto the socket info
@@ -1181,18 +1181,17 @@ nsSocketTransport::BuildSocket(PRFileDes
                     secCtrl->SetNotificationCallbacks(callbacks);
                 // remember if socket type is SSL so we can ProxyStartSSL if need be.
                 usingSSL = isSSL;
             }
             else if ((strcmp(mTypes[i], "socks") == 0) ||
                      (strcmp(mTypes[i], "socks4") == 0)) {
                 // since socks is transparent, any layers above
                 // it do not have to worry about proxy stuff
-                proxyHost = nullptr;
-                proxyPort = -1;
+                proxyInfo = nullptr;
                 proxyTransparent = true;
             }
         }
 
         if (NS_FAILED(rv)) {
             SOCKET_LOG(("  error pushing io layer [%u:%s rv=%x]\n", i, mTypes[i], rv));
             if (fd) {
                 PR_Close(fd);
--- a/netwerk/base/nsSocketTransport2.h
+++ b/netwerk/base/nsSocketTransport2.h
@@ -281,16 +281,17 @@ private:
 
     // socket type info:
     char       **mTypes;
     uint32_t     mTypeCount;
     nsCString    mHost;
     nsCString    mProxyHost;
     nsCString    mOriginHost;
     uint16_t     mPort;
+    nsCOMPtr<nsIProxyInfo> mProxyInfo;
     uint16_t     mProxyPort;
     uint16_t     mOriginPort;
     bool mProxyTransparent;
     bool mProxyTransparentResolvesHost;
     bool mHttpsProxy;
     uint32_t     mConnectionFlags;
     
     uint16_t         SocketPort() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyPort : mPort; }
--- a/netwerk/protocol/http/TunnelUtils.cpp
+++ b/netwerk/protocol/http/TunnelUtils.cpp
@@ -73,17 +73,17 @@ TLSFilterTransaction::TLSFilterTransacti
     sLayerMethodsPtr = &sLayerMethods;
   }
 
   mFD = PR_CreateIOLayerStub(sLayerIdentity, &sLayerMethods);
 
   if (provider && mFD) {
     mFD->secret = reinterpret_cast<PRFilePrivate *>(this);
     provider->AddToSocket(PR_AF_INET, aTLSHost, aTLSPort, nullptr,
-                          0, 0, mFD, getter_AddRefs(mSecInfo));
+                          0, mFD, getter_AddRefs(mSecInfo));
   }
 
   if (mTransaction) {
     nsCOMPtr<nsIInterfaceRequestor> callbacks;
     mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
     nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(mSecInfo));
     if (secCtrl) {
       secCtrl->SetNotificationCallbacks(callbacks);
--- a/netwerk/protocol/http/nsHttpConnectionInfo.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.cpp
@@ -11,16 +11,40 @@
 #undef LOG
 #define LOG(args) LOG5(args)
 #undef LOG_ENABLED
 #define LOG_ENABLED() LOG5_ENABLED()
 
 #include "nsHttpConnectionInfo.h"
 #include "mozilla/net/DNS.h"
 #include "prnetdb.h"
+#include "nsICryptoHash.h"
+
+static nsresult
+SHA256(const char* aPlainText, nsAutoCString& aResult)
+{
+  static nsICryptoHash* hasher = nullptr;
+  nsresult rv;
+  if (!hasher) {
+    rv = CallCreateInstance("@mozilla.org/security/hash;1", &hasher);
+    if (NS_FAILED(rv)) {
+      LOG(("nsHttpDigestAuth: no crypto hash!\n"));
+      return rv;
+    }
+  }
+
+  rv = hasher->Init(nsICryptoHash::SHA256);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = hasher->Update((unsigned char*) aPlainText, strlen(aPlainText));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = hasher->Finish(false, aResult);
+  return rv;
+}
 
 namespace mozilla {
 namespace net {
 
 nsHttpConnectionInfo::nsHttpConnectionInfo(const nsACString &originHost,
                                            int32_t originPort,
                                            const nsACString &npnToken,
                                            const nsACString &username,
@@ -146,25 +170,42 @@ void nsHttpConnectionInfo::BuildHashKey(
     // right protocol even if our proxy preferences change).
     //
     // NOTE: for SSL tunnels add the proxy information to the cache key.
     // We cannot use the proxy as the host parameter (as we do for non SSL)
     // because this is a single host tunnel, but we need to include the proxy
     // information so that a change in proxy config will mean this connection
     // is not reused
 
+    // NOTE: Adding the username and the password provides a means to isolate
+    // keep-alive to the URL bar domain as well: If the username is the URL bar
+    // domain, keep-alive connections are not reused by resources bound to
+    // different URL bar domains as the respective hash keys are not matching.
+
     if ((!mUsingHttpProxy && ProxyHost()) ||
         (mUsingHttpProxy && mUsingConnect)) {
         mHashKey.AppendLiteral(" (");
         mHashKey.Append(ProxyType());
         mHashKey.Append(':');
         mHashKey.Append(ProxyHost());
         mHashKey.Append(':');
         mHashKey.AppendInt(ProxyPort());
         mHashKey.Append(')');
+        mHashKey.Append('[');
+        mHashKey.Append(ProxyUsername());
+        mHashKey.Append(':');
+        const char* password = ProxyPassword();
+        if (strlen(password) > 0) {
+            nsAutoCString digestedPassword;
+            nsresult rv = SHA256(password, digestedPassword);
+            if (rv == NS_OK) {
+                mHashKey.Append(digestedPassword);
+            }
+        }
+        mHashKey.Append(']');
     }
 
     if(!mRoutedHost.IsEmpty()) {
         mHashKey.AppendLiteral(" <ROUTE-via ");
         mHashKey.Append(mRoutedHost);
         mHashKey.Append(':');
         mHashKey.AppendInt(mRoutedPort);
         mHashKey.Append('>');
--- a/netwerk/protocol/http/nsHttpConnectionInfo.h
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.h
@@ -79,16 +79,18 @@ public:
     // OK to treat these as an infalible allocation
     nsHttpConnectionInfo* Clone() const;
     void CloneAsDirectRoute(nsHttpConnectionInfo **outParam);
     nsresult CreateWildCard(nsHttpConnectionInfo **outParam);
 
     const char *ProxyHost() const { return mProxyInfo ? mProxyInfo->Host().get() : nullptr; }
     int32_t     ProxyPort() const { return mProxyInfo ? mProxyInfo->Port() : -1; }
     const char *ProxyType() const { return mProxyInfo ? mProxyInfo->Type() : nullptr; }
+    const char *ProxyUsername() const { return mProxyInfo ? mProxyInfo->Username().get() : nullptr; }
+    const char *ProxyPassword() const { return mProxyInfo ? mProxyInfo->Password().get() : nullptr; }
 
     // Compare this connection info to another...
     // Two connections are 'equal' if they end up talking the same
     // protocol to the same server. This is needed to properly manage
     // persistent connections to proxies
     // Note that we don't care about transparent proxies -
     // it doesn't matter if we're talking via socks or not, since
     // a request will end up at the same host.
--- a/netwerk/socket/nsISocketProvider.idl
+++ b/netwerk/socket/nsISocketProvider.idl
@@ -1,21 +1,22 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
+interface nsIProxyInfo;
 [ptr] native PRFileDescStar(struct PRFileDesc);
 
 /**
  * nsISocketProvider
  */
-[scriptable, uuid(00b3df92-e830-11d8-d48e-0004e22243f8)]
+[scriptable, uuid(508d5469-9e1e-4a08-b5b0-7cfebba1e51a)]
 interface nsISocketProvider : nsISupports
 {
     /**
      * newSocket
      *
      * @param aFamily
      *        The address family for this socket (PR_AF_INET or PR_AF_INET6).
      * @param aHost
@@ -33,18 +34,17 @@ interface nsISocketProvider : nsISupport
      * @param aSecurityInfo
      *        Any security info that should be associated with aFileDesc.  This
      *        object typically implements nsITransportSecurityInfo.
      */
     [noscript]
     void newSocket(in long            aFamily,
                    in string          aHost, 
                    in long            aPort,
-                   in string          aProxyHost,
-                   in long            aProxyPort,
+                   in nsIProxyInfo    aProxy,
                    in unsigned long   aFlags,
                    out PRFileDescStar aFileDesc, 
                    out nsISupports    aSecurityInfo);
 
     /**
      * addToSocket
      *
      * This function is called to allow the socket provider to layer a
@@ -53,18 +53,17 @@ interface nsISocketProvider : nsISupport
      *
      * Parameters are the same as newSocket with the exception of aFileDesc,
      * which is an in-param instead.
      */
     [noscript]
     void addToSocket(in long           aFamily,
                      in string         aHost, 
                      in long           aPort,
-                     in string         aProxyHost,
-                     in long           aProxyPort,
+                     in nsIProxyInfo   aProxy,
                      in unsigned long  aFlags,
                      in PRFileDescStar aFileDesc, 
                      out nsISupports   aSecurityInfo);
 
     /**
      * PROXY_RESOLVES_HOST
      *
      * This flag is set if the proxy is to perform hostname resolution instead
--- a/netwerk/socket/nsSOCKSIOLayer.cpp
+++ b/netwerk/socket/nsSOCKSIOLayer.cpp
@@ -42,39 +42,42 @@ class nsSOCKSSocketInfo : public nsISOCK
         SOCKS_INITIAL,
         SOCKS_DNS_IN_PROGRESS,
         SOCKS_DNS_COMPLETE,
         SOCKS_CONNECTING_TO_PROXY,
         SOCKS4_WRITE_CONNECT_REQUEST,
         SOCKS4_READ_CONNECT_RESPONSE,
         SOCKS5_WRITE_AUTH_REQUEST,
         SOCKS5_READ_AUTH_RESPONSE,
+        SOCKS5_WRITE_USERNAME_REQUEST,
+        SOCKS5_READ_USERNAME_RESPONSE,
         SOCKS5_WRITE_CONNECT_REQUEST,
         SOCKS5_READ_CONNECT_RESPONSE_TOP,
         SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
         SOCKS_CONNECTED,
         SOCKS_FAILED
     };
 
-    // A buffer of 265 bytes should be enough for any request and response
+    // A buffer of 520 bytes should be enough for any request and response
     // in case of SOCKS4 as well as SOCKS5
-    static const uint32_t BUFFER_SIZE = 265;
+    static const uint32_t BUFFER_SIZE = 520;
     static const uint32_t MAX_HOSTNAME_LEN = 255;
+    static const uint32_t MAX_USERNAME_LEN = 255;
+    static const uint32_t MAX_PASSWORD_LEN = 255;
 
 public:
     nsSOCKSSocketInfo();
 
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSISOCKSSOCKETINFO
     NS_DECL_NSIDNSLISTENER
 
     void Init(int32_t version,
               int32_t family,
-              const char *proxyHost,
-              int32_t proxyPort,
+              nsIProxyInfo *proxy,
               const char *destinationHost,
               uint32_t flags);
 
     void SetConnectTimeout(PRIntervalTime to);
     PRStatus DoHandshake(PRFileDesc *fd, int16_t oflags = -1);
     int16_t GetPollFlags() const;
     bool IsConnected() const { return mState == SOCKS_CONNECTED; }
     void ForgetFD() { mFD = nullptr; }
@@ -86,16 +89,18 @@ private:
     PRStatus StartDNS(PRFileDesc *fd);
     PRStatus ConnectToProxy(PRFileDesc *fd);
     void FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy);
     PRStatus ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags);
     PRStatus WriteV4ConnectRequest();
     PRStatus ReadV4ConnectResponse();
     PRStatus WriteV5AuthRequest();
     PRStatus ReadV5AuthResponse();
+    PRStatus WriteV5UsernameRequest();
+    PRStatus ReadV5UsernameResponse();
     PRStatus WriteV5ConnectRequest();
     PRStatus ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len);
     PRStatus ReadV5ConnectResponseTop();
     PRStatus ReadV5ConnectResponseBottom();
 
     uint8_t ReadUint8();
     uint16_t ReadUint16();
     uint32_t ReadUint32();
@@ -114,34 +119,33 @@ private:
     uint32_t  mReadOffset;
     uint32_t  mAmountToRead;
     nsCOMPtr<nsIDNSRecord>  mDnsRec;
     nsCOMPtr<nsICancelable> mLookup;
     nsresult                mLookupStatus;
     PRFileDesc             *mFD;
 
     nsCString mDestinationHost;
-    nsCString mProxyHost;
-    int32_t   mProxyPort;
+    nsCOMPtr<nsIProxyInfo> mProxy;
     int32_t   mVersion;   // SOCKS version 4 or 5
     int32_t   mDestinationFamily;
     uint32_t  mFlags;
     NetAddr   mInternalProxyAddr;
     NetAddr   mExternalProxyAddr;
     NetAddr   mDestinationAddr;
     PRIntervalTime mTimeout;
+    nsCString mProxyUsername; // Cache, from mProxy
 };
 
 nsSOCKSSocketInfo::nsSOCKSSocketInfo()
     : mState(SOCKS_INITIAL)
     , mDataIoPtr(nullptr)
     , mDataLength(0)
     , mReadOffset(0)
     , mAmountToRead(0)
-    , mProxyPort(-1)
     , mVersion(-1)
     , mDestinationFamily(AF_INET)
     , mFlags(0)
     , mTimeout(PR_INTERVAL_NO_TIMEOUT)
 {
     mData = new uint8_t[BUFFER_SIZE];
 
     mInternalProxyAddr.raw.family = AF_INET;
@@ -262,24 +266,24 @@ private:
   }
 
   uint8_t* mBuf;
   size_t mLength;
 };
 
 
 void
-nsSOCKSSocketInfo::Init(int32_t version, int32_t family, const char *proxyHost, int32_t proxyPort, const char *host, uint32_t flags)
+nsSOCKSSocketInfo::Init(int32_t version, int32_t family, nsIProxyInfo *proxy, const char *host, uint32_t flags)
 {
     mVersion         = version;
     mDestinationFamily = family;
-    mProxyHost       = proxyHost;
-    mProxyPort       = proxyPort;
+    mProxy           = proxy;
     mDestinationHost = host;
     mFlags           = flags;
+    mProxy->GetUsername(mProxyUsername); // cache
 }
 
 NS_IMPL_ISUPPORTS(nsSOCKSSocketInfo, nsISOCKSSocketInfo, nsIDNSListener)
 
 NS_IMETHODIMP 
 nsSOCKSSocketInfo::GetExternalProxyAddr(NetAddr * *aExternalProxyAddr)
 {
     memcpy(*aExternalProxyAddr, &mExternalProxyAddr, sizeof(NetAddr));
@@ -356,24 +360,27 @@ nsSOCKSSocketInfo::StartDNS(PRFileDesc *
 {
     MOZ_ASSERT(!mDnsRec && mState == SOCKS_INITIAL,
                "Must be in initial state to make DNS Lookup");
 
     nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
     if (!dns)
         return PR_FAILURE;
 
+    nsCString proxyHost;
+    mProxy->GetHost(proxyHost);
+
     mFD  = fd;
-    nsresult rv = dns->AsyncResolve(mProxyHost, 0, this,
+    nsresult rv = dns->AsyncResolve(proxyHost, 0, this,
                                     NS_GetCurrentThread(),
                                     getter_AddRefs(mLookup));
 
     if (NS_FAILED(rv)) {
         LOGERROR(("socks: DNS lookup for SOCKS proxy %s failed",
-                  mProxyHost.get()));
+                  proxyHost.get()));
         return PR_FAILURE;
     }
     mState = SOCKS_DNS_IN_PROGRESS;
     PR_SetError(PR_IN_PROGRESS_ERROR, 0);
     return PR_FAILURE;
 }
 
 NS_IMETHODIMP
@@ -408,26 +415,31 @@ nsSOCKSSocketInfo::ConnectToProxy(PRFile
     }
 
     // Try socks5 if the destination addrress is IPv6
     if (mVersion == 4 &&
         mDestinationAddr.raw.family == AF_INET6) {
         mVersion = 5;
     }
 
+    int32_t proxyPort;
+    mProxy->GetPort(&proxyPort);
+
     int32_t addresses = 0;
     do {
         if (addresses++)
-            mDnsRec->ReportUnusable(mProxyPort);
+            mDnsRec->ReportUnusable(proxyPort);
         
-        rv = mDnsRec->GetNextAddr(mProxyPort, &mInternalProxyAddr);
+        rv = mDnsRec->GetNextAddr(proxyPort, &mInternalProxyAddr);
         // No more addresses to try? If so, we'll need to bail
         if (NS_FAILED(rv)) {
+            nsCString proxyHost;
+            mProxy->GetHost(proxyHost);
             LOGERROR(("socks: unable to connect to SOCKS proxy, %s",
-                     mProxyHost.get()));
+                     proxyHost.get()));
             return PR_FAILURE;
         }
 
         if (MOZ_LOG_TEST(gSOCKSLog, LogLevel::Debug)) {
           char buf[kIPv6CStrBufSize];
           NetAddrToString(&mInternalProxyAddr, buf, sizeof(buf));
           LOGDEBUG(("socks: trying proxy server, %s:%hu",
                    buf, ntohs(mInternalProxyAddr.inet.port)));
@@ -534,16 +546,22 @@ nsSOCKSSocketInfo::ContinueConnectingToP
     if (mVersion == 4)
         return WriteV4ConnectRequest();
     return WriteV5AuthRequest();
 }
 
 PRStatus
 nsSOCKSSocketInfo::WriteV4ConnectRequest()
 {
+    if (mProxyUsername.Length() > MAX_USERNAME_LEN) {
+        LOGERROR(("socks username is too long"));
+        HandshakeFinished(PR_UNKNOWN_ERROR);
+        return PR_FAILURE;
+    }
+
     NetAddr *addr = &mDestinationAddr;
     int32_t proxy_resolve;
 
     MOZ_ASSERT(mState == SOCKS_CONNECTING_TO_PROXY,
                "Invalid state!");
     
     proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
 
@@ -563,28 +581,32 @@ nsSOCKSSocketInfo::WriteV4ConnectRequest
     // use a buffer with no further writes allowed.
     Buffer<0> buf3;
     if (proxy_resolve) {
         // Add the full name, null-terminated, to the request
         // according to SOCKS 4a. A fake IP address, with the first
         // four bytes set to 0 and the last byte set to something other
         // than 0, is used to notify the proxy that this is a SOCKS 4a
         // request. This request type works for Tor and perhaps others.
+        // Passwords not supported by V4.
         auto buf2 = buf.WriteUint32(htonl(0x00000001)) // Fake IP
-                       .WriteUint8(0x00) // Send an emtpy username
+                       .WriteString<MAX_USERNAME_LEN>(mProxyUsername)
+                       .WriteUint8(0x00) // Null-terminate username
                        .WriteString<MAX_HOSTNAME_LEN>(mDestinationHost); // Hostname
         if (!buf2) {
             LOGERROR(("socks4: destination host name is too long!"));
             HandshakeFinished(PR_BAD_ADDRESS_ERROR);
             return PR_FAILURE;
         }
         buf3 = buf2.WriteUint8(0x00);
     } else if (addr->raw.family == AF_INET) {
+        // Passwords not supported by V4.
         buf3 = buf.WriteNetAddr(addr) // Add the IPv4 address
-                  .WriteUint8(0x00); // Send an emtpy username
+                  .WriteString<MAX_USERNAME_LEN>(mProxyUsername)
+                  .WriteUint8(0x00); // Null-terminate username
     } else {
         LOGERROR(("socks: SOCKS 4 can only handle IPv4 addresses!"));
         HandshakeFinished(PR_BAD_ADDRESS_ERROR);
         return PR_FAILURE;
     }
 
     mDataLength = buf3.Written();
     return PR_SUCCESS;
@@ -618,24 +640,26 @@ nsSOCKSSocketInfo::ReadV4ConnectResponse
     return PR_FAILURE;
 }
 
 PRStatus
 nsSOCKSSocketInfo::WriteV5AuthRequest()
 {
     MOZ_ASSERT(mVersion == 5, "SOCKS version must be 5!");
 
+    mDataLength = 0;
     mState = SOCKS5_WRITE_AUTH_REQUEST;
 
     // Send an initial SOCKS 5 greeting
     LOGDEBUG(("socks5: sending auth methods"));
     mDataLength = Buffer<BUFFER_SIZE>(mData)
                   .WriteUint8(0x05) // version -- 5
-                  .WriteUint8(0x01) // # auth methods -- 1
-                  .WriteUint8(0x00) // we don't support authentication
+                  .WriteUint8(0x01) // # of auth methods -- 1
+                  // Use authenticate iff we have a proxy username.
+                  .WriteUint8(mProxyUsername.IsEmpty() ? 0x00 : 0x02)
                   .Written();
 
     return PR_SUCCESS;
 }
 
 PRStatus
 nsSOCKSSocketInfo::ReadV5AuthResponse()
 {
@@ -648,22 +672,91 @@ nsSOCKSSocketInfo::ReadV5AuthResponse()
 
     // Check version number
     if (ReadUint8() != 0x05) {
         LOGERROR(("socks5: unexpected version in the reply"));
         HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
         return PR_FAILURE;
     }
 
-    // Make sure our authentication choice was accepted
-    if (ReadUint8() != 0x00) {
+    // Make sure our authentication choice was accepted,
+    // and continue accordingly
+    uint8_t authMethod = ReadUint8();
+    if (mProxyUsername.IsEmpty() && authMethod == 0x00) { // no auth
+        LOGDEBUG(("socks5: server allows connection without authentication"));
+        return WriteV5ConnectRequest();
+    } else if (!mProxyUsername.IsEmpty() && authMethod == 0x02) { // username/pw
+        LOGDEBUG(("socks5: auth method accepted by server"));
+        return WriteV5UsernameRequest();
+    } else { // 0xFF signals error
         LOGERROR(("socks5: server did not accept our authentication method"));
         HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
         return PR_FAILURE;
     }
+}
+
+PRStatus
+nsSOCKSSocketInfo::WriteV5UsernameRequest()
+{
+    MOZ_ASSERT(mVersion == 5, "SOCKS version must be 5!");
+
+    if (mProxyUsername.Length() > MAX_USERNAME_LEN) {
+        LOGERROR(("socks username is too long"));
+        HandshakeFinished(PR_UNKNOWN_ERROR);
+        return PR_FAILURE;
+    }
+
+    nsCString password;
+    mProxy->GetPassword(password);
+    if (password.Length() > MAX_PASSWORD_LEN) {
+        LOGERROR(("socks password is too long"));
+        HandshakeFinished(PR_UNKNOWN_ERROR);
+        return PR_FAILURE;
+    }
+
+    mDataLength = 0;
+    mState = SOCKS5_WRITE_USERNAME_REQUEST;
+
+    // RFC 1929 Username/password auth for SOCKS 5
+    LOGDEBUG(("socks5: sending username and password"));
+    mDataLength = Buffer<BUFFER_SIZE>(mData)
+                  .WriteUint8(0x01) // version 1 (not 5)
+                  .WriteUint8(mProxyUsername.Length()) // username length
+                  .WriteString<MAX_USERNAME_LEN>(mProxyUsername) // username
+                  .WriteUint8(password.Length()) // password length
+                  .WriteString<MAX_PASSWORD_LEN>(password) // password. WARNING: Sent unencrypted!
+                  .Written();
+
+    return PR_SUCCESS;
+}
+
+PRStatus
+nsSOCKSSocketInfo::ReadV5UsernameResponse()
+{
+    MOZ_ASSERT(mState == SOCKS5_READ_USERNAME_RESPONSE,
+                      "Handling SOCKS 5 username/password reply in wrong state!");
+
+    MOZ_ASSERT(mDataLength == 2,
+               "SOCKS 5 username reply must be 2 bytes");
+
+    // Check version number, must be 1 (not 5)
+    if (ReadUint8() != 0x01) {
+        LOGERROR(("socks5: unexpected version in the reply"));
+        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
+        return PR_FAILURE;
+    }
+
+    // Check whether username/password were accepted
+    if (ReadUint8() != 0x00) { // 0 = success
+        LOGERROR(("socks5: username/password not accepted"));
+        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
+        return PR_FAILURE;
+    }
+
+    LOGDEBUG(("socks5: username/password accepted by server"));
 
     return WriteV5ConnectRequest();
 }
 
 PRStatus
 nsSOCKSSocketInfo::WriteV5ConnectRequest()
 {
     // Send SOCKS 5 connect request
@@ -902,16 +995,26 @@ nsSOCKSSocketInfo::DoHandshake(PRFileDes
                 return PR_FAILURE;
             WantRead(2);
             mState = SOCKS5_READ_AUTH_RESPONSE;
             return PR_SUCCESS;
         case SOCKS5_READ_AUTH_RESPONSE:
             if (ReadFromSocket(fd) != PR_SUCCESS)
                 return PR_FAILURE;
             return ReadV5AuthResponse();
+        case SOCKS5_WRITE_USERNAME_REQUEST:
+            if (WriteToSocket(fd) != PR_SUCCESS)
+                return PR_FAILURE;
+            WantRead(2);
+            mState = SOCKS5_READ_USERNAME_RESPONSE;
+            return PR_SUCCESS;
+        case SOCKS5_READ_USERNAME_RESPONSE:
+            if (ReadFromSocket(fd) != PR_SUCCESS)
+                return PR_FAILURE;
+            return ReadV5UsernameResponse();
         case SOCKS5_WRITE_CONNECT_REQUEST:
             if (WriteToSocket(fd) != PR_SUCCESS)
                 return PR_FAILURE;
 
             // The SOCKS 5 response to the connection request is variable
             // length. First, we'll read enough to tell how long the response
             // is, and will read the rest later.
             WantRead(5);
@@ -946,20 +1049,22 @@ nsSOCKSSocketInfo::GetPollFlags() const
 {
     switch (mState) {
         case SOCKS_DNS_IN_PROGRESS:
         case SOCKS_DNS_COMPLETE:
         case SOCKS_CONNECTING_TO_PROXY:
             return PR_POLL_EXCEPT | PR_POLL_WRITE;
         case SOCKS4_WRITE_CONNECT_REQUEST:
         case SOCKS5_WRITE_AUTH_REQUEST:
+        case SOCKS5_WRITE_USERNAME_REQUEST:
         case SOCKS5_WRITE_CONNECT_REQUEST:
             return PR_POLL_WRITE;
         case SOCKS4_READ_CONNECT_RESPONSE:
         case SOCKS5_READ_AUTH_RESPONSE:
+        case SOCKS5_READ_USERNAME_RESPONSE:
         case SOCKS5_READ_CONNECT_RESPONSE_TOP:
         case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
             return PR_POLL_READ;
         default:
             break;
     }
 
     return 0;
@@ -1264,18 +1369,17 @@ nsSOCKSIOLayerListen(PRFileDesc *fd, int
     return fd->lower->methods->listen(fd->lower, backlog);
 }
 
 // add SOCKS IO layer to an existing socket
 nsresult
 nsSOCKSIOLayerAddToSocket(int32_t family,
                           const char *host, 
                           int32_t port,
-                          const char *proxyHost,
-                          int32_t proxyPort,
+                          nsIProxyInfo *proxy,
                           int32_t socksVersion,
                           uint32_t flags,
                           PRFileDesc *fd, 
                           nsISupports** info)
 {
     NS_ENSURE_TRUE((socksVersion == 4) || (socksVersion == 5), NS_ERROR_NOT_INITIALIZED);
 
 
@@ -1327,17 +1431,17 @@ nsSOCKSIOLayerAddToSocket(int32_t family
     {
         // clean up IOLayerStub
         LOGERROR(("Failed to create nsSOCKSSocketInfo()."));
         PR_DELETE(layer);
         return NS_ERROR_FAILURE;
     }
 
     NS_ADDREF(infoObject);
-    infoObject->Init(socksVersion, family, proxyHost, proxyPort, host, flags);
+    infoObject->Init(socksVersion, family, proxy, host, flags);
     layer->secret = (PRFilePrivate*) infoObject;
     rv = PR_PushIOLayer(fd, PR_GetLayersIdentity(fd), layer);
 
     if (rv == PR_FAILURE) {
         LOGERROR(("PR_PushIOLayer() failed. rv = %x.", rv));
         NS_RELEASE(infoObject);
         PR_DELETE(layer);
         return NS_ERROR_FAILURE;
--- a/netwerk/socket/nsSOCKSIOLayer.h
+++ b/netwerk/socket/nsSOCKSIOLayer.h
@@ -4,20 +4,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsSOCKSIOLayer_h__
 #define nsSOCKSIOLayer_h__
 
 #include "prio.h"
 #include "nscore.h"
+#include "nsIProxyInfo.h"
 
 nsresult nsSOCKSIOLayerAddToSocket(int32_t       family,
                                    const char   *host, 
                                    int32_t       port,
-                                   const char   *proxyHost,
-                                   int32_t       proxyPort,
+                                   nsIProxyInfo *proxyInfo,
                                    int32_t       socksVersion,
                                    uint32_t      flags,
                                    PRFileDesc   *fd, 
                                    nsISupports **info);
 
 #endif /* nsSOCKSIOLayer_h__ */
--- a/netwerk/socket/nsSOCKSSocketProvider.cpp
+++ b/netwerk/socket/nsSOCKSSocketProvider.cpp
@@ -39,60 +39,56 @@ nsSOCKSSocketProvider::CreateV5(nsISuppo
         rv = inst->QueryInterface(aIID, aResult); 
     return rv;
 }
 
 NS_IMETHODIMP
 nsSOCKSSocketProvider::NewSocket(int32_t family,
                                  const char *host, 
                                  int32_t port,
-                                 const char *proxyHost,
-                                 int32_t proxyPort,
+                                 nsIProxyInfo *proxy,
                                  uint32_t flags,
                                  PRFileDesc **result,
                                  nsISupports **socksInfo)
 {
     PRFileDesc *sock;
     
     sock = PR_OpenTCPSocket(family);
     if (!sock)
         return NS_ERROR_OUT_OF_MEMORY;
 
     nsresult rv = nsSOCKSIOLayerAddToSocket(family,
                                             host, 
                                             port,
-                                            proxyHost,
-                                            proxyPort,
+                                            proxy,
                                             mVersion,
                                             flags,
                                             sock, 
                                             socksInfo);
     if (NS_SUCCEEDED(rv)) {
         *result = sock;
         return NS_OK;
     }
 
     return NS_ERROR_SOCKET_CREATE_FAILED;
 }
 
 NS_IMETHODIMP
 nsSOCKSSocketProvider::AddToSocket(int32_t family,
                                    const char *host,
                                    int32_t port,
-                                   const char *proxyHost,
-                                   int32_t proxyPort,
+                                   nsIProxyInfo *proxy,
                                    uint32_t flags,
                                    PRFileDesc *sock,
                                    nsISupports **socksInfo)
 {
     nsresult rv = nsSOCKSIOLayerAddToSocket(family,
                                             host, 
                                             port,
-                                            proxyHost,
-                                            proxyPort,
+                                            proxy,
                                             mVersion,
                                             flags,
                                             sock, 
                                             socksInfo);
     
     if (NS_FAILED(rv))
         rv = NS_ERROR_SOCKET_CREATE_FAILED;
     return rv;
--- a/netwerk/socket/nsUDPSocketProvider.cpp
+++ b/netwerk/socket/nsUDPSocketProvider.cpp
@@ -11,18 +11,17 @@ NS_IMPL_ISUPPORTS(nsUDPSocketProvider, n
 nsUDPSocketProvider::~nsUDPSocketProvider()
 {
 }
 
 NS_IMETHODIMP 
 nsUDPSocketProvider::NewSocket(int32_t aFamily,
                                const char *aHost, 
                                int32_t aPort, 
-                               const char *aProxyHost, 
-                               int32_t aProxyPort,
+                               nsIProxyInfo *aProxy,
                                uint32_t aFlags,
                                PRFileDesc * *aFileDesc, 
                                nsISupports **aSecurityInfo)
 {
     NS_ENSURE_ARG_POINTER(aFileDesc);
   
     PRFileDesc* udpFD = PR_OpenUDPSocket(aFamily);
     if (!udpFD)
@@ -31,18 +30,17 @@ nsUDPSocketProvider::NewSocket(int32_t a
     *aFileDesc = udpFD;
     return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsUDPSocketProvider::AddToSocket(int32_t aFamily,
                                  const char *aHost,
                                  int32_t aPort,
-                                 const char *aProxyHost,
-                                 int32_t aProxyPort,
+                                 nsIProxyInfo *aProxy,
                                  uint32_t aFlags,
                                  struct PRFileDesc * aFileDesc,
                                  nsISupports **aSecurityInfo)
 {
     // does not make sense to strap a UDP socket onto an existing socket
     NS_NOTREACHED("Cannot layer UDP socket on an existing socket");
     return NS_ERROR_UNEXPECTED;
 }
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -1812,28 +1812,27 @@ nsSSLIOLayerHelpers::treatUnsafeNegotiat
   MutexAutoLock lock(mutex);
   return mTreatUnsafeNegotiationAsBroken;
 }
 
 nsresult
 nsSSLIOLayerNewSocket(int32_t family,
                       const char* host,
                       int32_t port,
-                      const char* proxyHost,
-                      int32_t proxyPort,
+                      nsIProxyInfo *proxy,
                       PRFileDesc** fd,
                       nsISupports** info,
                       bool forSTARTTLS,
                       uint32_t flags)
 {
 
   PRFileDesc* sock = PR_OpenTCPSocket(family);
   if (!sock) return NS_ERROR_OUT_OF_MEMORY;
 
-  nsresult rv = nsSSLIOLayerAddToSocket(family, host, port, proxyHost, proxyPort,
+  nsresult rv = nsSSLIOLayerAddToSocket(family, host, port, proxy,
                                         sock, info, forSTARTTLS, flags);
   if (NS_FAILED(rv)) {
     PR_Close(sock);
     return rv;
   }
 
   *fd = sock;
   return NS_OK;
@@ -2508,21 +2507,21 @@ loser:
   if (sslSock) {
     PR_Close(sslSock);
   }
   return nullptr;
 }
 
 static nsresult
 nsSSLIOLayerSetOptions(PRFileDesc* fd, bool forSTARTTLS,
-                       const char* proxyHost, const char* host, int32_t port,
+                       bool haveProxy, const char* host, int32_t port,
                        nsNSSSocketInfo* infoObject)
 {
   nsNSSShutDownPreventionLock locker;
-  if (forSTARTTLS || proxyHost) {
+  if (forSTARTTLS || haveProxy) {
     if (SECSuccess != SSL_OptionSet(fd, SSL_SECURITY, false)) {
       return NS_ERROR_FAILURE;
     }
   }
 
   SSLVersionRange range;
   if (SSL_VersionRangeGet(fd, &range) != SECSuccess) {
     return NS_ERROR_FAILURE;
@@ -2589,18 +2588,17 @@ nsSSLIOLayerSetOptions(PRFileDesc* fd, b
 
   return NS_OK;
 }
 
 nsresult
 nsSSLIOLayerAddToSocket(int32_t family,
                         const char* host,
                         int32_t port,
-                        const char* proxyHost,
-                        int32_t proxyPort,
+                        nsIProxyInfo* proxy,
                         PRFileDesc* fd,
                         nsISupports** info,
                         bool forSTARTTLS,
                         uint32_t providerFlags)
 {
   nsNSSShutDownPreventionLock locker;
   PRFileDesc* layer = nullptr;
   PRFileDesc* plaintextLayer = nullptr;
@@ -2612,16 +2610,23 @@ nsSSLIOLayerAddToSocket(int32_t family,
   nsNSSSocketInfo* infoObject = new nsNSSSocketInfo(*sharedState, providerFlags);
   if (!infoObject) return NS_ERROR_FAILURE;
 
   NS_ADDREF(infoObject);
   infoObject->SetForSTARTTLS(forSTARTTLS);
   infoObject->SetHostName(host);
   infoObject->SetPort(port);
 
+  bool haveProxy = false;
+  if (proxy) {
+    nsCString proxyHost;
+    proxy->GetHost(proxyHost);
+    haveProxy = !proxyHost.IsEmpty();
+  }
+
   // A plaintext observer shim is inserted so we can observe some protocol
   // details without modifying nss
   plaintextLayer = PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity,
                                         &nsSSLIOLayerHelpers::nsSSLPlaintextLayerMethods);
   if (plaintextLayer) {
     plaintextLayer->secret = (PRFilePrivate*) infoObject;
     stat = PR_PushIOLayer(fd, PR_TOP_IO_LAYER, plaintextLayer);
     if (stat == PR_FAILURE) {
@@ -2633,17 +2638,17 @@ nsSSLIOLayerAddToSocket(int32_t family,
   PRFileDesc* sslSock = nsSSLIOLayerImportFD(fd, infoObject, host);
   if (!sslSock) {
     NS_ASSERTION(false, "NSS: Error importing socket");
     goto loser;
   }
 
   infoObject->SetFileDescPtr(sslSock);
 
-  rv = nsSSLIOLayerSetOptions(sslSock, forSTARTTLS, proxyHost, host, port,
+  rv = nsSSLIOLayerSetOptions(sslSock, forSTARTTLS, haveProxy, host, port,
                               infoObject);
 
   if (NS_FAILED(rv))
     goto loser;
 
   // Now, layer ourselves on top of the SSL socket...
   layer = PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLIOLayerIdentity,
                                &nsSSLIOLayerHelpers::nsSSLIOLayerMethods);
@@ -2656,17 +2661,17 @@ nsSSLIOLayerAddToSocket(int32_t family,
   if (stat == PR_FAILURE) {
     goto loser;
   }
 
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] Socket set up\n", (void*) sslSock));
   infoObject->QueryInterface(NS_GET_IID(nsISupports), (void**) (info));
 
   // We are going use a clear connection first //
-  if (forSTARTTLS || proxyHost) {
+  if (forSTARTTLS || haveProxy) {
     infoObject->SetHandshakeNotPending();
   }
 
   infoObject->SharedState().NoteSocketCreated();
 
   return NS_OK;
  loser:
   NS_IF_RELEASE(infoObject);
--- a/security/manager/ssl/nsNSSIOLayer.h
+++ b/security/manager/ssl/nsNSSIOLayer.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _NSNSSIOLAYER_H
 #define _NSNSSIOLAYER_H
 
 #include "TransportSecurityInfo.h"
 #include "nsISSLSocketControl.h"
 #include "nsIClientAuthDialogs.h"
+#include "nsIProxyInfo.h"
 #include "nsNSSCertificate.h"
 #include "nsDataHashtable.h"
 #include "nsTHashtable.h"
 #include "mozilla/TimeStamp.h"
 #include "sslt.h"
 
 namespace mozilla {
 namespace psm {
@@ -237,28 +238,26 @@ public:
 private:
   mozilla::Mutex mutex;
   nsCOMPtr<nsIObserver> mPrefObserver;
 };
 
 nsresult nsSSLIOLayerNewSocket(int32_t family,
                                const char* host,
                                int32_t port,
-                               const char* proxyHost,
-                               int32_t proxyPort,
+                               nsIProxyInfo *proxy,
                                PRFileDesc** fd,
                                nsISupports** securityInfo,
                                bool forSTARTTLS,
                                uint32_t flags);
 
 nsresult nsSSLIOLayerAddToSocket(int32_t family,
                                  const char* host,
                                  int32_t port,
-                                 const char* proxyHost,
-                                 int32_t proxyPort,
+                                 nsIProxyInfo *proxy,
                                  PRFileDesc* fd,
                                  nsISupports** securityInfo,
                                  bool forSTARTTLS,
                                  uint32_t flags);
 
 nsresult nsSSLIOLayerFreeTLSIntolerantSites();
 nsresult displayUnknownCertErrorAlert(nsNSSSocketInfo* infoObject, int error);
 
--- a/security/manager/ssl/nsSSLSocketProvider.cpp
+++ b/security/manager/ssl/nsSSLSocketProvider.cpp
@@ -17,49 +17,45 @@ nsSSLSocketProvider::~nsSSLSocketProvide
 }
 
 NS_IMPL_ISUPPORTS(nsSSLSocketProvider, nsISocketProvider)
 
 NS_IMETHODIMP
 nsSSLSocketProvider::NewSocket(int32_t family,
                                const char *host,
                                int32_t port,
-                               const char *proxyHost,
-                               int32_t proxyPort,
+                               nsIProxyInfo *proxy,
                                uint32_t flags,
                                PRFileDesc **_result,
                                nsISupports **securityInfo)
 {
   nsresult rv = nsSSLIOLayerNewSocket(family,
                                       host,
                                       port,
-                                      proxyHost,
-                                      proxyPort,
+                                      proxy,
                                       _result,
                                       securityInfo,
                                       false,
                                       flags);
   return (NS_FAILED(rv)) ? NS_ERROR_SOCKET_CREATE_FAILED : NS_OK;
 }
 
 // Add the SSL IO layer to an existing socket
 NS_IMETHODIMP
 nsSSLSocketProvider::AddToSocket(int32_t family,
                                  const char *host,
                                  int32_t port,
-                                 const char *proxyHost,
-                                 int32_t proxyPort,
+                                 nsIProxyInfo *proxy,
                                  uint32_t flags,
                                  PRFileDesc *aSocket,
                                  nsISupports **securityInfo)
 {
   nsresult rv = nsSSLIOLayerAddToSocket(family,
                                         host,
                                         port,
-                                        proxyHost,
-                                        proxyPort,
+                                        proxy,
                                         aSocket,
                                         securityInfo,
                                         false,
                                         flags);
   
   return (NS_FAILED(rv)) ? NS_ERROR_SOCKET_CREATE_FAILED : NS_OK;
 }
--- a/security/manager/ssl/nsTLSSocketProvider.cpp
+++ b/security/manager/ssl/nsTLSSocketProvider.cpp
@@ -17,50 +17,46 @@ nsTLSSocketProvider::~nsTLSSocketProvide
 }
 
 NS_IMPL_ISUPPORTS(nsTLSSocketProvider, nsISocketProvider)
 
 NS_IMETHODIMP
 nsTLSSocketProvider::NewSocket(int32_t family,
                                const char *host,
                                int32_t port,
-                               const char *proxyHost,
-                               int32_t proxyPort,
+                               nsIProxyInfo *proxy,
                                uint32_t flags,
                                PRFileDesc **_result,
                                nsISupports **securityInfo)
 {
   nsresult rv = nsSSLIOLayerNewSocket(family,
                                       host,
                                       port,
-                                      proxyHost,
-                                      proxyPort,
+                                      proxy,
                                       _result,
                                       securityInfo,
                                       true,
                                       flags);
   
   return (NS_FAILED(rv)) ? NS_ERROR_SOCKET_CREATE_FAILED : NS_OK;
 }
 
 // Add the SSL IO layer to an existing socket
 NS_IMETHODIMP
 nsTLSSocketProvider::AddToSocket(int32_t family,
                                  const char *host,
                                  int32_t port,
-                                 const char *proxyHost,
-                                 int32_t proxyPort,
+                                 nsIProxyInfo *proxy,
                                  uint32_t flags,
                                  PRFileDesc *aSocket,
                                  nsISupports **securityInfo)
 {
   nsresult rv = nsSSLIOLayerAddToSocket(family,
                                         host,
                                         port,
-                                        proxyHost,
-                                        proxyPort,
+                                        proxy,
                                         aSocket,
                                         securityInfo,
                                         true,
                                         flags);
   
   return (NS_FAILED(rv)) ? NS_ERROR_SOCKET_CREATE_FAILED : NS_OK;
 }