Bug 1211567 - Enable domain socket support for SOCKS; r=bagder
authorLiang-Heng Chen <xeonchen@mozilla.com>
Thu, 30 Jun 2016 11:23:40 +0800
changeset 311075 d58a180022cf3405d9683c8a61d9ca7371d8b02a
parent 311074 f992cf77d4548ecc1495642741de1534e013e60d
child 311076 687db967e4437a3becb8f32cd3581f192fc237ab
push id30602
push userkwierso@gmail.com
push dateThu, 25 Aug 2016 23:53:05 +0000
treeherdermozilla-central@cd4ed9909dc9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbagder
bugs1211567
milestone51.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 1211567 - Enable domain socket support for SOCKS; r=bagder MozReview-Commit-ID: 9yMFckwPf6C
netwerk/base/nsProtocolProxyService.cpp
netwerk/base/nsProtocolProxyService.h
netwerk/socket/nsSOCKSIOLayer.cpp
--- a/netwerk/base/nsProtocolProxyService.cpp
+++ b/netwerk/base/nsProtocolProxyService.cpp
@@ -389,16 +389,26 @@ proxy_GetBoolPref(nsIPrefBranch *aPrefBr
     bool temp;
     nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
     if (NS_FAILED(rv))
         aResult = false;
     else
         aResult = temp;
 }
 
+static inline bool
+IsHostDomainSocket(const nsACString& aHost)
+{
+#ifdef XP_UNIX
+    return Substring(aHost, 0, 5) == "file:";
+#else
+    return false;
+#endif // XP_UNIX
+}
+
 //----------------------------------------------------------------------------
 
 static const int32_t PROXYCONFIG_DIRECT4X = 3;
 static const int32_t PROXYCONFIG_COUNT = 6;
 
 NS_IMPL_ADDREF(nsProtocolProxyService)
 NS_IMPL_RELEASE(nsProtocolProxyService)
 NS_IMPL_CLASSINFO(nsProtocolProxyService, nullptr, nsIClassInfo::SINGLETON,
@@ -614,17 +624,17 @@ nsProtocolProxyService::PrefsChanged(nsI
 
     if (!pref || !strcmp(pref, PROXY_PREF("ftp")))
         proxy_GetStringPref(prefBranch, PROXY_PREF("ftp"), mFTPProxyHost);
 
     if (!pref || !strcmp(pref, PROXY_PREF("ftp_port")))
         proxy_GetIntPref(prefBranch, PROXY_PREF("ftp_port"), mFTPProxyPort);
 
     if (!pref || !strcmp(pref, PROXY_PREF("socks")))
-        proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyHost);
+        proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyTarget);
 
     if (!pref || !strcmp(pref, PROXY_PREF("socks_port")))
         proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
 
     if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
         int32_t version;
         proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
         // make sure this preference value remains sane
