LCOV - code coverage report
Current view: top level - netwerk/socket - nsSOCKSIOLayer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 722 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 141 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim:set expandtab ts=4 sw=4 sts=4 cin: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "nspr.h"
       8             : #include "private/pprio.h"
       9             : #include "nsString.h"
      10             : #include "nsCRT.h"
      11             : 
      12             : #include "nsIServiceManager.h"
      13             : #include "nsIDNSService.h"
      14             : #include "nsIDNSRecord.h"
      15             : #include "nsISOCKSSocketInfo.h"
      16             : #include "nsISocketProvider.h"
      17             : #include "nsNamedPipeIOLayer.h"
      18             : #include "nsSOCKSIOLayer.h"
      19             : #include "nsNetCID.h"
      20             : #include "nsIDNSListener.h"
      21             : #include "nsICancelable.h"
      22             : #include "nsThreadUtils.h"
      23             : #include "nsIFile.h"
      24             : #include "nsIFileProtocolHandler.h"
      25             : #include "mozilla/Logging.h"
      26             : #include "mozilla/net/DNS.h"
      27             : #include "mozilla/Unused.h"
      28             : 
      29             : using mozilla::LogLevel;
      30             : using namespace mozilla::net;
      31             : 
      32             : static PRDescIdentity nsSOCKSIOLayerIdentity;
      33             : static PRIOMethods nsSOCKSIOLayerMethods;
      34             : static bool firstTime = true;
      35             : static bool ipv6Supported = true;
      36             : 
      37             : 
      38             : static mozilla::LazyLogModule gSOCKSLog("SOCKS");
      39             : #define LOGDEBUG(args) MOZ_LOG(gSOCKSLog, mozilla::LogLevel::Debug, args)
      40             : #define LOGERROR(args) MOZ_LOG(gSOCKSLog, mozilla::LogLevel::Error , args)
      41             : 
      42             : class nsSOCKSSocketInfo : public nsISOCKSSocketInfo
      43             :                         , public nsIDNSListener
      44             : {
      45             :     enum State {
      46             :         SOCKS_INITIAL,
      47             :         SOCKS_DNS_IN_PROGRESS,
      48             :         SOCKS_DNS_COMPLETE,
      49             :         SOCKS_CONNECTING_TO_PROXY,
      50             :         SOCKS4_WRITE_CONNECT_REQUEST,
      51             :         SOCKS4_READ_CONNECT_RESPONSE,
      52             :         SOCKS5_WRITE_AUTH_REQUEST,
      53             :         SOCKS5_READ_AUTH_RESPONSE,
      54             :         SOCKS5_WRITE_USERNAME_REQUEST,
      55             :         SOCKS5_READ_USERNAME_RESPONSE,
      56             :         SOCKS5_WRITE_CONNECT_REQUEST,
      57             :         SOCKS5_READ_CONNECT_RESPONSE_TOP,
      58             :         SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
      59             :         SOCKS_CONNECTED,
      60             :         SOCKS_FAILED
      61             :     };
      62             : 
      63             :     // A buffer of 520 bytes should be enough for any request and response
      64             :     // in case of SOCKS4 as well as SOCKS5
      65             :     static const uint32_t BUFFER_SIZE = 520;
      66             :     static const uint32_t MAX_HOSTNAME_LEN = 255;
      67             :     static const uint32_t MAX_USERNAME_LEN = 255;
      68             :     static const uint32_t MAX_PASSWORD_LEN = 255;
      69             : 
      70             : public:
      71             :     nsSOCKSSocketInfo();
      72             : 
      73             :     NS_DECL_THREADSAFE_ISUPPORTS
      74             :     NS_DECL_NSISOCKSSOCKETINFO
      75             :     NS_DECL_NSIDNSLISTENER
      76             : 
      77             :     void Init(int32_t version,
      78             :               int32_t family,
      79             :               nsIProxyInfo *proxy,
      80             :               const char *destinationHost,
      81             :               uint32_t flags);
      82             : 
      83             :     void SetConnectTimeout(PRIntervalTime to);
      84             :     PRStatus DoHandshake(PRFileDesc *fd, int16_t oflags = -1);
      85             :     int16_t GetPollFlags() const;
      86           0 :     bool IsConnected() const { return mState == SOCKS_CONNECTED; }
      87           0 :     void ForgetFD() { mFD = nullptr; }
      88             :     void SetNamedPipeFD(PRFileDesc *fd) { mFD = fd; }
      89             : 
      90             : private:
      91           0 :     virtual ~nsSOCKSSocketInfo()
      92           0 :     {
      93           0 :         ForgetFD();
      94           0 :         HandshakeFinished();
      95           0 :     }
      96             : 
      97             :     void HandshakeFinished(PRErrorCode err = 0);
      98             :     PRStatus StartDNS(PRFileDesc *fd);
      99             :     PRStatus ConnectToProxy(PRFileDesc *fd);
     100             :     void FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy);
     101             :     PRStatus ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags);
     102             :     PRStatus WriteV4ConnectRequest();
     103             :     PRStatus ReadV4ConnectResponse();
     104             :     PRStatus WriteV5AuthRequest();
     105             :     PRStatus ReadV5AuthResponse();
     106             :     PRStatus WriteV5UsernameRequest();
     107             :     PRStatus ReadV5UsernameResponse();
     108             :     PRStatus WriteV5ConnectRequest();
     109             :     PRStatus ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len);
     110             :     PRStatus ReadV5ConnectResponseTop();
     111             :     PRStatus ReadV5ConnectResponseBottom();
     112             : 
     113             :     uint8_t ReadUint8();
     114             :     uint16_t ReadUint16();
     115             :     uint32_t ReadUint32();
     116             :     void ReadNetAddr(NetAddr *addr, uint16_t fam);
     117             :     void ReadNetPort(NetAddr *addr);
     118             : 
     119             :     void WantRead(uint32_t sz);
     120             :     PRStatus ReadFromSocket(PRFileDesc *fd);
     121             :     PRStatus WriteToSocket(PRFileDesc *fd);
     122             : 
     123           0 :     bool IsLocalProxy()
     124             :     {
     125           0 :         nsAutoCString proxyHost;
     126           0 :         mProxy->GetHost(proxyHost);
     127           0 :         return IsHostLocalTarget(proxyHost);
     128             :     }
     129             : 
     130           0 :     nsresult SetLocalProxyPath(const nsACString& aLocalProxyPath,
     131             :                                NetAddr* aProxyAddr)
     132             :     {
     133             : #ifdef XP_UNIX
     134             :         nsresult rv;
     135           0 :         MOZ_ASSERT(aProxyAddr);
     136             : 
     137             :         nsCOMPtr<nsIProtocolHandler> protocolHandler(
     138           0 :             do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "file", &rv));
     139           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
     140           0 :             return rv;
     141             :         }
     142             : 
     143             :         nsCOMPtr<nsIFileProtocolHandler> fileHandler(
     144           0 :             do_QueryInterface(protocolHandler, &rv));
     145           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
     146           0 :             return rv;
     147             :         }
     148             : 
     149           0 :         nsCOMPtr<nsIFile> socketFile;
     150           0 :         rv = fileHandler->GetFileFromURLSpec(aLocalProxyPath,
     151           0 :                                              getter_AddRefs(socketFile));
     152           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
     153           0 :             return rv;
     154             :         }
     155             : 
     156           0 :         nsAutoCString path;
     157           0 :         if (NS_WARN_IF(NS_FAILED(rv = socketFile->GetNativePath(path)))) {
     158           0 :             return rv;
     159             :         }
     160             : 
     161           0 :         if (sizeof(aProxyAddr->local.path) <= path.Length()) {
     162           0 :             NS_WARNING("domain socket path too long.");
     163           0 :             return NS_ERROR_FAILURE;
     164             :         }
     165             : 
     166           0 :         aProxyAddr->raw.family = AF_UNIX;
     167           0 :         strcpy(aProxyAddr->local.path, path.get());
     168             : 
     169           0 :         return NS_OK;
     170             : #elif defined(XP_WIN)
     171             :         MOZ_ASSERT(aProxyAddr);
     172             : 
     173             :         if (sizeof(aProxyAddr->local.path) <= aLocalProxyPath.Length()) {
     174             :             NS_WARNING("pipe path too long.");
     175             :             return NS_ERROR_FAILURE;
     176             :         }
     177             : 
     178             :         aProxyAddr->raw.family = AF_LOCAL;
     179             :         strcpy(aProxyAddr->local.path, PromiseFlatCString(aLocalProxyPath).get());
     180             :         return NS_OK;
     181             : #else
     182             :         mozilla::Unused << aLocalProxyPath;
     183             :         mozilla::Unused << aProxyAddr;
     184             :         return NS_ERROR_NOT_IMPLEMENTED;
     185             : #endif
     186             :     }
     187             : 
     188             :     bool
     189           0 :     SetupNamedPipeLayer(PRFileDesc *fd)
     190             :     {
     191             : #if defined(XP_WIN)
     192             :         if (IsLocalProxy()) {
     193             :             // nsSOCKSIOLayer handshaking only works under blocking mode
     194             :             // unfortunately. Remember named pipe's FD to switch between modes.
     195             :             SetNamedPipeFD(fd->lower);
     196             :             return true;
     197             :         }
     198             : #endif
     199           0 :         return false;
     200             :     }
     201             : 
     202             : private:
     203             :     State     mState;
     204             :     uint8_t * mData;
     205             :     uint8_t * mDataIoPtr;
     206             :     uint32_t  mDataLength;
     207             :     uint32_t  mReadOffset;
     208             :     uint32_t  mAmountToRead;
     209             :     nsCOMPtr<nsIDNSRecord>  mDnsRec;
     210             :     nsCOMPtr<nsICancelable> mLookup;
     211             :     nsresult                mLookupStatus;
     212             :     PRFileDesc             *mFD;
     213             : 
     214             :     nsCString mDestinationHost;
     215             :     nsCOMPtr<nsIProxyInfo> mProxy;
     216             :     int32_t   mVersion;   // SOCKS version 4 or 5
     217             :     int32_t   mDestinationFamily;
     218             :     uint32_t  mFlags;
     219             :     NetAddr   mInternalProxyAddr;
     220             :     NetAddr   mExternalProxyAddr;
     221             :     NetAddr   mDestinationAddr;
     222             :     PRIntervalTime mTimeout;
     223             :     nsCString mProxyUsername; // Cache, from mProxy
     224             : };
     225             : 
     226           0 : nsSOCKSSocketInfo::nsSOCKSSocketInfo()
     227             :     : mState(SOCKS_INITIAL)
     228             :     , mDataIoPtr(nullptr)
     229             :     , mDataLength(0)
     230             :     , mReadOffset(0)
     231             :     , mAmountToRead(0)
     232             :     , mVersion(-1)
     233             :     , mDestinationFamily(AF_INET)
     234             :     , mFlags(0)
     235           0 :     , mTimeout(PR_INTERVAL_NO_TIMEOUT)
     236             : {
     237           0 :     mData = new uint8_t[BUFFER_SIZE];
     238             : 
     239           0 :     mInternalProxyAddr.raw.family = AF_INET;
     240           0 :     mInternalProxyAddr.inet.ip = htonl(INADDR_ANY);
     241           0 :     mInternalProxyAddr.inet.port = htons(0);
     242             : 
     243           0 :     mExternalProxyAddr.raw.family = AF_INET;
     244           0 :     mExternalProxyAddr.inet.ip = htonl(INADDR_ANY);
     245           0 :     mExternalProxyAddr.inet.port = htons(0);
     246             : 
     247           0 :     mDestinationAddr.raw.family = AF_INET;
     248           0 :     mDestinationAddr.inet.ip = htonl(INADDR_ANY);
     249           0 :     mDestinationAddr.inet.port = htons(0);
     250           0 : }
     251             : 
     252             : /* Helper template class to statically check that writes to a fixed-size
     253             :  * buffer are not going to overflow.
     254             :  *
     255             :  * Example usage:
     256             :  *   uint8_t real_buf[TOTAL_SIZE];
     257             :  *   Buffer<TOTAL_SIZE> buf(&real_buf);
     258             :  *   auto buf2 = buf.WriteUint16(1);
     259             :  *   auto buf3 = buf2.WriteUint8(2);
     260             :  *
     261             :  * It is possible to chain them, to limit the number of (error-prone)
     262             :  * intermediate variables:
     263             :  *   auto buf = Buffer<TOTAL_SIZE>(&real_buf)
     264             :  *              .WriteUint16(1)
     265             :  *              .WriteUint8(2);
     266             :  *
     267             :  * Debug builds assert when intermediate variables are reused:
     268             :  *   Buffer<TOTAL_SIZE> buf(&real_buf);
     269             :  *   auto buf2 = buf.WriteUint16(1);
     270             :  *   auto buf3 = buf.WriteUint8(2); // Asserts
     271             :  *
     272             :  * Strings can be written, given an explicit maximum length.
     273             :  *   buf.WriteString<MAX_STRING_LENGTH>(str);
     274             :  *
     275             :  * The Written() method returns how many bytes have been written so far:
     276             :  *   Buffer<TOTAL_SIZE> buf(&real_buf);
     277             :  *   auto buf2 = buf.WriteUint16(1);
     278             :  *   auto buf3 = buf2.WriteUint8(2);
     279             :  *   buf3.Written(); // returns 3.
     280             :  */
     281             : template <size_t Size>
     282             : class Buffer
     283             : {
     284             : public:
     285           0 :   Buffer() : mBuf(nullptr), mLength(0) {}
     286             : 
     287           0 :   explicit Buffer(uint8_t* aBuf, size_t aLength=0)
     288           0 :   : mBuf(aBuf), mLength(aLength) {}
     289             : 
     290             :   template <size_t Size2>
     291           0 :   MOZ_IMPLICIT Buffer(const Buffer<Size2>& aBuf) : mBuf(aBuf.mBuf), mLength(aBuf.mLength) {
     292             :       static_assert(Size2 > Size, "Cannot cast buffer");
     293           0 :   }
     294             : 
     295           0 :   Buffer<Size - sizeof(uint8_t)> WriteUint8(uint8_t aValue) {
     296           0 :       return Write(aValue);
     297             :   }
     298             : 
     299           0 :   Buffer<Size - sizeof(uint16_t)> WriteUint16(uint16_t aValue) {
     300           0 :       return Write(aValue);
     301             :   }
     302             : 
     303           0 :   Buffer<Size - sizeof(uint32_t)> WriteUint32(uint32_t aValue) {
     304           0 :       return Write(aValue);
     305             :   }
     306             : 
     307           0 :   Buffer<Size - sizeof(uint16_t)> WriteNetPort(const NetAddr* aAddr) {
     308           0 :       return WriteUint16(aAddr->inet.port);
     309             :   }
     310             : 
     311           0 :   Buffer<Size - sizeof(IPv6Addr)> WriteNetAddr(const NetAddr* aAddr) {
     312           0 :       if (aAddr->raw.family == AF_INET) {
     313           0 :           return Write(aAddr->inet.ip);
     314           0 :       } else if (aAddr->raw.family == AF_INET6) {
     315           0 :           return Write(aAddr->inet6.ip.u8);
     316             :       }
     317           0 :       NS_NOTREACHED("Unknown address family");
     318           0 :       return *this;
     319             :   }
     320             : 
     321             :   template <size_t MaxLength>
     322           0 :   Buffer<Size - MaxLength> WriteString(const nsACString& aStr) {
     323           0 :       if (aStr.Length() > MaxLength) {
     324           0 :           return Buffer<Size - MaxLength>(nullptr);
     325             :       }
     326           0 :       return WritePtr<char, MaxLength>(aStr.Data(), aStr.Length());
     327             :   }
     328             : 
     329           0 :   size_t Written() {
     330           0 :       MOZ_ASSERT(mBuf);
     331           0 :       return mLength;
     332             :   }
     333             : 
     334           0 :   explicit operator bool() { return !!mBuf; }
     335             : private:
     336             :   template <size_t Size2>
     337             :   friend class Buffer;
     338             : 
     339             :   template <typename T>
     340           0 :   Buffer<Size - sizeof(T)> Write(T& aValue) {
     341           0 :       return WritePtr<T, sizeof(T)>(&aValue, sizeof(T));
     342             :   }
     343             : 
     344             :   template <typename T, size_t Length>
     345           0 :   Buffer<Size - Length> WritePtr(const T* aValue, size_t aCopyLength) {
     346             :       static_assert(Size >= Length, "Cannot write that much");
     347           0 :       MOZ_ASSERT(aCopyLength <= Length);
     348           0 :       MOZ_ASSERT(mBuf);
     349           0 :       memcpy(mBuf, aValue, aCopyLength);
     350           0 :       Buffer<Size - Length> result(mBuf + aCopyLength, mLength + aCopyLength);
     351           0 :       mBuf = nullptr;
     352           0 :       mLength = 0;
     353           0 :       return result;
     354             :   }
     355             : 
     356             :   uint8_t* mBuf;
     357             :   size_t mLength;
     358             : };
     359             : 
     360             : 
     361             : void
     362           0 : nsSOCKSSocketInfo::Init(int32_t version, int32_t family, nsIProxyInfo *proxy, const char *host, uint32_t flags)
     363             : {
     364           0 :     mVersion         = version;
     365           0 :     mDestinationFamily = family;
     366           0 :     mProxy           = proxy;
     367           0 :     mDestinationHost = host;
     368           0 :     mFlags           = flags;
     369           0 :     mProxy->GetUsername(mProxyUsername); // cache
     370           0 : }
     371             : 
     372           0 : NS_IMPL_ISUPPORTS(nsSOCKSSocketInfo, nsISOCKSSocketInfo, nsIDNSListener)
     373             : 
     374             : NS_IMETHODIMP
     375           0 : nsSOCKSSocketInfo::GetExternalProxyAddr(NetAddr * *aExternalProxyAddr)
     376             : {
     377           0 :     memcpy(*aExternalProxyAddr, &mExternalProxyAddr, sizeof(NetAddr));
     378           0 :     return NS_OK;
     379             : }
     380             : 
     381             : NS_IMETHODIMP
     382           0 : nsSOCKSSocketInfo::SetExternalProxyAddr(NetAddr *aExternalProxyAddr)
     383             : {
     384           0 :     memcpy(&mExternalProxyAddr, aExternalProxyAddr, sizeof(NetAddr));
     385           0 :     return NS_OK;
     386             : }
     387             : 
     388             : NS_IMETHODIMP
     389           0 : nsSOCKSSocketInfo::GetDestinationAddr(NetAddr * *aDestinationAddr)
     390             : {
     391           0 :     memcpy(*aDestinationAddr, &mDestinationAddr, sizeof(NetAddr));
     392           0 :     return NS_OK;
     393             : }
     394             : 
     395             : NS_IMETHODIMP
     396           0 : nsSOCKSSocketInfo::SetDestinationAddr(NetAddr *aDestinationAddr)
     397             : {
     398           0 :     memcpy(&mDestinationAddr, aDestinationAddr, sizeof(NetAddr));
     399           0 :     return NS_OK;
     400             : }
     401             : 
     402             : NS_IMETHODIMP
     403           0 : nsSOCKSSocketInfo::GetInternalProxyAddr(NetAddr * *aInternalProxyAddr)
     404             : {
     405           0 :     memcpy(*aInternalProxyAddr, &mInternalProxyAddr, sizeof(NetAddr));
     406           0 :     return NS_OK;
     407             : }
     408             : 
     409             : NS_IMETHODIMP
     410           0 : nsSOCKSSocketInfo::SetInternalProxyAddr(NetAddr *aInternalProxyAddr)
     411             : {
     412           0 :     memcpy(&mInternalProxyAddr, aInternalProxyAddr, sizeof(NetAddr));
     413           0 :     return NS_OK;
     414             : }
     415             : 
     416             : // There needs to be a means of distinguishing between connection errors
     417             : // that the SOCKS server reports when it rejects a connection request, and
     418             : // connection errors that happen while attempting to connect to the SOCKS
     419             : // server. Otherwise, Firefox will report incorrectly that the proxy server
     420             : // is refusing connections when a SOCKS request is rejected by the proxy.
     421             : // When a SOCKS handshake failure occurs, the PR error is set to
     422             : // PR_UNKNOWN_ERROR, and the real error code is returned via the OS error.
     423             : void
     424           0 : nsSOCKSSocketInfo::HandshakeFinished(PRErrorCode err)
     425             : {
     426           0 :     if (err == 0) {
     427           0 :         mState = SOCKS_CONNECTED;
     428             : #if defined(XP_WIN)
     429             :         // Switch back to nonblocking mode after finishing handshaking.
     430             :         if (IsLocalProxy() && mFD) {
     431             :             PRSocketOptionData opt_nonblock;
     432             :             opt_nonblock.option = PR_SockOpt_Nonblocking;
     433             :             opt_nonblock.value.non_blocking = PR_TRUE;
     434             :             PR_SetSocketOption(mFD, &opt_nonblock);
     435             :             mFD = nullptr;
     436             :         }
     437             : #endif
     438             :     } else {
     439           0 :         mState = SOCKS_FAILED;
     440           0 :         PR_SetError(PR_UNKNOWN_ERROR, err);
     441             :     }
     442             : 
     443             :     // We don't need the buffer any longer, so free it.
     444           0 :     delete [] mData;
     445           0 :     mData = nullptr;
     446           0 :     mDataIoPtr = nullptr;
     447           0 :     mDataLength = 0;
     448           0 :     mReadOffset = 0;
     449           0 :     mAmountToRead = 0;
     450           0 :     if (mLookup) {
     451           0 :         mLookup->Cancel(NS_ERROR_FAILURE);
     452           0 :         mLookup = nullptr;
     453             :     }
     454           0 : }
     455             : 
     456             : PRStatus
     457           0 : nsSOCKSSocketInfo::StartDNS(PRFileDesc *fd)
     458             : {
     459           0 :     MOZ_ASSERT(!mDnsRec && mState == SOCKS_INITIAL,
     460             :                "Must be in initial state to make DNS Lookup");
     461             : 
     462           0 :     nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
     463           0 :     if (!dns)
     464           0 :         return PR_FAILURE;
     465             : 
     466           0 :     nsCString proxyHost;
     467           0 :     mProxy->GetHost(proxyHost);
     468             : 
     469           0 :     mozilla::OriginAttributes attrs;
     470             : 
     471           0 :     mFD  = fd;
     472           0 :     nsresult rv = dns->AsyncResolveNative(proxyHost, 0, this,
     473             :                                           mozilla::GetCurrentThreadEventTarget(), attrs,
     474           0 :                                           getter_AddRefs(mLookup));
     475             : 
     476           0 :     if (NS_FAILED(rv)) {
     477           0 :         LOGERROR(("socks: DNS lookup for SOCKS proxy %s failed",
     478             :                   proxyHost.get()));
     479           0 :         return PR_FAILURE;
     480             :     }
     481           0 :     mState = SOCKS_DNS_IN_PROGRESS;
     482           0 :     PR_SetError(PR_IN_PROGRESS_ERROR, 0);
     483           0 :     return PR_FAILURE;
     484             : }
     485             : 
     486             : NS_IMETHODIMP
     487           0 : nsSOCKSSocketInfo::OnLookupComplete(nsICancelable *aRequest,
     488             :                                     nsIDNSRecord *aRecord,
     489             :                                     nsresult aStatus)
     490             : {
     491           0 :     MOZ_ASSERT(aRequest == mLookup, "wrong DNS query");
     492           0 :     mLookup = nullptr;
     493           0 :     mLookupStatus = aStatus;
     494           0 :     mDnsRec = aRecord;
     495           0 :     mState = SOCKS_DNS_COMPLETE;
     496           0 :     if (mFD) {
     497           0 :       ConnectToProxy(mFD);
     498           0 :       ForgetFD();
     499             :     }
     500           0 :     return NS_OK;
     501             : }
     502             : 
     503             : PRStatus
     504           0 : nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd)
     505             : {
     506             :     PRStatus status;
     507             :     nsresult rv;
     508             : 
     509           0 :     MOZ_ASSERT(mState == SOCKS_DNS_COMPLETE,
     510             :                "Must have DNS to make connection!");
     511             : 
     512           0 :     if (NS_FAILED(mLookupStatus)) {
     513           0 :         PR_SetError(PR_BAD_ADDRESS_ERROR, 0);
     514           0 :         return PR_FAILURE;
     515             :     }
     516             : 
     517             :     // Try socks5 if the destination addrress is IPv6
     518           0 :     if (mVersion == 4 &&
     519           0 :         mDestinationAddr.raw.family == AF_INET6) {
     520           0 :         mVersion = 5;
     521             :     }
     522             : 
     523           0 :     nsAutoCString proxyHost;
     524           0 :     mProxy->GetHost(proxyHost);
     525             : 
     526             :     int32_t proxyPort;
     527           0 :     mProxy->GetPort(&proxyPort);
     528             : 
     529           0 :     int32_t addresses = 0;
     530           0 :     do {
     531           0 :         if (IsLocalProxy()) {
     532           0 :             rv = SetLocalProxyPath(proxyHost, &mInternalProxyAddr);
     533           0 :             if (NS_FAILED(rv)) {
     534           0 :                 LOGERROR(("socks: unable to connect to SOCKS proxy, %s",
     535             :                          proxyHost.get()));
     536           0 :                 return PR_FAILURE;
     537             :             }
     538             :         } else {
     539           0 :             if (addresses++) {
     540           0 :                 mDnsRec->ReportUnusable(proxyPort);
     541             :             }
     542             : 
     543           0 :             rv = mDnsRec->GetNextAddr(proxyPort, &mInternalProxyAddr);
     544             :             // No more addresses to try? If so, we'll need to bail
     545           0 :             if (NS_FAILED(rv)) {
     546           0 :                 LOGERROR(("socks: unable to connect to SOCKS proxy, %s",
     547             :                          proxyHost.get()));
     548           0 :                 return PR_FAILURE;
     549             :             }
     550             : 
     551           0 :             if (MOZ_LOG_TEST(gSOCKSLog, LogLevel::Debug)) {
     552             :               char buf[kIPv6CStrBufSize];
     553           0 :               NetAddrToString(&mInternalProxyAddr, buf, sizeof(buf));
     554           0 :               LOGDEBUG(("socks: trying proxy server, %s:%hu",
     555             :                        buf, ntohs(mInternalProxyAddr.inet.port)));
     556             :             }
     557             :         }
     558             : 
     559           0 :         NetAddr proxy = mInternalProxyAddr;
     560           0 :         FixupAddressFamily(fd, &proxy);
     561             :         PRNetAddr prProxy;
     562           0 :         NetAddrToPRNetAddr(&proxy, &prProxy);
     563           0 :         status = fd->lower->methods->connect(fd->lower, &prProxy, mTimeout);
     564           0 :         if (status != PR_SUCCESS) {
     565           0 :             PRErrorCode c = PR_GetError();
     566             : 
     567             :             // If EINPROGRESS, return now and check back later after polling
     568           0 :             if (c == PR_WOULD_BLOCK_ERROR || c == PR_IN_PROGRESS_ERROR) {
     569           0 :                 mState = SOCKS_CONNECTING_TO_PROXY;
     570           0 :                 return status;
     571           0 :             } else if (IsLocalProxy()) {
     572           0 :                 LOGERROR(("socks: connect to domain socket failed (%d)", c));
     573           0 :                 PR_SetError(PR_CONNECT_REFUSED_ERROR, 0);
     574           0 :                 mState = SOCKS_FAILED;
     575           0 :                 return status;
     576             :             }
     577             :         }
     578           0 :     } while (status != PR_SUCCESS);
     579             : 
     580             : #if defined(XP_WIN)
     581             :     // Switch to blocking mode during handshaking
     582             :     if (IsLocalProxy() && mFD) {
     583             :         PRSocketOptionData opt_nonblock;
     584             :         opt_nonblock.option = PR_SockOpt_Nonblocking;
     585             :         opt_nonblock.value.non_blocking = PR_FALSE;
     586             :         PR_SetSocketOption(mFD, &opt_nonblock);
     587             :     }
     588             : #endif
     589             : 
     590             :     // Connected now, start SOCKS
     591           0 :     if (mVersion == 4)
     592           0 :         return WriteV4ConnectRequest();
     593           0 :     return WriteV5AuthRequest();
     594             : }
     595             : 
     596             : void
     597           0 : nsSOCKSSocketInfo::FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy)
     598             : {
     599           0 :     int32_t proxyFamily = mInternalProxyAddr.raw.family;
     600             :     // Do nothing if the address family is already matched
     601           0 :     if (proxyFamily == mDestinationFamily) {
     602           0 :         return;
     603             :     }
     604             :     // If the system does not support IPv6 and the proxy address is IPv6,
     605             :     // We can do nothing here.
     606           0 :     if (proxyFamily == AF_INET6 && !ipv6Supported) {
     607           0 :         return;
     608             :     }
     609             :     // If the system does not support IPv6 and the destination address is
     610             :     // IPv6, convert IPv4 address to IPv4-mapped IPv6 address to satisfy
     611             :     // the emulation layer
     612           0 :     if (mDestinationFamily == AF_INET6 && !ipv6Supported) {
     613           0 :         proxy->inet6.family = AF_INET6;
     614           0 :         proxy->inet6.port = mInternalProxyAddr.inet.port;
     615           0 :         uint8_t *proxyp = proxy->inet6.ip.u8;
     616           0 :         memset(proxyp, 0, 10);
     617           0 :         memset(proxyp + 10, 0xff, 2);
     618           0 :         memcpy(proxyp + 12,(char *) &mInternalProxyAddr.inet.ip, 4);
     619             :         // mDestinationFamily should not be updated
     620           0 :         return;
     621             :     }
     622             :     // There's no PR_NSPR_IO_LAYER required when using named pipe,
     623             :     // we simply ignore the TCP family here.
     624           0 :     if (SetupNamedPipeLayer(fd)) {
     625           0 :         return;
     626             :     }
     627             : 
     628             :     // Get an OS native handle from a specified FileDesc
     629           0 :     PROsfd osfd = PR_FileDesc2NativeHandle(fd);
     630           0 :     if (osfd == -1) {
     631           0 :         return;
     632             :     }
     633             : 
     634             :     // Create a new FileDesc with a specified family
     635           0 :     PRFileDesc *tmpfd = PR_OpenTCPSocket(proxyFamily);
     636           0 :     if (!tmpfd) {
     637           0 :         return;
     638             :     }
     639           0 :     PROsfd newsd = PR_FileDesc2NativeHandle(tmpfd);
     640           0 :     if (newsd == -1) {
     641           0 :         PR_Close(tmpfd);
     642           0 :         return;
     643             :     }
     644             :     // Must succeed because PR_FileDesc2NativeHandle succeeded
     645           0 :     fd = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
     646           0 :     MOZ_ASSERT(fd);
     647             :     // Swap OS native handles
     648           0 :     PR_ChangeFileDescNativeHandle(fd, newsd);
     649           0 :     PR_ChangeFileDescNativeHandle(tmpfd, osfd);
     650             :     // Close temporary FileDesc which is now associated with
     651             :     // old OS native handle
     652           0 :     PR_Close(tmpfd);
     653           0 :     mDestinationFamily = proxyFamily;
     654             : }
     655             : 
     656             : PRStatus
     657           0 : nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags)
     658             : {
     659             :     PRStatus status;
     660             : 
     661           0 :     MOZ_ASSERT(mState == SOCKS_CONNECTING_TO_PROXY,
     662             :                "Continuing connection in wrong state!");
     663             : 
     664           0 :     LOGDEBUG(("socks: continuing connection to proxy"));
     665             : 
     666           0 :     status = fd->lower->methods->connectcontinue(fd->lower, oflags);
     667           0 :     if (status != PR_SUCCESS) {
     668           0 :         PRErrorCode c = PR_GetError();
     669           0 :         if (c != PR_WOULD_BLOCK_ERROR && c != PR_IN_PROGRESS_ERROR) {
     670             :             // A connection failure occured, try another address
     671           0 :             mState = SOCKS_DNS_COMPLETE;
     672           0 :             return ConnectToProxy(fd);
     673             :         }
     674             : 
     675             :         // We're still connecting
     676           0 :         return PR_FAILURE;
     677             :     }
     678             : 
     679             :     // Connected now, start SOCKS
     680           0 :     if (mVersion == 4)
     681           0 :         return WriteV4ConnectRequest();
     682           0 :     return WriteV5AuthRequest();
     683             : }
     684             : 
     685             : PRStatus
     686           0 : nsSOCKSSocketInfo::WriteV4ConnectRequest()
     687             : {
     688           0 :     if (mProxyUsername.Length() > MAX_USERNAME_LEN) {
     689           0 :         LOGERROR(("socks username is too long"));
     690           0 :         HandshakeFinished(PR_UNKNOWN_ERROR);
     691           0 :         return PR_FAILURE;
     692             :     }
     693             : 
     694           0 :     NetAddr *addr = &mDestinationAddr;
     695             :     int32_t proxy_resolve;
     696             : 
     697           0 :     MOZ_ASSERT(mState == SOCKS_CONNECTING_TO_PROXY,
     698             :                "Invalid state!");
     699             : 
     700           0 :     proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
     701             : 
     702           0 :     mDataLength = 0;
     703           0 :     mState = SOCKS4_WRITE_CONNECT_REQUEST;
     704             : 
     705           0 :     LOGDEBUG(("socks4: sending connection request (socks4a resolve? %s)",
     706             :              proxy_resolve? "yes" : "no"));
     707             : 
     708             :     // Send a SOCKS 4 connect request.
     709           0 :     auto buf = Buffer<BUFFER_SIZE>(mData)
     710           0 :                .WriteUint8(0x04) // version -- 4
     711           0 :                .WriteUint8(0x01) // command -- connect
     712           0 :                .WriteNetPort(addr);
     713             : 
     714             :     // We don't have anything more to write after the if, so we can
     715             :     // use a buffer with no further writes allowed.
     716           0 :     Buffer<0> buf3;
     717           0 :     if (proxy_resolve) {
     718             :         // Add the full name, null-terminated, to the request
     719             :         // according to SOCKS 4a. A fake IP address, with the first
     720             :         // four bytes set to 0 and the last byte set to something other
     721             :         // than 0, is used to notify the proxy that this is a SOCKS 4a
     722             :         // request. This request type works for Tor and perhaps others.
     723             :         // Passwords not supported by V4.
     724           0 :         auto buf2 = buf.WriteUint32(htonl(0x00000001)) // Fake IP
     725           0 :                        .WriteString<MAX_USERNAME_LEN>(mProxyUsername)
     726           0 :                        .WriteUint8(0x00) // Null-terminate username
     727           0 :                        .WriteString<MAX_HOSTNAME_LEN>(mDestinationHost); // Hostname
     728           0 :         if (!buf2) {
     729           0 :             LOGERROR(("socks4: destination host name is too long!"));
     730           0 :             HandshakeFinished(PR_BAD_ADDRESS_ERROR);
     731           0 :             return PR_FAILURE;
     732             :         }
     733           0 :         buf3 = buf2.WriteUint8(0x00);
     734           0 :     } else if (addr->raw.family == AF_INET) {
     735             :         // Passwords not supported by V4.
     736           0 :         buf3 = buf.WriteNetAddr(addr) // Add the IPv4 address
     737           0 :                   .WriteString<MAX_USERNAME_LEN>(mProxyUsername)
     738           0 :                   .WriteUint8(0x00); // Null-terminate username
     739             :     } else {
     740           0 :         LOGERROR(("socks: SOCKS 4 can only handle IPv4 addresses!"));
     741           0 :         HandshakeFinished(PR_BAD_ADDRESS_ERROR);
     742           0 :         return PR_FAILURE;
     743             :     }
     744             : 
     745           0 :     mDataLength = buf3.Written();
     746           0 :     return PR_SUCCESS;
     747             : }
     748             : 
     749             : PRStatus
     750           0 : nsSOCKSSocketInfo::ReadV4ConnectResponse()
     751             : {
     752           0 :     MOZ_ASSERT(mState == SOCKS4_READ_CONNECT_RESPONSE,
     753             :                "Handling SOCKS 4 connection reply in wrong state!");
     754           0 :     MOZ_ASSERT(mDataLength == 8,
     755             :                "SOCKS 4 connection reply must be 8 bytes!");
     756             : 
     757           0 :     LOGDEBUG(("socks4: checking connection reply"));
     758             : 
     759           0 :     if (ReadUint8() != 0x00) {
     760           0 :         LOGERROR(("socks4: wrong connection reply"));
     761           0 :         HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
     762           0 :         return PR_FAILURE;
     763             :     }
     764             : 
     765             :     // See if our connection request was granted
     766           0 :     if (ReadUint8() == 90) {
     767           0 :         LOGDEBUG(("socks4: connection successful!"));
     768           0 :         HandshakeFinished();
     769           0 :         return PR_SUCCESS;
     770             :     }
     771             : 
     772           0 :     LOGERROR(("socks4: unable to connect"));
     773           0 :     HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
     774           0 :     return PR_FAILURE;
     775             : }
     776             : 
     777             : PRStatus
     778           0 : nsSOCKSSocketInfo::WriteV5AuthRequest()
     779             : {
     780           0 :     MOZ_ASSERT(mVersion == 5, "SOCKS version must be 5!");
     781             : 
     782           0 :     mDataLength = 0;
     783           0 :     mState = SOCKS5_WRITE_AUTH_REQUEST;
     784             : 
     785             :     // Send an initial SOCKS 5 greeting
     786           0 :     LOGDEBUG(("socks5: sending auth methods"));
     787           0 :     mDataLength = Buffer<BUFFER_SIZE>(mData)
     788           0 :                   .WriteUint8(0x05) // version -- 5
     789           0 :                   .WriteUint8(0x01) // # of auth methods -- 1
     790             :                   // Use authenticate iff we have a proxy username.
     791           0 :                   .WriteUint8(mProxyUsername.IsEmpty() ? 0x00 : 0x02)
     792           0 :                   .Written();
     793             : 
     794           0 :     return PR_SUCCESS;
     795             : }
     796             : 
     797             : PRStatus
     798           0 : nsSOCKSSocketInfo::ReadV5AuthResponse()
     799             : {
     800           0 :     MOZ_ASSERT(mState == SOCKS5_READ_AUTH_RESPONSE,
     801             :                "Handling SOCKS 5 auth method reply in wrong state!");
     802           0 :     MOZ_ASSERT(mDataLength == 2,
     803             :                "SOCKS 5 auth method reply must be 2 bytes!");
     804             : 
     805           0 :     LOGDEBUG(("socks5: checking auth method reply"));
     806             : 
     807             :     // Check version number
     808           0 :     if (ReadUint8() != 0x05) {
     809           0 :         LOGERROR(("socks5: unexpected version in the reply"));
     810           0 :         HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
     811           0 :         return PR_FAILURE;
     812             :     }
     813             : 
     814             :     // Make sure our authentication choice was accepted,
     815             :     // and continue accordingly
     816           0 :     uint8_t authMethod = ReadUint8();
     817           0 :     if (mProxyUsername.IsEmpty() && authMethod == 0x00) { // no auth
     818           0 :         LOGDEBUG(("socks5: server allows connection without authentication"));
     819           0 :         return WriteV5ConnectRequest();
     820           0 :     } else if (!mProxyUsername.IsEmpty() && authMethod == 0x02) { // username/pw
     821           0 :         LOGDEBUG(("socks5: auth method accepted by server"));
     822           0 :         return WriteV5UsernameRequest();
     823             :     } else { // 0xFF signals error
     824           0 :         LOGERROR(("socks5: server did not accept our authentication method"));
     825           0 :         HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
     826           0 :         return PR_FAILURE;
     827             :     }
     828             : }
     829             : 
     830             : PRStatus
     831           0 : nsSOCKSSocketInfo::WriteV5UsernameRequest()
     832             : {
     833           0 :     MOZ_ASSERT(mVersion == 5, "SOCKS version must be 5!");
     834             : 
     835           0 :     if (mProxyUsername.Length() > MAX_USERNAME_LEN) {
     836           0 :         LOGERROR(("socks username is too long"));
     837           0 :         HandshakeFinished(PR_UNKNOWN_ERROR);
     838           0 :         return PR_FAILURE;
     839             :     }
     840             : 
     841           0 :     nsCString password;
     842           0 :     mProxy->GetPassword(password);
     843           0 :     if (password.Length() > MAX_PASSWORD_LEN) {
     844           0 :         LOGERROR(("socks password is too long"));
     845           0 :         HandshakeFinished(PR_UNKNOWN_ERROR);
     846           0 :         return PR_FAILURE;
     847             :     }
     848             : 
     849           0 :     mDataLength = 0;
     850           0 :     mState = SOCKS5_WRITE_USERNAME_REQUEST;
     851             : 
     852             :     // RFC 1929 Username/password auth for SOCKS 5
     853           0 :     LOGDEBUG(("socks5: sending username and password"));
     854           0 :     mDataLength = Buffer<BUFFER_SIZE>(mData)
     855           0 :                   .WriteUint8(0x01) // version 1 (not 5)
     856           0 :                   .WriteUint8(mProxyUsername.Length()) // username length
     857           0 :                   .WriteString<MAX_USERNAME_LEN>(mProxyUsername) // username
     858           0 :                   .WriteUint8(password.Length()) // password length
     859           0 :                   .WriteString<MAX_PASSWORD_LEN>(password) // password. WARNING: Sent unencrypted!
     860           0 :                   .Written();
     861             : 
     862           0 :     return PR_SUCCESS;
     863             : }
     864             : 
     865             : PRStatus
     866           0 : nsSOCKSSocketInfo::ReadV5UsernameResponse()
     867             : {
     868           0 :     MOZ_ASSERT(mState == SOCKS5_READ_USERNAME_RESPONSE,
     869             :                       "Handling SOCKS 5 username/password reply in wrong state!");
     870             : 
     871           0 :     MOZ_ASSERT(mDataLength == 2,
     872             :                "SOCKS 5 username reply must be 2 bytes");
     873             : 
     874             :     // Check version number, must be 1 (not 5)
     875           0 :     if (ReadUint8() != 0x01) {
     876           0 :         LOGERROR(("socks5: unexpected version in the reply"));
     877           0 :         HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
     878           0 :         return PR_FAILURE;
     879             :     }
     880             : 
     881             :     // Check whether username/password were accepted
     882           0 :     if (ReadUint8() != 0x00) { // 0 = success
     883           0 :         LOGERROR(("socks5: username/password not accepted"));
     884           0 :         HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
     885           0 :         return PR_FAILURE;
     886             :     }
     887             : 
     888           0 :     LOGDEBUG(("socks5: username/password accepted by server"));
     889             : 
     890           0 :     return WriteV5ConnectRequest();
     891             : }
     892             : 
     893             : PRStatus
     894           0 : nsSOCKSSocketInfo::WriteV5ConnectRequest()
     895             : {
     896             :     // Send SOCKS 5 connect request
     897           0 :     NetAddr *addr = &mDestinationAddr;
     898             :     int32_t proxy_resolve;
     899           0 :     proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
     900             : 
     901           0 :     LOGDEBUG(("socks5: sending connection request (socks5 resolve? %s)",
     902             :              proxy_resolve? "yes" : "no"));
     903             : 
     904           0 :     mDataLength = 0;
     905           0 :     mState = SOCKS5_WRITE_CONNECT_REQUEST;
     906             : 
     907           0 :     auto buf = Buffer<BUFFER_SIZE>(mData)
     908           0 :                .WriteUint8(0x05) // version -- 5
     909           0 :                .WriteUint8(0x01) // command -- connect
     910           0 :                .WriteUint8(0x00); // reserved
     911             : 
     912             :     // We're writing a net port after the if, so we need a buffer allowing
     913             :     // to write that much.
     914           0 :     Buffer<sizeof(uint16_t)> buf2;
     915             :     // Add the address to the SOCKS 5 request. SOCKS 5 supports several
     916             :     // address types, so we pick the one that works best for us.
     917           0 :     if (proxy_resolve) {
     918             :         // Add the host name. Only a single byte is used to store the length,
     919             :         // so we must prevent long names from being used.
     920           0 :         buf2 = buf.WriteUint8(0x03) // addr type -- domainname
     921           0 :                   .WriteUint8(mDestinationHost.Length()) // name length
     922           0 :                   .WriteString<MAX_HOSTNAME_LEN>(mDestinationHost); // Hostname
     923           0 :         if (!buf2) {
     924           0 :             LOGERROR(("socks5: destination host name is too long!"));
     925           0 :             HandshakeFinished(PR_BAD_ADDRESS_ERROR);
     926           0 :             return PR_FAILURE;
     927             :         }
     928           0 :     } else if (addr->raw.family == AF_INET) {
     929           0 :         buf2 = buf.WriteUint8(0x01) // addr type -- IPv4
     930           0 :                   .WriteNetAddr(addr);
     931           0 :     } else if (addr->raw.family == AF_INET6) {
     932           0 :         buf2 = buf.WriteUint8(0x04) // addr type -- IPv6
     933           0 :                   .WriteNetAddr(addr);
     934             :     } else {
     935           0 :         LOGERROR(("socks5: destination address of unknown type!"));
     936           0 :         HandshakeFinished(PR_BAD_ADDRESS_ERROR);
     937           0 :         return PR_FAILURE;
     938             :     }
     939             : 
     940           0 :     auto buf3 = buf2.WriteNetPort(addr); // port
     941           0 :     mDataLength = buf3.Written();
     942             : 
     943           0 :     return PR_SUCCESS;
     944             : }
     945             : 
     946             : PRStatus
     947           0 : nsSOCKSSocketInfo::ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len)
     948             : {
     949           0 :     MOZ_ASSERT(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP ||
     950             :                mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
     951             :                "Invalid state!");
     952           0 :     MOZ_ASSERT(mDataLength >= 5,
     953             :                "SOCKS 5 connection reply must be at least 5 bytes!");
     954             : 
     955             :     // Seek to the address location
     956           0 :     mReadOffset = 3;
     957             : 
     958           0 :     *type = ReadUint8();
     959             : 
     960           0 :     switch (*type) {
     961             :         case 0x01: // ipv4
     962           0 :             *len = 4 - 1;
     963           0 :             break;
     964             :         case 0x04: // ipv6
     965           0 :             *len = 16 - 1;
     966           0 :             break;
     967             :         case 0x03: // fqdn
     968           0 :             *len = ReadUint8();
     969           0 :             break;
     970             :         default:   // wrong address type
     971           0 :             LOGERROR(("socks5: wrong address type in connection reply!"));
     972           0 :             return PR_FAILURE;
     973             :     }
     974             : 
     975           0 :     return PR_SUCCESS;
     976             : }
     977             : 
     978             : PRStatus
     979           0 : nsSOCKSSocketInfo::ReadV5ConnectResponseTop()
     980             : {
     981             :     uint8_t res;
     982             :     uint32_t len;
     983             : 
     984           0 :     MOZ_ASSERT(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP,
     985             :                "Invalid state!");
     986           0 :     MOZ_ASSERT(mDataLength == 5,
     987             :                "SOCKS 5 connection reply must be exactly 5 bytes!");
     988             : 
     989           0 :     LOGDEBUG(("socks5: checking connection reply"));
     990             : 
     991             :     // Check version number
     992           0 :     if (ReadUint8() != 0x05) {
     993           0 :         LOGERROR(("socks5: unexpected version in the reply"));
     994           0 :         HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
     995           0 :         return PR_FAILURE;
     996             :     }
     997             : 
     998             :     // Check response
     999           0 :     res = ReadUint8();
    1000           0 :     if (res != 0x00) {
    1001           0 :         PRErrorCode c = PR_CONNECT_REFUSED_ERROR;
    1002             : 
    1003           0 :         switch (res) {
    1004             :             case 0x01:
    1005           0 :                 LOGERROR(("socks5: connect failed: "
    1006             :                           "01, General SOCKS server failure."));
    1007           0 :                 break;
    1008             :             case 0x02:
    1009           0 :                 LOGERROR(("socks5: connect failed: "
    1010             :                           "02, Connection not allowed by ruleset."));
    1011           0 :                 break;
    1012             :             case 0x03:
    1013           0 :                 LOGERROR(("socks5: connect failed: 03, Network unreachable."));
    1014           0 :                 c = PR_NETWORK_UNREACHABLE_ERROR;
    1015           0 :                 break;
    1016             :             case 0x04:
    1017           0 :                 LOGERROR(("socks5: connect failed: 04, Host unreachable."));
    1018           0 :                 c = PR_BAD_ADDRESS_ERROR;
    1019           0 :                 break;
    1020             :             case 0x05:
    1021           0 :                 LOGERROR(("socks5: connect failed: 05, Connection refused."));
    1022           0 :                 break;
    1023             :             case 0x06:
    1024           0 :                 LOGERROR(("socks5: connect failed: 06, TTL expired."));
    1025           0 :                 c = PR_CONNECT_TIMEOUT_ERROR;
    1026           0 :                 break;
    1027             :             case 0x07:
    1028           0 :                 LOGERROR(("socks5: connect failed: "
    1029             :                           "07, Command not supported."));
    1030           0 :                 break;
    1031             :             case 0x08:
    1032           0 :                 LOGERROR(("socks5: connect failed: "
    1033             :                           "08, Address type not supported."));
    1034           0 :                 c = PR_BAD_ADDRESS_ERROR;
    1035           0 :                 break;
    1036             :             default:
    1037           0 :                 LOGERROR(("socks5: connect failed."));
    1038           0 :                 break;
    1039             :         }
    1040             : 
    1041           0 :         HandshakeFinished(c);
    1042           0 :         return PR_FAILURE;
    1043             :     }
    1044             : 
    1045           0 :     if (ReadV5AddrTypeAndLength(&res, &len) != PR_SUCCESS) {
    1046           0 :         HandshakeFinished(PR_BAD_ADDRESS_ERROR);
    1047           0 :         return PR_FAILURE;
    1048             :     }
    1049             : 
    1050           0 :     mState = SOCKS5_READ_CONNECT_RESPONSE_BOTTOM;
    1051           0 :     WantRead(len + 2);
    1052             : 
    1053           0 :     return PR_SUCCESS;
    1054             : }
    1055             : 
    1056             : PRStatus
    1057           0 : nsSOCKSSocketInfo::ReadV5ConnectResponseBottom()
    1058             : {
    1059             :     uint8_t type;
    1060             :     uint32_t len;
    1061             : 
    1062           0 :     MOZ_ASSERT(mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
    1063             :                "Invalid state!");
    1064             : 
    1065           0 :     if (ReadV5AddrTypeAndLength(&type, &len) != PR_SUCCESS) {
    1066           0 :         HandshakeFinished(PR_BAD_ADDRESS_ERROR);
    1067           0 :         return PR_FAILURE;
    1068             :     }
    1069             : 
    1070           0 :     MOZ_ASSERT(mDataLength == 7+len,
    1071             :                "SOCKS 5 unexpected length of connection reply!");
    1072             : 
    1073           0 :     LOGDEBUG(("socks5: loading source addr and port"));
    1074             :     // Read what the proxy says is our source address
    1075           0 :     switch (type) {
    1076             :         case 0x01: // ipv4
    1077           0 :             ReadNetAddr(&mExternalProxyAddr, AF_INET);
    1078           0 :             break;
    1079             :         case 0x04: // ipv6
    1080           0 :             ReadNetAddr(&mExternalProxyAddr, AF_INET6);
    1081           0 :             break;
    1082             :         case 0x03: // fqdn (skip)
    1083           0 :             mReadOffset += len;
    1084           0 :             mExternalProxyAddr.raw.family = AF_INET;
    1085           0 :             break;
    1086             :     }
    1087             : 
    1088           0 :     ReadNetPort(&mExternalProxyAddr);
    1089             : 
    1090           0 :     LOGDEBUG(("socks5: connected!"));
    1091           0 :     HandshakeFinished();
    1092             : 
    1093           0 :     return PR_SUCCESS;
    1094             : }
    1095             : 
    1096             : void
    1097           0 : nsSOCKSSocketInfo::SetConnectTimeout(PRIntervalTime to)
    1098             : {
    1099           0 :     mTimeout = to;
    1100           0 : }
    1101             : 
    1102             : PRStatus
    1103           0 : nsSOCKSSocketInfo::DoHandshake(PRFileDesc *fd, int16_t oflags)
    1104             : {
    1105           0 :     LOGDEBUG(("socks: DoHandshake(), state = %d", mState));
    1106             : 
    1107           0 :     switch (mState) {
    1108             :         case SOCKS_INITIAL:
    1109           0 :             if (IsLocalProxy()) {
    1110           0 :                 mState = SOCKS_DNS_COMPLETE;
    1111           0 :                 mLookupStatus = NS_OK;
    1112           0 :                 return ConnectToProxy(fd);
    1113             :             }
    1114             : 
    1115           0 :             return StartDNS(fd);
    1116             :         case SOCKS_DNS_IN_PROGRESS:
    1117           0 :             PR_SetError(PR_IN_PROGRESS_ERROR, 0);
    1118           0 :             return PR_FAILURE;
    1119             :         case SOCKS_DNS_COMPLETE:
    1120           0 :             return ConnectToProxy(fd);
    1121             :         case SOCKS_CONNECTING_TO_PROXY:
    1122           0 :             return ContinueConnectingToProxy(fd, oflags);
    1123             :         case SOCKS4_WRITE_CONNECT_REQUEST:
    1124           0 :             if (WriteToSocket(fd) != PR_SUCCESS)
    1125           0 :                 return PR_FAILURE;
    1126           0 :             WantRead(8);
    1127           0 :             mState = SOCKS4_READ_CONNECT_RESPONSE;
    1128           0 :             return PR_SUCCESS;
    1129             :         case SOCKS4_READ_CONNECT_RESPONSE:
    1130           0 :             if (ReadFromSocket(fd) != PR_SUCCESS)
    1131           0 :                 return PR_FAILURE;
    1132           0 :             return ReadV4ConnectResponse();
    1133             : 
    1134             :         case SOCKS5_WRITE_AUTH_REQUEST:
    1135           0 :             if (WriteToSocket(fd) != PR_SUCCESS)
    1136           0 :                 return PR_FAILURE;
    1137           0 :             WantRead(2);
    1138           0 :             mState = SOCKS5_READ_AUTH_RESPONSE;
    1139           0 :             return PR_SUCCESS;
    1140             :         case SOCKS5_READ_AUTH_RESPONSE:
    1141           0 :             if (ReadFromSocket(fd) != PR_SUCCESS)
    1142           0 :                 return PR_FAILURE;
    1143           0 :             return ReadV5AuthResponse();
    1144             :         case SOCKS5_WRITE_USERNAME_REQUEST:
    1145           0 :             if (WriteToSocket(fd) != PR_SUCCESS)
    1146           0 :                 return PR_FAILURE;
    1147           0 :             WantRead(2);
    1148           0 :             mState = SOCKS5_READ_USERNAME_RESPONSE;
    1149           0 :             return PR_SUCCESS;
    1150             :         case SOCKS5_READ_USERNAME_RESPONSE:
    1151           0 :             if (ReadFromSocket(fd) != PR_SUCCESS)
    1152           0 :                 return PR_FAILURE;
    1153           0 :             return ReadV5UsernameResponse();
    1154             :         case SOCKS5_WRITE_CONNECT_REQUEST:
    1155           0 :             if (WriteToSocket(fd) != PR_SUCCESS)
    1156           0 :                 return PR_FAILURE;
    1157             : 
    1158             :             // The SOCKS 5 response to the connection request is variable
    1159             :             // length. First, we'll read enough to tell how long the response
    1160             :             // is, and will read the rest later.
    1161           0 :             WantRead(5);
    1162           0 :             mState = SOCKS5_READ_CONNECT_RESPONSE_TOP;
    1163           0 :             return PR_SUCCESS;
    1164             :         case SOCKS5_READ_CONNECT_RESPONSE_TOP:
    1165           0 :             if (ReadFromSocket(fd) != PR_SUCCESS)
    1166           0 :                 return PR_FAILURE;
    1167           0 :             return ReadV5ConnectResponseTop();
    1168             :         case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
    1169           0 :             if (ReadFromSocket(fd) != PR_SUCCESS)
    1170           0 :                 return PR_FAILURE;
    1171           0 :             return ReadV5ConnectResponseBottom();
    1172             : 
    1173             :         case SOCKS_CONNECTED:
    1174           0 :             LOGERROR(("socks: already connected"));
    1175           0 :             HandshakeFinished(PR_IS_CONNECTED_ERROR);
    1176           0 :             return PR_FAILURE;
    1177             :         case SOCKS_FAILED:
    1178           0 :             LOGERROR(("socks: already failed"));
    1179           0 :             return PR_FAILURE;
    1180             :     }
    1181             : 
    1182           0 :     LOGERROR(("socks: executing handshake in invalid state, %d", mState));
    1183           0 :     HandshakeFinished(PR_INVALID_STATE_ERROR);
    1184             : 
    1185           0 :     return PR_FAILURE;
    1186             : }
    1187             : 
    1188             : int16_t
    1189           0 : nsSOCKSSocketInfo::GetPollFlags() const
    1190             : {
    1191           0 :     switch (mState) {
    1192             :         case SOCKS_DNS_IN_PROGRESS:
    1193             :         case SOCKS_DNS_COMPLETE:
    1194             :         case SOCKS_CONNECTING_TO_PROXY:
    1195           0 :             return PR_POLL_EXCEPT | PR_POLL_WRITE;
    1196             :         case SOCKS4_WRITE_CONNECT_REQUEST:
    1197             :         case SOCKS5_WRITE_AUTH_REQUEST:
    1198             :         case SOCKS5_WRITE_USERNAME_REQUEST:
    1199             :         case SOCKS5_WRITE_CONNECT_REQUEST:
    1200           0 :             return PR_POLL_WRITE;
    1201             :         case SOCKS4_READ_CONNECT_RESPONSE:
    1202             :         case SOCKS5_READ_AUTH_RESPONSE:
    1203             :         case SOCKS5_READ_USERNAME_RESPONSE:
    1204             :         case SOCKS5_READ_CONNECT_RESPONSE_TOP:
    1205             :         case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
    1206           0 :             return PR_POLL_READ;
    1207             :         default:
    1208           0 :             break;
    1209             :     }
    1210             : 
    1211           0 :     return 0;
    1212             : }
    1213             : 
    1214             : inline uint8_t
    1215           0 : nsSOCKSSocketInfo::ReadUint8()
    1216             : {
    1217             :     uint8_t rv;
    1218           0 :     MOZ_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
    1219             :                "Not enough space to pop a uint8_t!");
    1220           0 :     rv = mData[mReadOffset];
    1221           0 :     mReadOffset += sizeof(rv);
    1222           0 :     return rv;
    1223             : }
    1224             : 
    1225             : inline uint16_t
    1226           0 : nsSOCKSSocketInfo::ReadUint16()
    1227             : {
    1228             :     uint16_t rv;
    1229           0 :     MOZ_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
    1230             :                "Not enough space to pop a uint16_t!");
    1231           0 :     memcpy(&rv, mData + mReadOffset, sizeof(rv));
    1232           0 :     mReadOffset += sizeof(rv);
    1233           0 :     return rv;
    1234             : }
    1235             : 
    1236             : inline uint32_t
    1237             : nsSOCKSSocketInfo::ReadUint32()
    1238             : {
    1239             :     uint32_t rv;
    1240             :     MOZ_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
    1241             :                "Not enough space to pop a uint32_t!");
    1242             :     memcpy(&rv, mData + mReadOffset, sizeof(rv));
    1243             :     mReadOffset += sizeof(rv);
    1244             :     return rv;
    1245             : }
    1246             : 
    1247             : void
    1248           0 : nsSOCKSSocketInfo::ReadNetAddr(NetAddr *addr, uint16_t fam)
    1249             : {
    1250           0 :     uint32_t amt = 0;
    1251           0 :     const uint8_t *ip = mData + mReadOffset;
    1252             : 
    1253           0 :     addr->raw.family = fam;
    1254           0 :     if (fam == AF_INET) {
    1255           0 :         amt = sizeof(addr->inet.ip);
    1256           0 :         MOZ_ASSERT(mReadOffset + amt <= mDataLength,
    1257             :                    "Not enough space to pop an ipv4 addr!");
    1258           0 :         memcpy(&addr->inet.ip, ip, amt);
    1259           0 :     } else if (fam == AF_INET6) {
    1260           0 :         amt = sizeof(addr->inet6.ip.u8);
    1261           0 :         MOZ_ASSERT(mReadOffset + amt <= mDataLength,
    1262             :                    "Not enough space to pop an ipv6 addr!");
    1263           0 :         memcpy(addr->inet6.ip.u8, ip, amt);
    1264             :     }
    1265             : 
    1266           0 :     mReadOffset += amt;
    1267           0 : }
    1268             : 
    1269             : void
    1270           0 : nsSOCKSSocketInfo::ReadNetPort(NetAddr *addr)
    1271             : {
    1272           0 :     addr->inet.port = ReadUint16();
    1273           0 : }
    1274             : 
    1275             : void
    1276           0 : nsSOCKSSocketInfo::WantRead(uint32_t sz)
    1277             : {
    1278           0 :     MOZ_ASSERT(mDataIoPtr == nullptr,
    1279             :                "WantRead() called while I/O already in progress!");
    1280           0 :     MOZ_ASSERT(mDataLength + sz <= BUFFER_SIZE,
    1281             :                "Can't read that much data!");
    1282           0 :     mAmountToRead = sz;
    1283           0 : }
    1284             : 
    1285             : PRStatus
    1286           0 : nsSOCKSSocketInfo::ReadFromSocket(PRFileDesc *fd)
    1287             : {
    1288             :     int32_t rc;
    1289             :     const uint8_t *end;
    1290             : 
    1291           0 :     if (!mAmountToRead) {
    1292           0 :         LOGDEBUG(("socks: ReadFromSocket(), nothing to do"));
    1293           0 :         return PR_SUCCESS;
    1294             :     }
    1295             : 
    1296           0 :     if (!mDataIoPtr) {
    1297           0 :         mDataIoPtr = mData + mDataLength;
    1298           0 :         mDataLength += mAmountToRead;
    1299             :     }
    1300             : 
    1301           0 :     end = mData + mDataLength;
    1302             : 
    1303           0 :     while (mDataIoPtr < end) {
    1304           0 :         rc = PR_Read(fd, mDataIoPtr, end - mDataIoPtr);
    1305           0 :         if (rc <= 0) {
    1306           0 :             if (rc == 0) {
    1307           0 :                 LOGERROR(("socks: proxy server closed connection"));
    1308           0 :                 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
    1309           0 :                 return PR_FAILURE;
    1310           0 :             } else if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
    1311           0 :                 LOGDEBUG(("socks: ReadFromSocket(), want read"));
    1312             :             }
    1313           0 :             break;
    1314             :         }
    1315             : 
    1316           0 :         mDataIoPtr += rc;
    1317             :     }
    1318             : 
    1319           0 :     LOGDEBUG(("socks: ReadFromSocket(), have %u bytes total",
    1320             :              unsigned(mDataIoPtr - mData)));
    1321           0 :     if (mDataIoPtr == end) {
    1322           0 :         mDataIoPtr = nullptr;
    1323           0 :         mAmountToRead = 0;
    1324           0 :         mReadOffset = 0;
    1325           0 :         return PR_SUCCESS;
    1326             :     }
    1327             : 
    1328           0 :     return PR_FAILURE;
    1329             : }
    1330             : 
    1331             : PRStatus
    1332           0 : nsSOCKSSocketInfo::WriteToSocket(PRFileDesc *fd)
    1333             : {
    1334             :     int32_t rc;
    1335             :     const uint8_t *end;
    1336             : 
    1337           0 :     if (!mDataLength) {
    1338           0 :         LOGDEBUG(("socks: WriteToSocket(), nothing to do"));
    1339           0 :         return PR_SUCCESS;
    1340             :     }
    1341             : 
    1342           0 :     if (!mDataIoPtr)
    1343           0 :         mDataIoPtr = mData;
    1344             : 
    1345           0 :     end = mData + mDataLength;
    1346             : 
    1347           0 :     while (mDataIoPtr < end) {
    1348           0 :         rc = PR_Write(fd, mDataIoPtr, end - mDataIoPtr);
    1349           0 :         if (rc < 0) {
    1350           0 :             if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
    1351           0 :                 LOGDEBUG(("socks: WriteToSocket(), want write"));
    1352             :             }
    1353           0 :             break;
    1354             :         }
    1355             : 
    1356           0 :         mDataIoPtr += rc;
    1357             :     }
    1358             : 
    1359           0 :     if (mDataIoPtr == end) {
    1360           0 :         mDataIoPtr = nullptr;
    1361           0 :         mDataLength = 0;
    1362           0 :         mReadOffset = 0;
    1363           0 :         return PR_SUCCESS;
    1364             :     }
    1365             : 
    1366           0 :     return PR_FAILURE;
    1367             : }
    1368             : 
    1369             : static PRStatus
    1370           0 : nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime to)
    1371             : {
    1372             :     PRStatus status;
    1373             :     NetAddr dst;
    1374             : 
    1375           0 :     nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
    1376           0 :     if (info == nullptr) return PR_FAILURE;
    1377             : 
    1378           0 :     if (addr->raw.family == PR_AF_INET6 &&
    1379           0 :         PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
    1380             :         const uint8_t *srcp;
    1381             : 
    1382           0 :         LOGDEBUG(("socks: converting ipv4-mapped ipv6 address to ipv4"));
    1383             : 
    1384             :         // copied from _PR_ConvertToIpv4NetAddr()
    1385           0 :         dst.raw.family = AF_INET;
    1386           0 :         dst.inet.ip = htonl(INADDR_ANY);
    1387           0 :         dst.inet.port = htons(0);
    1388           0 :         srcp = addr->ipv6.ip.pr_s6_addr;
    1389           0 :         memcpy(&dst.inet.ip, srcp + 12, 4);
    1390           0 :         dst.inet.family = AF_INET;
    1391           0 :         dst.inet.port = addr->ipv6.port;
    1392             :     } else {
    1393           0 :         memcpy(&dst, addr, sizeof(dst));
    1394             :     }
    1395             : 
    1396           0 :     info->SetDestinationAddr(&dst);
    1397           0 :     info->SetConnectTimeout(to);
    1398             : 
    1399           0 :     do {
    1400           0 :         status = info->DoHandshake(fd, -1);
    1401           0 :     } while (status == PR_SUCCESS && !info->IsConnected());
    1402             : 
    1403           0 :     return status;
    1404             : }
    1405             : 
    1406             : static PRStatus
    1407           0 : nsSOCKSIOLayerConnectContinue(PRFileDesc *fd, int16_t oflags)
    1408             : {
    1409             :     PRStatus status;
    1410             : 
    1411           0 :     nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
    1412           0 :     if (info == nullptr) return PR_FAILURE;
    1413             : 
    1414           0 :     do {
    1415           0 :         status = info->DoHandshake(fd, oflags);
    1416           0 :     } while (status == PR_SUCCESS && !info->IsConnected());
    1417             : 
    1418           0 :     return status;
    1419             : }
    1420             : 
    1421             : static int16_t
    1422           0 : nsSOCKSIOLayerPoll(PRFileDesc *fd, int16_t in_flags, int16_t *out_flags)
    1423             : {
    1424           0 :     nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
    1425           0 :     if (info == nullptr) return PR_FAILURE;
    1426             : 
    1427           0 :     if (!info->IsConnected()) {
    1428           0 :         *out_flags = 0;
    1429           0 :         return info->GetPollFlags();
    1430             :     }
    1431             : 
    1432           0 :     return fd->lower->methods->poll(fd->lower, in_flags, out_flags);
    1433             : }
    1434             : 
    1435             : static PRStatus
    1436           0 : nsSOCKSIOLayerClose(PRFileDesc *fd)
    1437             : {
    1438           0 :     nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
    1439           0 :     PRDescIdentity id = PR_GetLayersIdentity(fd);
    1440             : 
    1441           0 :     if (info && id == nsSOCKSIOLayerIdentity)
    1442             :     {
    1443           0 :         info->ForgetFD();
    1444           0 :         NS_RELEASE(info);
    1445           0 :         fd->identity = PR_INVALID_IO_LAYER;
    1446             :     }
    1447             : 
    1448           0 :     return fd->lower->methods->close(fd->lower);
    1449             : }
    1450             : 
    1451             : static PRFileDesc*
    1452           0 : nsSOCKSIOLayerAccept(PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
    1453             : {
    1454             :     // TODO: implement SOCKS support for accept
    1455           0 :     return fd->lower->methods->accept(fd->lower, addr, timeout);
    1456             : }
    1457             : 
    1458             : static int32_t
    1459           0 : nsSOCKSIOLayerAcceptRead(PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf, int32_t amount, PRIntervalTime timeout)
    1460             : {
    1461             :     // TODO: implement SOCKS support for accept, then read from it
    1462           0 :     return sd->lower->methods->acceptread(sd->lower, nd, raddr, buf, amount, timeout);
    1463             : }
    1464             : 
    1465             : static PRStatus
    1466           0 : nsSOCKSIOLayerBind(PRFileDesc *fd, const PRNetAddr *addr)
    1467             : {
    1468             :     // TODO: implement SOCKS support for bind (very similar to connect)
    1469           0 :     return fd->lower->methods->bind(fd->lower, addr);
    1470             : }
    1471             : 
    1472             : static PRStatus
    1473           0 : nsSOCKSIOLayerGetName(PRFileDesc *fd, PRNetAddr *addr)
    1474             : {
    1475           0 :     nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
    1476             : 
    1477           0 :     if (info != nullptr && addr != nullptr) {
    1478             :         NetAddr temp;
    1479           0 :         NetAddr *tempPtr = &temp;
    1480           0 :         if (info->GetExternalProxyAddr(&tempPtr) == NS_OK) {
    1481           0 :             NetAddrToPRNetAddr(tempPtr, addr);
    1482           0 :             return PR_SUCCESS;
    1483             :         }
    1484             :     }
    1485             : 
    1486           0 :     return PR_FAILURE;
    1487             : }
    1488             : 
    1489             : static PRStatus
    1490           0 : nsSOCKSIOLayerGetPeerName(PRFileDesc *fd, PRNetAddr *addr)
    1491             : {
    1492           0 :     nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
    1493             : 
    1494           0 :     if (info != nullptr && addr != nullptr) {
    1495             :         NetAddr temp;
    1496           0 :         NetAddr *tempPtr = &temp;
    1497           0 :         if (info->GetDestinationAddr(&tempPtr) == NS_OK) {
    1498           0 :             NetAddrToPRNetAddr(tempPtr, addr);
    1499           0 :             return PR_SUCCESS;
    1500             :         }
    1501             :     }
    1502             : 
    1503           0 :     return PR_FAILURE;
    1504             : }
    1505             : 
    1506             : static PRStatus
    1507           0 : nsSOCKSIOLayerListen(PRFileDesc *fd, int backlog)
    1508             : {
    1509             :     // TODO: implement SOCKS support for listen
    1510           0 :     return fd->lower->methods->listen(fd->lower, backlog);
    1511             : }
    1512             : 
    1513             : // add SOCKS IO layer to an existing socket
    1514             : nsresult
    1515           0 : nsSOCKSIOLayerAddToSocket(int32_t family,
    1516             :                           const char *host,
    1517             :                           int32_t port,
    1518             :                           nsIProxyInfo *proxy,
    1519             :                           int32_t socksVersion,
    1520             :                           uint32_t flags,
    1521             :                           PRFileDesc *fd,
    1522             :                           nsISupports** info)
    1523             : {
    1524           0 :     NS_ENSURE_TRUE((socksVersion == 4) || (socksVersion == 5), NS_ERROR_NOT_INITIALIZED);
    1525             : 
    1526             : 
    1527           0 :     if (firstTime)
    1528             :     {
    1529             :         //XXX hack until NSPR provides an official way to detect system IPv6
    1530             :         // support (bug 388519)
    1531           0 :         PRFileDesc *tmpfd = PR_OpenTCPSocket(PR_AF_INET6);
    1532           0 :         if (!tmpfd) {
    1533           0 :             ipv6Supported = false;
    1534             :         } else {
    1535             :             // If the system does not support IPv6, NSPR will push
    1536             :             // IPv6-to-IPv4 emulation layer onto the native layer
    1537           0 :             ipv6Supported = PR_GetIdentitiesLayer(tmpfd, PR_NSPR_IO_LAYER) == tmpfd;
    1538           0 :             PR_Close(tmpfd);
    1539             :         }
    1540             : 
    1541           0 :         nsSOCKSIOLayerIdentity = PR_GetUniqueIdentity("SOCKS layer");
    1542           0 :         nsSOCKSIOLayerMethods = *PR_GetDefaultIOMethods();
    1543             : 
    1544           0 :         nsSOCKSIOLayerMethods.connect = nsSOCKSIOLayerConnect;
    1545           0 :         nsSOCKSIOLayerMethods.connectcontinue = nsSOCKSIOLayerConnectContinue;
    1546           0 :         nsSOCKSIOLayerMethods.poll = nsSOCKSIOLayerPoll;
    1547           0 :         nsSOCKSIOLayerMethods.bind = nsSOCKSIOLayerBind;
    1548           0 :         nsSOCKSIOLayerMethods.acceptread = nsSOCKSIOLayerAcceptRead;
    1549           0 :         nsSOCKSIOLayerMethods.getsockname = nsSOCKSIOLayerGetName;
    1550           0 :         nsSOCKSIOLayerMethods.getpeername = nsSOCKSIOLayerGetPeerName;
    1551           0 :         nsSOCKSIOLayerMethods.accept = nsSOCKSIOLayerAccept;
    1552           0 :         nsSOCKSIOLayerMethods.listen = nsSOCKSIOLayerListen;
    1553           0 :         nsSOCKSIOLayerMethods.close = nsSOCKSIOLayerClose;
    1554             : 
    1555           0 :         firstTime = false;
    1556             :     }
    1557             : 
    1558           0 :     LOGDEBUG(("Entering nsSOCKSIOLayerAddToSocket()."));
    1559             : 
    1560             :     PRFileDesc *layer;
    1561             :     PRStatus rv;
    1562             : 
    1563           0 :     layer = PR_CreateIOLayerStub(nsSOCKSIOLayerIdentity, &nsSOCKSIOLayerMethods);
    1564           0 :     if (! layer)
    1565             :     {
    1566           0 :         LOGERROR(("PR_CreateIOLayerStub() failed."));
    1567           0 :         return NS_ERROR_FAILURE;
    1568             :     }
    1569             : 
    1570           0 :     nsSOCKSSocketInfo * infoObject = new nsSOCKSSocketInfo();
    1571           0 :     if (!infoObject)
    1572             :     {
    1573             :         // clean up IOLayerStub
    1574           0 :         LOGERROR(("Failed to create nsSOCKSSocketInfo()."));
    1575           0 :         PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
    1576           0 :         return NS_ERROR_FAILURE;
    1577             :     }
    1578             : 
    1579           0 :     NS_ADDREF(infoObject);
    1580           0 :     infoObject->Init(socksVersion, family, proxy, host, flags);
    1581           0 :     layer->secret = (PRFilePrivate*) infoObject;
    1582             : 
    1583           0 :     PRDescIdentity fdIdentity = PR_GetLayersIdentity(fd);
    1584             : #if defined(XP_WIN)
    1585             :     if (fdIdentity == mozilla::net::nsNamedPipeLayerIdentity) {
    1586             :         // remember named pipe fd on the info object so that we can switch
    1587             :         // blocking and non-blocking mode on the pipe later.
    1588             :         infoObject->SetNamedPipeFD(fd);
    1589             :     }
    1590             : #endif
    1591           0 :     rv = PR_PushIOLayer(fd, fdIdentity, layer);
    1592             : 
    1593           0 :     if (rv == PR_FAILURE) {
    1594           0 :         LOGERROR(("PR_PushIOLayer() failed. rv = %x.", rv));
    1595           0 :         NS_RELEASE(infoObject);
    1596           0 :         PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
    1597           0 :         return NS_ERROR_FAILURE;
    1598             :     }
    1599             : 
    1600           0 :     *info = static_cast<nsISOCKSSocketInfo*>(infoObject);
    1601           0 :     NS_ADDREF(*info);
    1602           0 :     return NS_OK;
    1603             : }
    1604             : 
    1605             : bool
    1606           0 : IsHostLocalTarget(const nsACString& aHost)
    1607             : {
    1608             : #if defined(XP_UNIX)
    1609           0 :     return StringBeginsWith(aHost, NS_LITERAL_CSTRING("file:"));
    1610             : #elif defined(XP_WIN)
    1611             :     return IsNamedPipePath(aHost);
    1612             : #else
    1613             :     return false;
    1614             : #endif // XP_UNIX
    1615             : }

Generated by: LCOV version 1.13