tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsSOCKSIOLayer.cpp (47741B)


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