Bug 1211567 - Enable domain socket support for SOCKS; r=bagder draft
authorLiang-Heng Chen <xeonchen@mozilla.com>
Thu, 30 Jun 2016 11:23:40 +0800
changeset 404824 b4ea6dac2f574577a1c302a68f075900348701ea
parent 404131 24763f58772d45279a935790f732d80851924b46
child 529283 98adb5c9d990965b1163bc340c090d7edc4a5850
push id27314
push userbmo:xeonchen@mozilla.com
push dateWed, 24 Aug 2016 07:33:17 +0000
reviewersbagder
bugs1211567
milestone51.0a1
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);