@@ -1862,18 +1872,19 @@ nsProtocolProxyService::Resolve_Internal
     // proxy info values for manual configuration mode
     const char *type = nullptr;
     const nsACString *host = nullptr;
     int32_t port = -1;
 
     uint32_t proxyFlags = 0;
 
     if ((flags & RESOLVE_PREFER_SOCKS_PROXY) &&
-        !mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) {
-      host = &mSOCKSProxyHost;
+        !mSOCKSProxyTarget.IsEmpty() &&
+        (IsHostDomainSocket(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
+      host = &mSOCKSProxyTarget;
       if (mSOCKSProxyVersion == 4)
           type = kProxyType_SOCKS4;
       else
           type = kProxyType_SOCKS;
       port = mSOCKSProxyPort;
       if (mSOCKSProxyRemoteDNS)
           proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
     }
@@ -1899,18 +1910,19 @@ nsProtocolProxyService::Resolve_Internal
     }
     else if (!mFTPProxyHost.IsEmpty() && mFTPProxyPort > 0 &&
              !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
              info.scheme.EqualsLiteral("ftp")) {
         host = &mFTPProxyHost;
         type = kProxyType_HTTP;
         port = mFTPProxyPort;
     }
-    else if (!mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) {
-        host = &mSOCKSProxyHost;
+    else if (!mSOCKSProxyTarget.IsEmpty() &&
+        (IsHostDomainSocket(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
+        host = &mSOCKSProxyTarget;
         if (mSOCKSProxyVersion == 4)
             type = kProxyType_SOCKS4;
         else
             type = kProxyType_SOCKS;
         port = mSOCKSProxyPort;
         if (mSOCKSProxyRemoteDNS)
             proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
     }
--- a/netwerk/base/nsProtocolProxyService.h
+++ b/netwerk/base/nsProtocolProxyService.h
@@ -382,18 +382,19 @@ protected:
     nsCString                    mHTTPProxyHost;
     int32_t                      mHTTPProxyPort;
 
     nsCString                    mFTPProxyHost;
     int32_t                      mFTPProxyPort;
 
     nsCString                    mHTTPSProxyHost;
     int32_t                      mHTTPSProxyPort;
-    
-    nsCString                    mSOCKSProxyHost;
+
+    // mSOCKSProxyTarget could be a host or a domain socket path.
+    nsCString                    mSOCKSProxyTarget;
     int32_t                      mSOCKSProxyPort;
     int32_t                      mSOCKSProxyVersion;
     bool                         mSOCKSProxyRemoteDNS;
     bool                         mProxyOverTLS;
 
     RefPtr<nsPACMan>           mPACMan;  // non-null if we are using PAC
     nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
 
--- a/netwerk/socket/nsSOCKSIOLayer.cpp
+++ b/netwerk/socket/nsSOCKSIOLayer.cpp
@@ -14,18 +14,20 @@
 #include "nsIDNSRecord.h"
 #include "nsISOCKSSocketInfo.h"
 #include "nsISocketProvider.h"
 #include "nsSOCKSIOLayer.h"
 #include "nsNetCID.h"
 #include "nsIDNSListener.h"
 #include "nsICancelable.h"
 #include "nsThreadUtils.h"
+#include "nsIURL.h"
 #include "mozilla/Logging.h"
 #include "mozilla/net/DNS.h"
+#include "mozilla/unused.h"
 
 using mozilla::LogLevel;
 using namespace mozilla::net;
 
 static PRDescIdentity nsSOCKSIOLayerIdentity;
 static PRIOMethods nsSOCKSIOLayerMethods;
 static bool firstTime = true;
 static bool ipv6Supported = true;
@@ -106,16 +108,64 @@ private:
     uint32_t ReadUint32();
     void ReadNetAddr(NetAddr *addr, uint16_t fam);
     void ReadNetPort(NetAddr *addr);
 
     void WantRead(uint32_t sz);
     PRStatus ReadFromSocket(PRFileDesc *fd);
     PRStatus WriteToSocket(PRFileDesc *fd);
 
+    bool IsHostDomainSocket()
+    {
+#ifdef XP_UNIX
+        nsAutoCString proxyHost;
+        mProxy->GetHost(proxyHost);
+        return Substring(proxyHost, 0, 5) == "file:";
+#else
+        return false;
+#endif // XP_UNIX
+    }
+
+    nsresult SetDomainSocketPath(const nsACString& aDomainSocketPath,
+                             NetAddr* aProxyAddr)
+    {
+#ifdef XP_UNIX
+        nsresult rv;
+        MOZ_ASSERT(aProxyAddr);
+
+        nsCOMPtr<nsIURL> url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+            return rv;
+        }
+
+        if (NS_WARN_IF(NS_FAILED(rv = url->SetSpec(aDomainSocketPath)))) {
+            return rv;
+        }
+
+        nsAutoCString path;
+        if (NS_WARN_IF(NS_FAILED(rv = url->GetPath(path)))) {
+            return rv;
+        }
+
+        if (sizeof(aProxyAddr->local.path) <= path.Length()) {
+            NS_WARNING("domain socket path too long.");
+            return NS_ERROR_FAILURE;
+        }
+
+        aProxyAddr->raw.family = AF_UNIX;
+        strcpy(aProxyAddr->local.path, path.get());
+
+        return NS_OK;
+#else
+        mozilla::Unused << aProxyAddr;
+        mozilla::Unused << aDomainSocketPath;
+        return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+    }
+
 private:
     State     mState;
     uint8_t * mData;
     uint8_t * mDataIoPtr;
     uint32_t  mDataLength;
     uint32_t  mReadOffset;
     uint32_t  mAmountToRead;
     nsCOMPtr<nsIDNSRecord>  mDnsRec;
@@ -415,39 +465,50 @@ nsSOCKSSocketInfo::ConnectToProxy(PRFile
     }
 
     // Try socks5 if the destination addrress is IPv6
     if (mVersion == 4 &&
         mDestinationAddr.raw.family == AF_INET6) {
         mVersion = 5;
     }
 
+    nsAutoCString proxyHost;
+    mProxy->GetHost(proxyHost);
+
     int32_t proxyPort;
     mProxy->GetPort(&proxyPort);
 
     int32_t addresses = 0;
     do {
-        if (addresses++)
-            mDnsRec->ReportUnusable(proxyPort);
-        
-        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",
-                     proxyHost.get()));
-            return PR_FAILURE;
-        }
+        if (IsHostDomainSocket()) {
+            rv = SetDomainSocketPath(proxyHost, &mInternalProxyAddr);
+            if (NS_FAILED(rv)) {
+                LOGERROR(("socks: unable to connect to SOCKS proxy, %s",
+                         proxyHost.get()));
+              return PR_FAILURE;
+            }
+        } else {
+            if (addresses++) {
+                mDnsRec->ReportUnusable(proxyPort);
+            }
 
-        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)));
+            rv = mDnsRec->GetNextAddr(proxyPort, &mInternalProxyAddr);
+            // No more addresses to try? If so, we'll need to bail
+            if (NS_FAILED(rv)) {
+                LOGERROR(("socks: unable to connect to SOCKS proxy, %s",
+                         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)));
+            }
         }
 
         NetAddr proxy = mInternalProxyAddr;
         FixupAddressFamily(fd, &proxy);
         PRNetAddr prProxy;
         NetAddrToPRNetAddr(&proxy, &prProxy);
         status = fd->lower->methods->connect(fd->lower, &prProxy, mTimeout);
         if (status != PR_SUCCESS) {
@@ -966,16 +1027,22 @@ nsSOCKSSocketInfo::SetConnectTimeout(PRI
 
 PRStatus
 nsSOCKSSocketInfo::DoHandshake(PRFileDesc *fd, int16_t oflags)
 {
     LOGDEBUG(("socks: DoHandshake(), state = %d", mState));
 
     switch (mState) {
         case SOCKS_INITIAL:
+            if (IsHostDomainSocket()) {
+                mState = SOCKS_DNS_COMPLETE;
+                mLookupStatus = NS_OK;
+                return ConnectToProxy(fd);
+            }
+
             return StartDNS(fd);
         case SOCKS_DNS_IN_PROGRESS:
             PR_SetError(PR_IN_PROGRESS_ERROR, 0);
             return PR_FAILURE;
         case SOCKS_DNS_COMPLETE:
             return ConnectToProxy(fd);
         case SOCKS_CONNECTING_TO_PROXY:
             return ContinueConnectingToProxy(fd, oflags);