tor-browser

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

nsSocketTransport2.cpp (110253B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=4 sw=2 et cindent: */
      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 <algorithm>
      8 
      9 #include "nsSocketTransport2.h"
     10 
     11 #include "MockNetworkLayer.h"
     12 #include "MockNetworkLayerController.h"
     13 #include "NSSErrorsService.h"
     14 #include "NetworkDataCountLayer.h"
     15 #include "QuicSocketControl.h"
     16 #include "mozilla/StaticPrefs_network.h"
     17 #include "mozilla/SyncRunnable.h"
     18 #include "mozilla/glean/NetwerkMetrics.h"
     19 #include "mozilla/Telemetry.h"
     20 #include "mozilla/dom/ToJSValue.h"
     21 #include "mozilla/net/NeckoChild.h"
     22 #include "mozilla/net/SSLTokensCache.h"
     23 #include "mozilla/ProfilerBandwidthCounter.h"
     24 #include "nsCOMPtr.h"
     25 #include "nsICancelable.h"
     26 #include "nsIClassInfoImpl.h"
     27 #include "nsIDNSByTypeRecord.h"
     28 #include "nsIDNSRecord.h"
     29 #include "nsIDNSService.h"
     30 #include "nsIOService.h"
     31 #include "nsIPipe.h"
     32 #include "nsISocketProvider.h"
     33 #include "nsITLSSocketControl.h"
     34 #include "nsNetAddr.h"
     35 #include "nsNetCID.h"
     36 #include "nsNetSegmentUtils.h"
     37 #include "nsNetUtil.h"
     38 #include "nsPrintfCString.h"
     39 #include "nsProxyInfo.h"
     40 #include "nsSocketProviderService.h"
     41 #include "nsStreamUtils.h"
     42 #include "nsThreadUtils.h"
     43 #include "nsTransportUtils.h"
     44 #include "nsURLHelper.h"
     45 #include "prerr.h"
     46 #include "sslexp.h"
     47 #include "xpcpublic.h"
     48 
     49 #if defined(FUZZING)
     50 #  include "FuzzyLayer.h"
     51 #  include "FuzzySocketControl.h"
     52 #  include "mozilla/StaticPrefs_fuzzing.h"
     53 #endif
     54 
     55 #if defined(XP_WIN)
     56 #  include "ShutdownLayer.h"
     57 #endif
     58 
     59 /* Following inclusions required for keepalive config not supported by NSPR. */
     60 #include "private/pprio.h"
     61 #if defined(XP_WIN)
     62 #  include <winsock2.h>
     63 #  include <mstcpip.h>
     64 #elif defined(XP_UNIX)
     65 #  include <errno.h>
     66 #  include <netinet/tcp.h>
     67 #endif
     68 /* End keepalive config inclusions. */
     69 
     70 #define SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS 0
     71 #define UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS 1
     72 #define SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS 2
     73 #define UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS 3
     74 
     75 //-----------------------------------------------------------------------------
     76 
     77 static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
     78 
     79 //-----------------------------------------------------------------------------
     80 
     81 namespace mozilla {
     82 namespace net {
     83 
     84 class nsSocketEvent : public Runnable {
     85 public:
     86  nsSocketEvent(nsSocketTransport* transport, uint32_t type,
     87                nsresult status = NS_OK, nsISupports* param = nullptr,
     88                std::function<void()>&& task = nullptr)
     89      : Runnable("net::nsSocketEvent"),
     90        mTransport(transport),
     91        mType(type),
     92        mStatus(status),
     93        mParam(param),
     94        mTask(std::move(task)) {}
     95 
     96  NS_IMETHOD Run() override {
     97    mTransport->OnSocketEvent(mType, mStatus, mParam, std::move(mTask));
     98    return NS_OK;
     99  }
    100 
    101 private:
    102  RefPtr<nsSocketTransport> mTransport;
    103 
    104  uint32_t mType;
    105  nsresult mStatus;
    106  nsCOMPtr<nsISupports> mParam;
    107  std::function<void()> mTask;
    108 };
    109 
    110 //-----------------------------------------------------------------------------
    111 
    112 // #define TEST_CONNECT_ERRORS
    113 #ifdef TEST_CONNECT_ERRORS
    114 #  include <stdlib.h>
    115 static PRErrorCode RandomizeConnectError(PRErrorCode code) {
    116  //
    117  // To test out these errors, load http://www.yahoo.com/.  It should load
    118  // correctly despite the random occurrence of these errors.
    119  //
    120  int n = rand();
    121  if (n > RAND_MAX / 2) {
    122    struct {
    123      PRErrorCode err_code;
    124      const char* err_name;
    125    } errors[] = {
    126        //
    127        // These errors should be recoverable provided there is another
    128        // IP address in mDNSRecord.
    129        //
    130        {PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR"},
    131        {PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR"},
    132        //
    133        // This error will cause this socket transport to error out;
    134        // however, if the consumer is HTTP, then the HTTP transaction
    135        // should be restarted when this error occurs.
    136        //
    137        {PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR"},
    138    };
    139    n = n % (sizeof(errors) / sizeof(errors[0]));
    140    code = errors[n].err_code;
    141    SOCKET_LOG(("simulating NSPR error %d [%s]\n", code, errors[n].err_name));
    142  }
    143  return code;
    144 }
    145 #endif
    146 
    147 //-----------------------------------------------------------------------------
    148 
    149 nsresult ErrorAccordingToNSPR(PRErrorCode errorCode) {
    150  nsresult rv = NS_ERROR_FAILURE;
    151  switch (errorCode) {
    152    case PR_WOULD_BLOCK_ERROR:
    153      rv = NS_BASE_STREAM_WOULD_BLOCK;
    154      break;
    155    case PR_CONNECT_ABORTED_ERROR:
    156    case PR_CONNECT_RESET_ERROR:
    157      rv = NS_ERROR_NET_RESET;
    158      break;
    159    case PR_END_OF_FILE_ERROR:  // XXX document this correlation
    160      rv = NS_ERROR_NET_INTERRUPT;
    161      break;
    162    case PR_CONNECT_REFUSED_ERROR:
    163    // We lump the following NSPR codes in with PR_CONNECT_REFUSED_ERROR. We
    164    // could get better diagnostics by adding distinct XPCOM error codes for
    165    // each of these, but there are a lot of places in Gecko that check
    166    // specifically for NS_ERROR_CONNECTION_REFUSED, all of which would need to
    167    // be checked.
    168    case PR_NETWORK_UNREACHABLE_ERROR:
    169    case PR_HOST_UNREACHABLE_ERROR:
    170    case PR_ADDRESS_NOT_AVAILABLE_ERROR:
    171    // Treat EACCES as a soft error since (at least on Linux) connect() returns
    172    // EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
    173    case PR_NO_ACCESS_RIGHTS_ERROR:
    174      rv = NS_ERROR_CONNECTION_REFUSED;
    175      break;
    176    case PR_ADDRESS_NOT_SUPPORTED_ERROR:
    177      rv = NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
    178      break;
    179    case PR_IO_TIMEOUT_ERROR:
    180    case PR_CONNECT_TIMEOUT_ERROR:
    181      rv = NS_ERROR_NET_TIMEOUT;
    182      break;
    183    case PR_OUT_OF_MEMORY_ERROR:
    184    // These really indicate that the descriptor table filled up, or that the
    185    // kernel ran out of network buffers - but nobody really cares which part of
    186    // the system ran out of memory.
    187    case PR_PROC_DESC_TABLE_FULL_ERROR:
    188    case PR_SYS_DESC_TABLE_FULL_ERROR:
    189    case PR_INSUFFICIENT_RESOURCES_ERROR:
    190      rv = NS_ERROR_OUT_OF_MEMORY;
    191      break;
    192    case PR_ADDRESS_IN_USE_ERROR:
    193      rv = NS_ERROR_SOCKET_ADDRESS_IN_USE;
    194      break;
    195    // These filename-related errors can arise when using Unix-domain sockets.
    196    case PR_FILE_NOT_FOUND_ERROR:
    197      rv = NS_ERROR_FILE_NOT_FOUND;
    198      break;
    199    case PR_IS_DIRECTORY_ERROR:
    200      rv = NS_ERROR_FILE_IS_DIRECTORY;
    201      break;
    202    case PR_LOOP_ERROR:
    203      rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
    204      break;
    205    case PR_NAME_TOO_LONG_ERROR:
    206      rv = NS_ERROR_FILE_NAME_TOO_LONG;
    207      break;
    208    case PR_NO_DEVICE_SPACE_ERROR:
    209      rv = NS_ERROR_FILE_NO_DEVICE_SPACE;
    210      break;
    211    case PR_NOT_DIRECTORY_ERROR:
    212      rv = NS_ERROR_FILE_NOT_DIRECTORY;
    213      break;
    214    case PR_READ_ONLY_FILESYSTEM_ERROR:
    215      rv = NS_ERROR_FILE_READ_ONLY;
    216      break;
    217    case PR_BAD_ADDRESS_ERROR:
    218      rv = NS_ERROR_UNKNOWN_HOST;
    219      break;
    220    default:
    221      if (psm::IsNSSErrorCode(errorCode)) {
    222        rv = psm::GetXPCOMFromNSSError(errorCode);
    223      } else {
    224        // If we received a Tor extended error code via SOCKS, pass it through.
    225        nsresult res = nsresult(errorCode);
    226        if (NS_ERROR_GET_MODULE(res) == NS_ERROR_MODULE_TOR) {
    227          rv = res;
    228        }
    229      }
    230      break;
    231 
    232      // NSPR's socket code can return these, but they're not worth breaking out
    233      // into their own error codes, distinct from NS_ERROR_FAILURE:
    234      //
    235      // PR_BAD_DESCRIPTOR_ERROR
    236      // PR_INVALID_ARGUMENT_ERROR
    237      // PR_NOT_SOCKET_ERROR
    238      // PR_NOT_TCP_SOCKET_ERROR
    239      //   These would indicate a bug internal to the component.
    240      //
    241      // PR_PROTOCOL_NOT_SUPPORTED_ERROR
    242      //   This means that we can't use the given "protocol" (like
    243      //   IPPROTO_TCP or IPPROTO_UDP) with a socket of the given type. As
    244      //   above, this indicates an internal bug.
    245      //
    246      // PR_IS_CONNECTED_ERROR
    247      //   This indicates that we've applied a system call like 'bind' or
    248      //   'connect' to a socket that is already connected. The socket
    249      //   components manage each file descriptor's state, and in some cases
    250      //   handle this error result internally. We shouldn't be returning
    251      //   this to our callers.
    252      //
    253      // PR_IO_ERROR
    254      //   This is so vague that NS_ERROR_FAILURE is just as good.
    255  }
    256  SOCKET_LOG(("ErrorAccordingToNSPR [in=%d out=%" PRIx32 "]\n", errorCode,
    257              static_cast<uint32_t>(rv)));
    258  return rv;
    259 }
    260 
    261 //-----------------------------------------------------------------------------
    262 // socket input stream impl
    263 //-----------------------------------------------------------------------------
    264 
    265 nsSocketInputStream::nsSocketInputStream(nsSocketTransport* trans)
    266    : mTransport(trans) {}
    267 
    268 // called on the socket transport thread...
    269 //
    270 //   condition : failure code if socket has been closed
    271 //
    272 void nsSocketInputStream::OnSocketReady(nsresult condition) {
    273  SOCKET_LOG(("nsSocketInputStream::OnSocketReady [this=%p cond=%" PRIx32 "]\n",
    274              this, static_cast<uint32_t>(condition)));
    275 
    276  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    277 
    278  nsCOMPtr<nsIInputStreamCallback> callback;
    279  {
    280    MutexAutoLock lock(mTransport->mLock);
    281 
    282    // update condition, but be careful not to erase an already
    283    // existing error condition.
    284    if (NS_SUCCEEDED(mCondition)) mCondition = condition;
    285 
    286    // ignore event if only waiting for closure and not closed.
    287    if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
    288      callback = std::move(mCallback);
    289      mCallbackFlags = 0;
    290    }
    291  }
    292 
    293  if (callback) callback->OnInputStreamReady(this);
    294 }
    295 
    296 NS_IMPL_QUERY_INTERFACE(nsSocketInputStream, nsIInputStream,
    297                        nsIAsyncInputStream)
    298 
    299 NS_IMETHODIMP_(MozExternalRefCountType)
    300 nsSocketInputStream::AddRef() {
    301  ++mReaderRefCnt;
    302  return mTransport->AddRef();
    303 }
    304 
    305 NS_IMETHODIMP_(MozExternalRefCountType)
    306 nsSocketInputStream::Release() {
    307  if (--mReaderRefCnt == 0) Close();
    308  return mTransport->Release();
    309 }
    310 
    311 NS_IMETHODIMP
    312 nsSocketInputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED); }
    313 
    314 NS_IMETHODIMP
    315 nsSocketInputStream::Available(uint64_t* avail) {
    316  SOCKET_LOG(("nsSocketInputStream::Available [this=%p]\n", this));
    317 
    318  *avail = 0;
    319 
    320  PRFileDesc* fd;
    321  {
    322    MutexAutoLock lock(mTransport->mLock);
    323 
    324    if (NS_FAILED(mCondition)) return mCondition;
    325 
    326    fd = mTransport->GetFD_Locked();
    327    if (!fd) return NS_OK;
    328  }
    329 
    330  // cannot hold lock while calling NSPR.  (worried about the fact that PSM
    331  // synchronously proxies notifications over to the UI thread, which could
    332  // mistakenly try to re-enter this code.)
    333  int32_t n = PR_Available(fd);
    334 
    335  // PSM does not implement PR_Available() so do a best approximation of it
    336  // with MSG_PEEK
    337  if ((n == -1) && (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR)) {
    338    char c;
    339 
    340    n = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
    341    SOCKET_LOG(
    342        ("nsSocketInputStream::Available [this=%p] "
    343         "using PEEK backup n=%d]\n",
    344         this, n));
    345  }
    346 
    347  nsresult rv;
    348  {
    349    MutexAutoLock lock(mTransport->mLock);
    350 
    351    mTransport->ReleaseFD_Locked(fd);
    352 
    353    if (n >= 0) {
    354      *avail = n;
    355    } else {
    356      PRErrorCode code = PR_GetError();
    357      if (code == PR_WOULD_BLOCK_ERROR) return NS_OK;
    358      mCondition = ErrorAccordingToNSPR(code);
    359    }
    360    rv = mCondition;
    361  }
    362  if (NS_FAILED(rv)) mTransport->OnInputClosed(rv);
    363  return rv;
    364 }
    365 
    366 NS_IMETHODIMP
    367 nsSocketInputStream::StreamStatus() {
    368  SOCKET_LOG(("nsSocketInputStream::StreamStatus [this=%p]\n", this));
    369 
    370  MutexAutoLock lock(mTransport->mLock);
    371  return mCondition;
    372 }
    373 
    374 NS_IMETHODIMP
    375 nsSocketInputStream::Read(char* buf, uint32_t count, uint32_t* countRead) {
    376  SOCKET_LOG(("nsSocketInputStream::Read [this=%p count=%u]\n", this, count));
    377 
    378  *countRead = 0;
    379 
    380  PRFileDesc* fd = nullptr;
    381  {
    382    MutexAutoLock lock(mTransport->mLock);
    383 
    384    if (NS_FAILED(mCondition)) {
    385      return (mCondition == NS_BASE_STREAM_CLOSED) ? NS_OK : mCondition;
    386    }
    387 
    388    fd = mTransport->GetFD_Locked();
    389    if (!fd) return NS_BASE_STREAM_WOULD_BLOCK;
    390  }
    391 
    392  SOCKET_LOG(("  calling PR_Read [count=%u]\n", count));
    393 
    394  // cannot hold lock while calling NSPR.  (worried about the fact that PSM
    395  // synchronously proxies notifications over to the UI thread, which could
    396  // mistakenly try to re-enter this code.)
    397  int32_t n = PR_Read(fd, buf, count);
    398 
    399  SOCKET_LOG(("  PR_Read returned [n=%d]\n", n));
    400 
    401  nsresult rv = NS_OK;
    402  {
    403    MutexAutoLock lock(mTransport->mLock);
    404 
    405 #ifdef ENABLE_SOCKET_TRACING
    406    if (n > 0) mTransport->TraceInBuf(buf, n);
    407 #endif
    408 
    409    mTransport->ReleaseFD_Locked(fd);
    410 
    411    if (n > 0) {
    412      mByteCount += (*countRead = n);
    413      profiler_count_bandwidth_read_bytes(n);
    414    } else if (n < 0) {
    415      PRErrorCode code = PR_GetError();
    416      if (code == PR_WOULD_BLOCK_ERROR) return NS_BASE_STREAM_WOULD_BLOCK;
    417      mCondition = ErrorAccordingToNSPR(code);
    418    }
    419    rv = mCondition;
    420  }
    421  if (NS_FAILED(rv)) mTransport->OnInputClosed(rv);
    422 
    423  // only send this notification if we have indeed read some data.
    424  // see bug 196827 for an example of why this is important.
    425  if (n > 0) mTransport->SendStatus(NS_NET_STATUS_RECEIVING_FROM);
    426  return rv;
    427 }
    428 
    429 NS_IMETHODIMP
    430 nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer, void* closure,
    431                                  uint32_t count, uint32_t* countRead) {
    432  // socket stream is unbuffered
    433  return NS_ERROR_NOT_IMPLEMENTED;
    434 }
    435 
    436 NS_IMETHODIMP
    437 nsSocketInputStream::IsNonBlocking(bool* nonblocking) {
    438  *nonblocking = true;
    439  return NS_OK;
    440 }
    441 
    442 NS_IMETHODIMP
    443 nsSocketInputStream::CloseWithStatus(nsresult reason) {
    444  SOCKET_LOG(("nsSocketInputStream::CloseWithStatus [this=%p reason=%" PRIx32
    445              "]\n",
    446              this, static_cast<uint32_t>(reason)));
    447 
    448  // may be called from any thread
    449 
    450  nsresult rv;
    451  {
    452    MutexAutoLock lock(mTransport->mLock);
    453 
    454    if (NS_SUCCEEDED(mCondition)) {
    455      rv = mCondition = reason;
    456    } else {
    457      rv = NS_OK;
    458    }
    459  }
    460  if (NS_FAILED(rv)) mTransport->OnInputClosed(rv);
    461  return NS_OK;
    462 }
    463 
    464 NS_IMETHODIMP
    465 nsSocketInputStream::AsyncWait(nsIInputStreamCallback* callback, uint32_t flags,
    466                               uint32_t amount, nsIEventTarget* target) {
    467  SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%p]\n", this));
    468 
    469  bool hasError = false;
    470  {
    471    MutexAutoLock lock(mTransport->mLock);
    472 
    473    if (callback && target) {
    474      //
    475      // build event proxy
    476      //
    477      mCallback = NS_NewInputStreamReadyEvent("nsSocketInputStream::AsyncWait",
    478                                              callback, target);
    479    } else {
    480      mCallback = callback;
    481    }
    482    mCallbackFlags = flags;
    483 
    484    hasError = NS_FAILED(mCondition);
    485  }  // unlock mTransport->mLock
    486 
    487  if (hasError) {
    488    // OnSocketEvent will call OnInputStreamReady with an error code after
    489    // going through the event loop. We do this because most socket callers
    490    // do not expect AsyncWait() to synchronously execute the OnInputStreamReady
    491    // callback.
    492    mTransport->PostEvent(nsSocketTransport::MSG_INPUT_PENDING);
    493  } else {
    494    mTransport->OnInputPending();
    495  }
    496 
    497  return NS_OK;
    498 }
    499 
    500 //-----------------------------------------------------------------------------
    501 // socket output stream impl
    502 //-----------------------------------------------------------------------------
    503 
    504 nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport* trans)
    505    : mTransport(trans) {}
    506 
    507 // called on the socket transport thread...
    508 //
    509 //   condition : failure code if socket has been closed
    510 //
    511 void nsSocketOutputStream::OnSocketReady(nsresult condition) {
    512  SOCKET_LOG(("nsSocketOutputStream::OnSocketReady [this=%p cond=%" PRIx32
    513              "]\n",
    514              this, static_cast<uint32_t>(condition)));
    515 
    516  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    517 
    518  nsCOMPtr<nsIOutputStreamCallback> callback;
    519  {
    520    MutexAutoLock lock(mTransport->mLock);
    521 
    522    // update condition, but be careful not to erase an already
    523    // existing error condition.
    524    if (NS_SUCCEEDED(mCondition)) mCondition = condition;
    525 
    526    // ignore event if only waiting for closure and not closed.
    527    if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
    528      callback = std::move(mCallback);
    529      mCallbackFlags = 0;
    530    }
    531  }
    532 
    533  if (callback) callback->OnOutputStreamReady(this);
    534 }
    535 
    536 NS_IMPL_QUERY_INTERFACE(nsSocketOutputStream, nsIOutputStream,
    537                        nsIAsyncOutputStream)
    538 
    539 NS_IMETHODIMP_(MozExternalRefCountType)
    540 nsSocketOutputStream::AddRef() {
    541  ++mWriterRefCnt;
    542  return mTransport->AddRef();
    543 }
    544 
    545 NS_IMETHODIMP_(MozExternalRefCountType)
    546 nsSocketOutputStream::Release() {
    547  if (--mWriterRefCnt == 0) Close();
    548  return mTransport->Release();
    549 }
    550 
    551 NS_IMETHODIMP
    552 nsSocketOutputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED); }
    553 
    554 NS_IMETHODIMP
    555 nsSocketOutputStream::Flush() { return NS_OK; }
    556 
    557 NS_IMETHODIMP
    558 nsSocketOutputStream::StreamStatus() {
    559  MutexAutoLock lock(mTransport->mLock);
    560  return mCondition;
    561 }
    562 
    563 NS_IMETHODIMP
    564 nsSocketOutputStream::Write(const char* buf, uint32_t count,
    565                            uint32_t* countWritten) {
    566  SOCKET_LOG(("nsSocketOutputStream::Write [this=%p count=%u]\n", this, count));
    567 
    568  *countWritten = 0;
    569 
    570  // A write of 0 bytes can be used to force the initial SSL handshake, so do
    571  // not reject that.
    572 
    573  PRFileDesc* fd = nullptr;
    574  {
    575    MutexAutoLock lock(mTransport->mLock);
    576 
    577    if (NS_FAILED(mCondition)) return mCondition;
    578 
    579    fd = mTransport->GetFD_Locked();
    580    if (!fd) return NS_BASE_STREAM_WOULD_BLOCK;
    581  }
    582 
    583  SOCKET_LOG(("  calling PR_Write [count=%u]\n", count));
    584 
    585  // cannot hold lock while calling NSPR.  (worried about the fact that PSM
    586  // synchronously proxies notifications over to the UI thread, which could
    587  // mistakenly try to re-enter this code.)
    588  int32_t n = PR_Write(fd, buf, count);
    589 
    590  SOCKET_LOG(("  PR_Write returned [n=%d]\n", n));
    591 
    592  nsresult rv = NS_OK;
    593  {
    594    MutexAutoLock lock(mTransport->mLock);
    595 
    596 #ifdef ENABLE_SOCKET_TRACING
    597    if (n > 0) mTransport->TraceOutBuf(buf, n);
    598 #endif
    599 
    600    mTransport->ReleaseFD_Locked(fd);
    601 
    602    if (n > 0) {
    603      mByteCount += (*countWritten = n);
    604      profiler_count_bandwidth_written_bytes(n);
    605    } else if (n < 0) {
    606      PRErrorCode code = PR_GetError();
    607      if (code == PR_WOULD_BLOCK_ERROR) return NS_BASE_STREAM_WOULD_BLOCK;
    608      mCondition = ErrorAccordingToNSPR(code);
    609    }
    610    rv = mCondition;
    611  }
    612  if (NS_FAILED(rv)) mTransport->OnOutputClosed(rv);
    613 
    614  // only send this notification if we have indeed written some data.
    615  // see bug 196827 for an example of why this is important.
    616  if ((n > 0)) {
    617    mTransport->SendStatus(NS_NET_STATUS_SENDING_TO);
    618  }
    619 
    620  return rv;
    621 }
    622 
    623 NS_IMETHODIMP
    624 nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader, void* closure,
    625                                    uint32_t count, uint32_t* countRead) {
    626  // socket stream is unbuffered
    627  return NS_ERROR_NOT_IMPLEMENTED;
    628 }
    629 
    630 nsresult nsSocketOutputStream::WriteFromSegments(
    631    nsIInputStream* input, void* closure, const char* fromSegment,
    632    uint32_t offset, uint32_t count, uint32_t* countRead) {
    633  nsSocketOutputStream* self = (nsSocketOutputStream*)closure;
    634  return self->Write(fromSegment, count, countRead);
    635 }
    636 
    637 NS_IMETHODIMP
    638 nsSocketOutputStream::WriteFrom(nsIInputStream* stream, uint32_t count,
    639                                uint32_t* countRead) {
    640  return stream->ReadSegments(WriteFromSegments, this, count, countRead);
    641 }
    642 
    643 NS_IMETHODIMP
    644 nsSocketOutputStream::IsNonBlocking(bool* nonblocking) {
    645  *nonblocking = true;
    646  return NS_OK;
    647 }
    648 
    649 NS_IMETHODIMP
    650 nsSocketOutputStream::CloseWithStatus(nsresult reason) {
    651  SOCKET_LOG(("nsSocketOutputStream::CloseWithStatus [this=%p reason=%" PRIx32
    652              "]\n",
    653              this, static_cast<uint32_t>(reason)));
    654 
    655  // may be called from any thread
    656 
    657  nsresult rv;
    658  {
    659    MutexAutoLock lock(mTransport->mLock);
    660 
    661    if (NS_SUCCEEDED(mCondition)) {
    662      rv = mCondition = reason;
    663    } else {
    664      rv = NS_OK;
    665    }
    666  }
    667  if (NS_FAILED(rv)) mTransport->OnOutputClosed(rv);
    668  return NS_OK;
    669 }
    670 
    671 NS_IMETHODIMP
    672 nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback* callback,
    673                                uint32_t flags, uint32_t amount,
    674                                nsIEventTarget* target) {
    675  SOCKET_LOG(("nsSocketOutputStream::AsyncWait [this=%p]\n", this));
    676 
    677  {
    678    MutexAutoLock lock(mTransport->mLock);
    679 
    680    if (callback && target) {
    681      //
    682      // build event proxy
    683      //
    684      mCallback = NS_NewOutputStreamReadyEvent(callback, target);
    685    } else {
    686      mCallback = callback;
    687    }
    688 
    689    mCallbackFlags = flags;
    690  }
    691  mTransport->OnOutputPending();
    692  return NS_OK;
    693 }
    694 
    695 //-----------------------------------------------------------------------------
    696 // socket transport impl
    697 //-----------------------------------------------------------------------------
    698 
    699 nsSocketTransport::nsSocketTransport()
    700    : mFD(this),
    701      mSocketTransportService(gSocketTransportService),
    702      mInput(new nsSocketInputStream(this)),
    703      mOutput(new nsSocketOutputStream(this)) {
    704  SOCKET_LOG(("creating nsSocketTransport @%p\n", this));
    705 
    706  mTimeouts[TIMEOUT_CONNECT] = UINT16_MAX;     // no timeout
    707  mTimeouts[TIMEOUT_READ_WRITE] = UINT16_MAX;  // no timeout
    708 }
    709 
    710 nsSocketTransport::~nsSocketTransport() {
    711  MOZ_RELEASE_ASSERT(!mAttached);
    712  SOCKET_LOG(("destroying nsSocketTransport @%p\n", this));
    713 }
    714 
    715 nsresult nsSocketTransport::Init(const nsTArray<nsCString>& types,
    716                                 const nsACString& host, uint16_t port,
    717                                 const nsACString& hostRoute,
    718                                 uint16_t portRoute,
    719                                 nsIProxyInfo* givenProxyInfo,
    720                                 nsIDNSRecord* dnsRecord) {
    721  nsCOMPtr<nsProxyInfo> proxyInfo;
    722  if (givenProxyInfo) {
    723    proxyInfo = do_QueryInterface(givenProxyInfo);
    724    NS_ENSURE_ARG(proxyInfo);
    725  }
    726 
    727  if (dnsRecord) {
    728    mExternalDNSResolution = true;
    729    mDNSRecord = do_QueryInterface(dnsRecord);
    730    mDNSRecord->IsTRR(&mResolvedByTRR);
    731    mDNSRecord->GetEffectiveTRRMode(&mEffectiveTRRMode);
    732    mDNSRecord->GetTrrSkipReason(&mTRRSkipReason);
    733  }
    734 
    735  // init socket type info
    736 
    737  mOriginHost = host;
    738  mOriginPort = port;
    739  if (!hostRoute.IsEmpty()) {
    740    mHost = hostRoute;
    741    mPort = portRoute;
    742  } else {
    743    mHost = host;
    744    mPort = port;
    745  }
    746 
    747  // A subtle check we don't enter this method more than once for the socket
    748  // transport lifetime.  Disable on TSan builds to prevent race checking, we
    749  // don't want an atomic here for perf reasons!
    750 #ifndef MOZ_TSAN
    751  MOZ_ASSERT(!mPortRemappingApplied);
    752 #endif  // !MOZ_TSAN
    753 
    754  if (proxyInfo) {
    755    mHttpsProxy = proxyInfo->IsHTTPS();
    756  }
    757 
    758  const char* proxyType = nullptr;
    759  mProxyInfo = proxyInfo;
    760  if (proxyInfo) {
    761    mProxyPort = proxyInfo->Port();
    762    mProxyHost = proxyInfo->Host();
    763    // grab proxy type (looking for "socks" for example)
    764    proxyType = proxyInfo->Type();
    765    if (proxyType && (proxyInfo->IsHTTP() || proxyInfo->IsHTTPS() ||
    766                      proxyInfo->IsDirect() || !strcmp(proxyType, "unknown"))) {
    767      proxyType = nullptr;
    768    }
    769  }
    770 
    771  SOCKET_LOG1(
    772      ("nsSocketTransport::Init [this=%p host=%s:%hu origin=%s:%d "
    773       "proxy=%s:%hu]\n",
    774       this, mHost.get(), mPort, mOriginHost.get(), mOriginPort,
    775       mProxyHost.get(), mProxyPort));
    776 
    777  // include proxy type as a socket type if proxy type is not "http"
    778  uint32_t typeCount = types.Length() + (proxyType != nullptr);
    779  if (!typeCount) return NS_OK;
    780 
    781  // if we have socket types, then the socket provider service had
    782  // better exist!
    783  nsresult rv;
    784  nsCOMPtr<nsISocketProviderService> spserv =
    785      nsSocketProviderService::GetOrCreate();
    786 
    787  if (!mTypes.SetCapacity(typeCount, fallible)) {
    788    return NS_ERROR_OUT_OF_MEMORY;
    789  }
    790 
    791  // now verify that each socket type has a registered socket provider.
    792  for (uint32_t i = 0, type = 0; i < typeCount; ++i) {
    793    // store socket types
    794    if (i == 0 && proxyType) {
    795      mTypes.AppendElement(proxyType);
    796    } else {
    797      mTypes.AppendElement(types[type++]);
    798    }
    799 
    800    nsCOMPtr<nsISocketProvider> provider;
    801    rv = spserv->GetSocketProvider(mTypes[i].get(), getter_AddRefs(provider));
    802    if (NS_FAILED(rv)) {
    803      NS_WARNING("no registered socket provider");
    804      return rv;
    805    }
    806 
    807    // note if socket type corresponds to a transparent proxy
    808    // XXX don't hardcode SOCKS here (use proxy info's flags instead).
    809    if (mTypes[i].EqualsLiteral("socks") || mTypes[i].EqualsLiteral("socks4")) {
    810      mProxyTransparent = true;
    811 
    812      if (proxyInfo->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
    813        // we want the SOCKS layer to send the hostname
    814        // and port to the proxy and let it do the DNS.
    815        mProxyTransparentResolvesHost = true;
    816      }
    817    }
    818  }
    819 
    820  return NS_OK;
    821 }
    822 
    823 #if defined(XP_UNIX)
    824 nsresult nsSocketTransport::InitWithFilename(const char* filename) {
    825  return InitWithName(filename, strlen(filename));
    826 }
    827 
    828 nsresult nsSocketTransport::InitWithName(const char* name, size_t length) {
    829  if (length > sizeof(mNetAddr.local.path) - 1) {
    830    return NS_ERROR_FILE_NAME_TOO_LONG;
    831  }
    832 
    833  if (!name[0] && length > 1) {
    834    // name is abstract address name that is supported on Linux only
    835 #  if defined(XP_LINUX)
    836    mHost.Assign(name + 1, length - 1);
    837 #  else
    838    return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
    839 #  endif
    840  } else {
    841    // The name isn't abstract socket address.  So this is Unix domain
    842    // socket that has file path.
    843    mHost.Assign(name, length);
    844  }
    845  mPort = 0;
    846 
    847  mNetAddr.local.family = AF_LOCAL;
    848  memcpy(mNetAddr.local.path, name, length);
    849  mNetAddr.local.path[length] = '\0';
    850  mNetAddrIsSet = true;
    851 
    852  return NS_OK;
    853 }
    854 #endif
    855 
    856 nsresult nsSocketTransport::InitWithConnectedSocket(PRFileDesc* fd,
    857                                                    const NetAddr* addr) {
    858  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    859 
    860  char buf[kNetAddrMaxCStrBufSize];
    861  addr->ToStringBuffer(buf, sizeof(buf));
    862  mHost.Assign(buf);
    863 
    864  uint16_t port;
    865  if (addr->raw.family == AF_INET) {
    866    port = addr->inet.port;
    867  } else if (addr->raw.family == AF_INET6) {
    868    port = addr->inet6.port;
    869  } else {
    870    port = 0;
    871  }
    872  mPort = ntohs(port);
    873 
    874  mNetAddr = *addr;
    875 
    876  mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
    877  mState = STATE_TRANSFERRING;
    878  SetSocketName(fd);
    879  mNetAddrIsSet = true;
    880 
    881  {
    882    MutexAutoLock lock(mLock);
    883    NS_ASSERTION(!mFD.IsInitialized(), "already initialized");
    884    mFD = fd;
    885    mFDref = 1;
    886    mFDconnected = true;
    887    mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
    888  }
    889 
    890  // make sure new socket is non-blocking
    891  PRSocketOptionData opt;
    892  opt.option = PR_SockOpt_Nonblocking;
    893  opt.value.non_blocking = true;
    894  PR_SetSocketOption(fd, &opt);
    895 
    896  SOCKET_LOG(
    897      ("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
    898       this, mHost.get(), mPort));
    899 
    900  // jump to InitiateSocket to get ourselves attached to the STS poll list.
    901  return PostEvent(MSG_RETRY_INIT_SOCKET);
    902 }
    903 
    904 nsresult nsSocketTransport::InitWithConnectedSocket(
    905    PRFileDesc* aFD, const NetAddr* aAddr, nsIInterfaceRequestor* aCallbacks) {
    906  {
    907    MutexAutoLock lock(mLock);
    908    mCallbacks = aCallbacks;
    909  }
    910  return InitWithConnectedSocket(aFD, aAddr);
    911 }
    912 
    913 nsresult nsSocketTransport::PostEvent(uint32_t type, nsresult status,
    914                                      nsISupports* param,
    915                                      std::function<void()>&& task) {
    916  SOCKET_LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%" PRIx32
    917              " param=%p]\n",
    918              this, type, static_cast<uint32_t>(status), param));
    919 
    920  nsCOMPtr<nsIRunnable> event =
    921      new nsSocketEvent(this, type, status, param, std::move(task));
    922  if (!event) return NS_ERROR_OUT_OF_MEMORY;
    923 
    924  return mSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
    925 }
    926 
    927 void nsSocketTransport::SendStatus(nsresult status) {
    928  SOCKET_LOG1(("nsSocketTransport::SendStatus [this=%p status=%" PRIx32 "]\n",
    929               this, static_cast<uint32_t>(status)));
    930 
    931  nsCOMPtr<nsITransportEventSink> sink;
    932  uint64_t progress;
    933  {
    934    MutexAutoLock lock(mLock);
    935    sink = mEventSink;
    936    switch (status) {
    937      case NS_NET_STATUS_SENDING_TO:
    938        progress = mOutput->ByteCount(lock);
    939        break;
    940      case NS_NET_STATUS_RECEIVING_FROM:
    941        progress = mInput->ByteCount(lock);
    942        break;
    943      default:
    944        progress = 0;
    945        break;
    946    }
    947  }
    948  if (sink) {
    949    sink->OnTransportStatus(this, status, progress, -1);
    950  }
    951 }
    952 
    953 nsresult nsSocketTransport::ResolveHost() {
    954  SOCKET_LOG((
    955      "nsSocketTransport::ResolveHost [this=%p %s:%d%s] "
    956      "mProxyTransparentResolvesHost=%d\n",
    957      this, SocketHost().get(), SocketPort(),
    958      mConnectionFlags & nsSocketTransport::BYPASS_CACHE ? " bypass cache" : "",
    959      mProxyTransparentResolvesHost));
    960  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    961 
    962  nsresult rv;
    963 
    964  if (!mProxyHost.IsEmpty()) {
    965    if (!mProxyTransparent || mProxyTransparentResolvesHost) {
    966 #if defined(XP_UNIX)
    967      MOZ_ASSERT(!mNetAddrIsSet || mNetAddr.raw.family != AF_LOCAL,
    968                 "Unix domain sockets can't be used with proxies");
    969 #endif
    970      // When not resolving mHost locally, we still want to ensure that
    971      // it only contains valid characters.  See bug 304904 for details.
    972      // Sometimes the end host is not yet known and mHost is *
    973      if (!net_IsValidDNSHost(mHost) && !mHost.EqualsLiteral("*")) {
    974        SOCKET_LOG(("  invalid hostname %s\n", mHost.get()));
    975        return NS_ERROR_UNKNOWN_HOST;
    976      }
    977    }
    978    if (mProxyTransparentResolvesHost) {
    979      // Name resolution is done on the server side.  Just pretend
    980      // client resolution is complete, this will get picked up later.
    981      // since we don't need to do DNS now, we bypass the resolving
    982      // step by initializing mNetAddr to an empty address, but we
    983      // must keep the port. The SOCKS IO layer will use the hostname
    984      // we send it when it's created, rather than the empty address
    985      // we send with the connect call.
    986      mState = STATE_RESOLVING;
    987      mNetAddr.raw.family = AF_INET;
    988      mNetAddr.inet.port = htons(SocketPort());
    989      mNetAddr.inet.ip = htonl(INADDR_ANY);
    990      return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nullptr);
    991    }
    992  }
    993 
    994  if (mExternalDNSResolution) {
    995    MOZ_ASSERT(mDNSRecord);
    996    mState = STATE_RESOLVING;
    997    return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nullptr);
    998  }
    999 
   1000  nsCOMPtr<nsIDNSService> dns = nullptr;
   1001  auto initTask = [&dns]() { dns = do_GetService(kDNSServiceCID); };
   1002  if (!NS_IsMainThread()) {
   1003    // Forward to the main thread synchronously.
   1004    RefPtr<nsIThread> mainThread = do_GetMainThread();
   1005    if (!mainThread) {
   1006      return NS_ERROR_FAILURE;
   1007    }
   1008 
   1009    SyncRunnable::DispatchToThread(
   1010        mainThread,
   1011        NS_NewRunnableFunction("nsSocketTransport::ResolveHost->GetDNSService",
   1012                               initTask));
   1013  } else {
   1014    initTask();
   1015  }
   1016  if (!dns) {
   1017    return NS_ERROR_FAILURE;
   1018  }
   1019 
   1020  mResolving = true;
   1021 
   1022  nsIDNSService::DNSFlags dnsFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS;
   1023  if (mConnectionFlags & nsSocketTransport::BYPASS_CACHE) {
   1024    dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE;
   1025  }
   1026  if (mConnectionFlags & nsSocketTransport::REFRESH_CACHE) {
   1027    dnsFlags = nsIDNSService::RESOLVE_REFRESH_CACHE;
   1028  }
   1029  if (mConnectionFlags & nsSocketTransport::DISABLE_IPV6) {
   1030    dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
   1031  }
   1032  if (mConnectionFlags & nsSocketTransport::DISABLE_IPV4) {
   1033    dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4;
   1034  }
   1035  if (mConnectionFlags & nsSocketTransport::DISABLE_TRR) {
   1036    dnsFlags |= nsIDNSService::RESOLVE_DISABLE_TRR;
   1037  }
   1038 
   1039  if (mConnectionFlags & nsSocketTransport::USE_IP_HINT_ADDRESS) {
   1040    dnsFlags |= nsIDNSService::RESOLVE_IP_HINT;
   1041  }
   1042 
   1043  dnsFlags |= nsIDNSService::GetFlagsFromTRRMode(
   1044      nsISocketTransport::GetTRRModeFromFlags(mConnectionFlags));
   1045 
   1046  // When we get here, we are not resolving using any configured proxy likely
   1047  // because of individual proxy setting on the request or because the host is
   1048  // excluded from proxying.  Hence, force resolution despite global proxy-DNS
   1049  // configuration.
   1050  dnsFlags |= nsIDNSService::RESOLVE_IGNORE_SOCKS_DNS;
   1051 
   1052  NS_ASSERTION(!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) ||
   1053                   !(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4),
   1054               "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");
   1055 
   1056  SendStatus(NS_NET_STATUS_RESOLVING_HOST);
   1057 
   1058  if (!SocketHost().Equals(mOriginHost)) {
   1059    SOCKET_LOG(("nsSocketTransport %p origin %s doing dns for %s\n", this,
   1060                mOriginHost.get(), SocketHost().get()));
   1061  }
   1062  rv =
   1063      dns->AsyncResolveNative(SocketHost(), nsIDNSService::RESOLVE_TYPE_DEFAULT,
   1064                              dnsFlags, nullptr, this, mSocketTransportService,
   1065                              mOriginAttributes, getter_AddRefs(mDNSRequest));
   1066 
   1067  if (NS_SUCCEEDED(rv)) {
   1068    SOCKET_LOG(("  advancing to STATE_RESOLVING\n"));
   1069    mState = STATE_RESOLVING;
   1070  }
   1071  return rv;
   1072 }
   1073 
   1074 nsresult nsSocketTransport::BuildSocket(PRFileDesc*& fd, bool& proxyTransparent,
   1075                                        bool& usingSSL) {
   1076  SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%p]\n", this));
   1077 
   1078  nsresult rv = NS_OK;
   1079 
   1080  proxyTransparent = false;
   1081  usingSSL = false;
   1082 
   1083  if (mTypes.IsEmpty()) {
   1084    fd = PR_OpenTCPSocket(mNetAddr.raw.family);
   1085    if (!fd) {
   1086      SOCKET_LOG(("  error creating TCP nspr socket [rv=%" PRIx32 "]\n",
   1087                  static_cast<uint32_t>(rv)));
   1088      return NS_ERROR_OUT_OF_MEMORY;
   1089    }
   1090    return NS_OK;
   1091  }
   1092 
   1093 #if defined(XP_UNIX)
   1094  MOZ_ASSERT(!mNetAddrIsSet || mNetAddr.raw.family != AF_LOCAL,
   1095             "Unix domain sockets can't be used with socket types");
   1096 #endif
   1097 
   1098  fd = nullptr;
   1099 
   1100  uint32_t controlFlags = 0;
   1101  if (mProxyTransparentResolvesHost) {
   1102    controlFlags |= nsISocketProvider::PROXY_RESOLVES_HOST;
   1103  }
   1104 
   1105  if (mConnectionFlags & nsISocketTransport::ANONYMOUS_CONNECT) {
   1106    controlFlags |= nsISocketProvider::ANONYMOUS_CONNECT;
   1107  }
   1108 
   1109  if (mConnectionFlags & nsISocketTransport::NO_PERMANENT_STORAGE) {
   1110    controlFlags |= nsISocketProvider::NO_PERMANENT_STORAGE;
   1111  }
   1112 
   1113  if (mConnectionFlags & nsISocketTransport::BE_CONSERVATIVE) {
   1114    controlFlags |= nsISocketProvider::BE_CONSERVATIVE;
   1115  }
   1116 
   1117  if (mConnectionFlags & nsISocketTransport::DONT_TRY_ECH) {
   1118    controlFlags |= nsISocketProvider::DONT_TRY_ECH;
   1119  }
   1120 
   1121  if (mConnectionFlags & nsISocketTransport::IS_RETRY) {
   1122    controlFlags |= nsISocketProvider::IS_RETRY;
   1123  }
   1124 
   1125  if (mConnectionFlags &
   1126      nsISocketTransport::ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT) {
   1127    controlFlags |= nsISocketProvider::ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT;
   1128  }
   1129 
   1130  if (mConnectionFlags & nsISocketTransport::IS_SPECULATIVE_CONNECTION) {
   1131    controlFlags |= nsISocketProvider::IS_SPECULATIVE_CONNECTION;
   1132  }
   1133 
   1134  if (mResolvedByTRR) {
   1135    controlFlags |= nsISocketProvider::USED_PRIVATE_DNS;
   1136  }
   1137 
   1138  // by setting host to mOriginHost, instead of mHost we send the
   1139  // SocketProvider (e.g. PSM) the origin hostname but can still do DNS
   1140  // on an explicit alternate service host name
   1141  const char* host = mOriginHost.get();
   1142  int32_t port = (int32_t)mOriginPort;
   1143 
   1144  nsCOMPtr<nsISocketProviderService> spserv =
   1145      nsSocketProviderService::GetOrCreate();
   1146  nsCOMPtr<nsIProxyInfo> proxyInfo = mProxyInfo;
   1147 
   1148  uint32_t i;
   1149  for (i = 0; i < mTypes.Length(); ++i) {
   1150    nsCOMPtr<nsISocketProvider> provider;
   1151 
   1152    SOCKET_LOG(("  pushing io layer [%u:%s]\n", i, mTypes[i].get()));
   1153 
   1154    rv = spserv->GetSocketProvider(mTypes[i].get(), getter_AddRefs(provider));
   1155    if (NS_FAILED(rv)) break;
   1156 
   1157    nsCOMPtr<nsITLSSocketControl> tlsSocketControl;
   1158    if (i == 0) {
   1159      // if this is the first type, we'll want the
   1160      // service to allocate a new socket
   1161 
   1162      // Most layers _ESPECIALLY_ PSM want the origin name here as they
   1163      // will use it for secure checks, etc.. and any connection management
   1164      // differences between the origin name and the routed name can be
   1165      // taken care of via DNS. However, SOCKS is a special case as there is
   1166      // no DNS. in the case of SOCKS and PSM the PSM is a separate layer
   1167      // and receives the origin name.
   1168      const char* socketProviderHost = host;
   1169      int32_t socketProviderPort = port;
   1170      if (mProxyTransparentResolvesHost &&
   1171          (mTypes[0].EqualsLiteral("socks") ||
   1172           mTypes[0].EqualsLiteral("socks4"))) {
   1173        SOCKET_LOG(("SOCKS %d Host/Route override: %s:%d -> %s:%d\n",
   1174                    mHttpsProxy, socketProviderHost, socketProviderPort,
   1175                    mHost.get(), mPort));
   1176        socketProviderHost = mHost.get();
   1177        socketProviderPort = mPort;
   1178      }
   1179 
   1180      // when https proxying we want to just connect to the proxy as if
   1181      // it were the end host (i.e. expect the proxy's cert)
   1182 
   1183      rv = provider->NewSocket(
   1184          mNetAddr.raw.family,
   1185          mHttpsProxy ? mProxyHost.get() : socketProviderHost,
   1186          mHttpsProxy ? mProxyPort : socketProviderPort, proxyInfo,
   1187          mOriginAttributes, controlFlags, mTlsFlags, &fd,
   1188          getter_AddRefs(tlsSocketControl));
   1189 
   1190      if (NS_SUCCEEDED(rv) && !fd) {
   1191        MOZ_ASSERT_UNREACHABLE(
   1192            "NewSocket succeeded but failed to "
   1193            "create a PRFileDesc");
   1194        rv = NS_ERROR_UNEXPECTED;
   1195      }
   1196    } else {
   1197      // the socket has already been allocated,
   1198      // so we just want the service to add itself
   1199      // to the stack (such as pushing an io layer)
   1200      rv = provider->AddToSocket(mNetAddr.raw.family, host, port, proxyInfo,
   1201                                 mOriginAttributes, controlFlags, mTlsFlags, fd,
   1202                                 getter_AddRefs(tlsSocketControl));
   1203    }
   1204 
   1205    // controlFlags = 0; not used below this point...
   1206    if (NS_FAILED(rv)) break;
   1207 
   1208    // if the service was ssl or starttls, we want to hold onto the socket
   1209    // info
   1210    bool isSSL = mTypes[i].EqualsLiteral("ssl");
   1211    if (isSSL || mTypes[i].EqualsLiteral("starttls")) {
   1212      // remember security info
   1213      {
   1214        MutexAutoLock lock(mLock);
   1215        mTLSSocketControl = tlsSocketControl;
   1216        SOCKET_LOG(("  [tlsSocketControl=%p callbacks=%p]\n",
   1217                    mTLSSocketControl.get(), mCallbacks.get()));
   1218      }
   1219      // remember if socket type is SSL so we can ProxyStartSSL if need be.
   1220      usingSSL = isSSL;
   1221    } else if (mTypes[i].EqualsLiteral("socks") ||
   1222               mTypes[i].EqualsLiteral("socks4")) {
   1223      // since socks is transparent, any layers above
   1224      // it do not have to worry about proxy stuff
   1225      proxyInfo = nullptr;
   1226      proxyTransparent = true;
   1227    }
   1228  }
   1229 
   1230  if (NS_FAILED(rv)) {
   1231    SOCKET_LOG(("  error pushing io layer [%u:%s rv=%" PRIx32 "]\n", i,
   1232                mTypes[i].get(), static_cast<uint32_t>(rv)));
   1233    if (fd) {
   1234      CloseSocket(
   1235          fd, mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
   1236    }
   1237  }
   1238  return rv;
   1239 }
   1240 
   1241 static bool ShouldBlockAddress(const NetAddr& aAddr) {
   1242  if (!xpc::AreNonLocalConnectionsDisabled()) {
   1243    return false;
   1244  }
   1245 
   1246  NetAddr overrideAddr;
   1247  bool hasOverride = FindNetAddrOverride(aAddr, overrideAddr);
   1248  const NetAddr& addrToCheck = hasOverride ? overrideAddr : aAddr;
   1249 
   1250  return !(addrToCheck.IsIPAddrAny() || addrToCheck.IsIPAddrLocal() ||
   1251           addrToCheck.IsIPAddrShared() || addrToCheck.IsLoopbackAddr());
   1252 }
   1253 
   1254 nsresult nsSocketTransport::InitiateSocket() {
   1255  SOCKET_LOG(("nsSocketTransport::InitiateSocket [this=%p]\n", this));
   1256  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1257 
   1258  nsresult rv;
   1259  bool isLocal;
   1260  IsLocal(&isLocal);
   1261 
   1262  if (gIOService->IsNetTearingDown()) {
   1263    return NS_ERROR_ABORT;
   1264  }
   1265 
   1266  // Since https://github.com/whatwg/fetch/pull/1763,
   1267  // we need to disable access to 0.0.0.0 for non-test purposes
   1268  if (mNetAddr.IsIPAddrAny() && !mProxyTransparentResolvesHost) {
   1269    if (StaticPrefs::network_socket_ip_addr_any_disabled()) {
   1270      SOCKET_LOG(("connection refused NS_ERROR_CONNECTION_REFUSED\n"));
   1271      return NS_ERROR_CONNECTION_REFUSED;
   1272    }
   1273  }
   1274 
   1275  if (gIOService->IsOffline()) {
   1276    if (StaticPrefs::network_disable_localhost_when_offline() || !isLocal) {
   1277      return NS_ERROR_OFFLINE;
   1278    }
   1279  } else if (!isLocal) {
   1280 #ifdef DEBUG
   1281    // all IP networking has to be done from the parent
   1282    if (NS_SUCCEEDED(mCondition) && ((mNetAddr.raw.family == AF_INET) ||
   1283                                     (mNetAddr.raw.family == AF_INET6))) {
   1284      MOZ_ASSERT(!IsNeckoChild());
   1285    }
   1286 #endif
   1287 
   1288    if (NS_SUCCEEDED(mCondition) && ShouldBlockAddress(mNetAddr)) {
   1289      nsAutoCString ipaddr;
   1290      RefPtr<nsNetAddr> netaddr = new nsNetAddr(&mNetAddr);
   1291      netaddr->GetAddress(ipaddr);
   1292      fprintf_stderr(
   1293          stderr,
   1294          "FATAL ERROR: Non-local network connections are disabled and a "
   1295          "connection "
   1296          "attempt to %s (%s) was made.\nYou should only access hostnames "
   1297          "available via the test networking proxy (if running mochitests) "
   1298          "or from a test-specific httpd.js server (if running xpcshell "
   1299          "tests). "
   1300          "Browser services should be disabled or redirected to a local "
   1301          "server.\n",
   1302          mHost.get(), ipaddr.get());
   1303      return NS_ERROR_NON_LOCAL_CONNECTION_REFUSED;
   1304    }
   1305  }
   1306 
   1307  // Hosts/Proxy Hosts that are Local IP Literals should not be speculatively
   1308  // connected - Bug 853423.
   1309  if (mConnectionFlags & nsISocketTransport::DISABLE_RFC1918 &&
   1310      mNetAddr.IsIPAddrLocal()) {
   1311    if (SOCKET_LOG_ENABLED()) {
   1312      nsAutoCString netAddrCString;
   1313      netAddrCString.SetLength(kIPv6CStrBufSize);
   1314      if (!mNetAddr.ToStringBuffer(netAddrCString.BeginWriting(),
   1315                                   kIPv6CStrBufSize)) {
   1316        netAddrCString = "<IP-to-string failed>"_ns;
   1317      }
   1318      SOCKET_LOG(
   1319          ("nsSocketTransport::InitiateSocket skipping "
   1320           "speculative connection for host [%s:%d] proxy "
   1321           "[%s:%d] with Local IP address [%s]",
   1322           mHost.get(), mPort, mProxyHost.get(), mProxyPort,
   1323           netAddrCString.get()));
   1324    }
   1325    mCondition = NS_ERROR_CONNECTION_REFUSED;
   1326    OnSocketDetached(nullptr);
   1327    return mCondition;
   1328  }
   1329 
   1330  //
   1331  // find out if it is going to be ok to attach another socket to the STS.
   1332  // if not then we have to wait for the STS to tell us that it is ok.
   1333  // the notification is asynchronous, which means that when we could be
   1334  // in a race to call AttachSocket once notified.  for this reason, when
   1335  // we get notified, we just re-enter this function.  as a result, we are
   1336  // sure to ask again before calling AttachSocket.  in this way we deal
   1337  // with the race condition.  though it isn't the most elegant solution,
   1338  // it is far simpler than trying to build a system that would guarantee
   1339  // FIFO ordering (which wouldn't even be that valuable IMO).  see bug
   1340  // 194402 for more info.
   1341  //
   1342  if (!mSocketTransportService->CanAttachSocket()) {
   1343    nsCOMPtr<nsIRunnable> event =
   1344        new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET);
   1345    if (!event) return NS_ERROR_OUT_OF_MEMORY;
   1346    return mSocketTransportService->NotifyWhenCanAttachSocket(event);
   1347  }
   1348 
   1349  //
   1350  // if we already have a connected socket, then just attach and return.
   1351  //
   1352  {
   1353    MutexAutoLock lock(mLock);
   1354    if (mFD.IsInitialized()) {
   1355      rv = mSocketTransportService->AttachSocket(mFD, this);
   1356      if (NS_SUCCEEDED(rv)) mAttached = true;
   1357      return rv;
   1358    }
   1359  }
   1360 
   1361  //
   1362  // create new socket fd, push io layers, etc.
   1363  //
   1364  PRFileDesc* fd;
   1365  bool proxyTransparent;
   1366  bool usingSSL;
   1367 
   1368  rv = BuildSocket(fd, proxyTransparent, usingSSL);
   1369  if (NS_FAILED(rv)) {
   1370    SOCKET_LOG(
   1371        ("  BuildSocket failed [rv=%" PRIx32 "]\n", static_cast<uint32_t>(rv)));
   1372    return rv;
   1373  }
   1374 
   1375 #ifdef FUZZING
   1376  if (StaticPrefs::fuzzing_necko_enabled()) {
   1377    rv = AttachFuzzyIOLayer(fd);
   1378    if (NS_FAILED(rv)) {
   1379      SOCKET_LOG(("Failed to attach fuzzing IOLayer [rv=%" PRIx32 "].\n",
   1380                  static_cast<uint32_t>(rv)));
   1381      return rv;
   1382    }
   1383    SOCKET_LOG(("Successfully attached fuzzing IOLayer.\n"));
   1384 
   1385    if (usingSSL) {
   1386      mTLSSocketControl = new FuzzySocketControl();
   1387    }
   1388  }
   1389 #endif
   1390 
   1391  PRStatus status;
   1392 
   1393  // Make the socket non-blocking...
   1394  PRSocketOptionData opt;
   1395  opt.option = PR_SockOpt_Nonblocking;
   1396  opt.value.non_blocking = true;
   1397  status = PR_SetSocketOption(fd, &opt);
   1398  NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
   1399 
   1400  if (mReuseAddrPort) {
   1401    SOCKET_LOG(("  Setting port/addr reuse socket options\n"));
   1402 
   1403    // Set ReuseAddr for TCP sockets to enable having several
   1404    // sockets bound to same local IP and port
   1405    PRSocketOptionData opt_reuseaddr;
   1406    opt_reuseaddr.option = PR_SockOpt_Reuseaddr;
   1407    opt_reuseaddr.value.reuse_addr = PR_TRUE;
   1408    status = PR_SetSocketOption(fd, &opt_reuseaddr);
   1409    if (status != PR_SUCCESS) {
   1410      SOCKET_LOG(("  Couldn't set reuse addr socket option: %d\n", status));
   1411    }
   1412 
   1413    // And also set ReusePort for platforms supporting this socket option
   1414    PRSocketOptionData opt_reuseport;
   1415    opt_reuseport.option = PR_SockOpt_Reuseport;
   1416    opt_reuseport.value.reuse_port = PR_TRUE;
   1417    status = PR_SetSocketOption(fd, &opt_reuseport);
   1418    if (status != PR_SUCCESS &&
   1419        PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR) {
   1420      SOCKET_LOG(("  Couldn't set reuse port socket option: %d\n", status));
   1421    }
   1422  }
   1423 
   1424  // disable the nagle algorithm - if we rely on it to coalesce writes into
   1425  // full packets the final packet of a multi segment POST/PUT or pipeline
   1426  // sequence is delayed a full rtt
   1427  opt.option = PR_SockOpt_NoDelay;
   1428  opt.value.no_delay = true;
   1429  PR_SetSocketOption(fd, &opt);
   1430 
   1431  // if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF
   1432  // The Windows default of 8KB is too small and as of vista sp1, autotuning
   1433  // only applies to receive window
   1434  int32_t sndBufferSize;
   1435  mSocketTransportService->GetSendBufferSize(&sndBufferSize);
   1436  if (sndBufferSize > 0) {
   1437    opt.option = PR_SockOpt_SendBufferSize;
   1438    opt.value.send_buffer_size = sndBufferSize;
   1439    PR_SetSocketOption(fd, &opt);
   1440  }
   1441 
   1442  if (mQoSBits) {
   1443    opt.option = PR_SockOpt_IpTypeOfService;
   1444    opt.value.tos = mQoSBits;
   1445    PR_SetSocketOption(fd, &opt);
   1446  }
   1447 
   1448 #if defined(XP_WIN)
   1449  // The linger is turned off by default. This is not a hard close, but
   1450  // closesocket should return immediately and operating system tries to send
   1451  // remaining data for certain, implementation specific, amount of time.
   1452  // https://msdn.microsoft.com/en-us/library/ms739165.aspx
   1453  //
   1454  // Turn the linger option on an set the interval to 0. This will cause hard
   1455  // close of the socket.
   1456  opt.option = PR_SockOpt_Linger;
   1457  opt.value.linger.polarity = 1;
   1458  opt.value.linger.linger = 0;
   1459  PR_SetSocketOption(fd, &opt);
   1460 #endif
   1461 
   1462  // up to here, mFD will only be accessed by us
   1463 
   1464  // assign mFD so that we can properly handle OnSocketDetached before we've
   1465  // established a connection.
   1466  {
   1467    MutexAutoLock lock(mLock);
   1468    // inform socket transport about this newly created socket...
   1469    rv = mSocketTransportService->AttachSocket(fd, this);
   1470    if (NS_FAILED(rv)) {
   1471      CloseSocket(
   1472          fd, mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
   1473      return rv;
   1474    }
   1475    mAttached = true;
   1476 
   1477    mFD = fd;
   1478    mFDref = 1;
   1479    mFDconnected = false;
   1480    mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
   1481  }
   1482 
   1483  SOCKET_LOG(("  advancing to STATE_CONNECTING\n"));
   1484  mState = STATE_CONNECTING;
   1485  SendStatus(NS_NET_STATUS_CONNECTING_TO);
   1486 
   1487  if (SOCKET_LOG_ENABLED()) {
   1488    char buf[kNetAddrMaxCStrBufSize];
   1489    mNetAddr.ToStringBuffer(buf, sizeof(buf));
   1490    SOCKET_LOG(("  trying address: %s\n", buf));
   1491  }
   1492 
   1493  //
   1494  // Initiate the connect() to the host...
   1495  //
   1496  PRNetAddr prAddr;
   1497  memset(&prAddr, 0, sizeof(prAddr));
   1498  {
   1499    if (mBindAddr) {
   1500      MutexAutoLock lock(mLock);
   1501      NetAddrToPRNetAddr(mBindAddr.get(), &prAddr);
   1502      status = PR_Bind(fd, &prAddr);
   1503      if (status != PR_SUCCESS) {
   1504        return NS_ERROR_FAILURE;
   1505      }
   1506      mBindAddr = nullptr;
   1507    }
   1508  }
   1509 
   1510  NetAddrToPRNetAddr(&mNetAddr, &prAddr);
   1511 
   1512 #ifdef XP_WIN
   1513  // Find the real tcp socket and set non-blocking once again!
   1514  // Bug 1158189.
   1515  PRFileDesc* bottom = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
   1516  if (bottom) {
   1517    PROsfd osfd = PR_FileDesc2NativeHandle(bottom);
   1518    u_long nonblocking = 1;
   1519    if (ioctlsocket(osfd, FIONBIO, &nonblocking) != 0) {
   1520      NS_WARNING("Socket could not be set non-blocking!");
   1521      return NS_ERROR_FAILURE;
   1522    }
   1523  }
   1524 #endif
   1525 
   1526  if (mTLSSocketControl) {
   1527    if (!mEchConfig.IsEmpty() &&
   1528        !(mConnectionFlags & (DONT_TRY_ECH | BE_CONSERVATIVE))) {
   1529      SOCKET_LOG(("nsSocketTransport::InitiateSocket set echconfig."));
   1530      rv = mTLSSocketControl->SetEchConfig(mEchConfig);
   1531      if (NS_FAILED(rv)) {
   1532        return rv;
   1533      }
   1534      mEchConfigUsed = true;
   1535    }
   1536  }
   1537 
   1538  // We use PRIntervalTime here because we need
   1539  // nsIOService::LastOfflineStateChange time and
   1540  // nsIOService::LastConectivityChange time to be atomic.
   1541  PRIntervalTime connectStarted = 0;
   1542  if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
   1543    connectStarted = PR_IntervalNow();
   1544  }
   1545 
   1546  if (Telemetry::CanRecordPrereleaseData() ||
   1547      Telemetry::CanRecordReleaseData()) {
   1548    if (NS_FAILED(AttachNetworkDataCountLayer(fd))) {
   1549      SOCKET_LOG(
   1550          ("nsSocketTransport::InitiateSocket "
   1551           "AttachNetworkDataCountLayer failed [this=%p]\n",
   1552           this));
   1553    }
   1554  }
   1555  if (StaticPrefs::network_socket_attach_mock_network_layer() &&
   1556      xpc::AreNonLocalConnectionsDisabled()) {
   1557    if (NS_FAILED(AttachMockNetworkLayer(fd))) {
   1558      SOCKET_LOG(
   1559          ("nsSocketTransport::InitiateSocket "
   1560           "AttachMockNetworkLayer failed [this=%p]\n",
   1561           this));
   1562    }
   1563  }
   1564 
   1565  bool connectCalled = true;  // This is only needed for telemetry.
   1566  status = PR_Connect(fd, &prAddr, NS_SOCKET_CONNECT_TIMEOUT);
   1567  PRErrorCode code = PR_GetError();
   1568  if (status == PR_SUCCESS) {
   1569    PR_SetFDInheritable(fd, false);
   1570  }
   1571 
   1572  if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
   1573      connectStarted && connectCalled) {
   1574    SendPRBlockingTelemetry(
   1575        connectStarted, glean::networking::prconnect_blocking_time_normal,
   1576        glean::networking::prconnect_blocking_time_shutdown,
   1577        glean::networking::prconnect_blocking_time_connectivity_change,
   1578        glean::networking::prconnect_blocking_time_link_change,
   1579        glean::networking::prconnect_blocking_time_offline);
   1580  }
   1581 
   1582  if (status == PR_SUCCESS) {
   1583    //
   1584    // we are connected!
   1585    //
   1586    OnSocketConnected();
   1587  } else {
   1588 #if defined(TEST_CONNECT_ERRORS)
   1589    code = RandomizeConnectError(code);
   1590 #endif
   1591    //
   1592    // If the PR_Connect(...) would block, then poll for a connection.
   1593    //
   1594    if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
   1595      mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
   1596      //
   1597      // If the socket is already connected, then return success...
   1598      //
   1599    } else if (PR_IS_CONNECTED_ERROR == code) {
   1600      //
   1601      // we are connected!
   1602      //
   1603      OnSocketConnected();
   1604 
   1605      if (mTLSSocketControl && !mProxyHost.IsEmpty() && proxyTransparent &&
   1606          usingSSL) {
   1607        // if the connection phase is finished, and the ssl layer has
   1608        // been pushed, and we were proxying (transparently; ie. nothing
   1609        // has to happen in the protocol layer above us), it's time for
   1610        // the ssl to start doing it's thing.
   1611        SOCKET_LOG(("  calling ProxyStartSSL()\n"));
   1612        mTLSSocketControl->ProxyStartSSL();
   1613        // XXX what if we were forced to poll on the socket for a successful
   1614        // connection... wouldn't we need to call ProxyStartSSL after a call
   1615        // to PR_ConnectContinue indicates that we are connected?
   1616        //
   1617        // XXX this appears to be what the old socket transport did.  why
   1618        // isn't this broken?
   1619      }
   1620    }
   1621    //
   1622    // A SOCKS request was rejected; get the actual error code from
   1623    // the OS error
   1624    //
   1625    else if (PR_UNKNOWN_ERROR == code && mProxyTransparent &&
   1626             !mProxyHost.IsEmpty()) {
   1627      code = PR_GetOSError();
   1628      rv = ErrorAccordingToNSPR(code);
   1629    }
   1630    //
   1631    // The connection was refused...
   1632    //
   1633    else {
   1634      if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
   1635          connectStarted && connectCalled) {
   1636        SendPRBlockingTelemetry(
   1637            connectStarted,
   1638            glean::networking::prconnect_fail_blocking_time_normal,
   1639            glean::networking::prconnect_fail_blocking_time_shutdown,
   1640            glean::networking::prconnect_fail_blocking_time_connectivity_change,
   1641            glean::networking::prconnect_fail_blocking_time_link_change,
   1642            glean::networking::prconnect_fail_blocking_time_offline);
   1643      }
   1644 
   1645      rv = ErrorAccordingToNSPR(code);
   1646      if ((rv == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty()) {
   1647        rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
   1648      }
   1649    }
   1650  }
   1651  return rv;
   1652 }
   1653 
   1654 bool nsSocketTransport::RecoverFromError() {
   1655  NS_ASSERTION(NS_FAILED(mCondition), "there should be something wrong");
   1656 
   1657  SOCKET_LOG(
   1658      ("nsSocketTransport::RecoverFromError [this=%p state=%x cond=%" PRIx32
   1659       "]\n",
   1660       this, mState, static_cast<uint32_t>(mCondition)));
   1661 
   1662  if (mDoNotRetryToConnect) {
   1663    SOCKET_LOG(
   1664        ("nsSocketTransport::RecoverFromError do not retry because "
   1665         "mDoNotRetryToConnect is set [this=%p]\n",
   1666         this));
   1667    return false;
   1668  }
   1669 
   1670 #if defined(XP_UNIX)
   1671  // Unix domain connections don't have multiple addresses to try,
   1672  // so the recovery techniques here don't apply.
   1673  if (mNetAddrIsSet && mNetAddr.raw.family == AF_LOCAL) return false;
   1674 #endif
   1675 
   1676  if ((mConnectionFlags & nsSocketTransport::USE_IP_HINT_ADDRESS) &&
   1677      mCondition == NS_ERROR_UNKNOWN_HOST &&
   1678      (mState == MSG_DNS_LOOKUP_COMPLETE || mState == MSG_ENSURE_CONNECT)) {
   1679    SOCKET_LOG(("  try again without USE_IP_HINT_ADDRESS"));
   1680    mConnectionFlags &= ~nsSocketTransport::USE_IP_HINT_ADDRESS;
   1681    mState = STATE_CLOSED;
   1682    return NS_SUCCEEDED(PostEvent(MSG_ENSURE_CONNECT, NS_OK));
   1683  }
   1684 
   1685  // can only recover from errors in these states
   1686  if (mState != STATE_RESOLVING && mState != STATE_CONNECTING) {
   1687    SOCKET_LOG(("  not in a recoverable state"));
   1688    return false;
   1689  }
   1690 
   1691  nsresult rv;
   1692 
   1693 #ifdef DEBUG
   1694  {
   1695    MutexAutoLock lock(mLock);
   1696    NS_ASSERTION(!mFDconnected, "socket should not be connected");
   1697  }
   1698 #endif
   1699 
   1700  // all connection failures need to be reported to DNS so that the next
   1701  // time we will use a different address if available.
   1702  // NS_BASE_STREAM_CLOSED is not an actual connection failure, so don't report
   1703  // to DNS.
   1704  if (mState == STATE_CONNECTING && mDNSRecord &&
   1705      mCondition != NS_BASE_STREAM_CLOSED) {
   1706    mDNSRecord->ReportUnusable(SocketPort());
   1707  }
   1708 
   1709  if (mCondition != NS_ERROR_CONNECTION_REFUSED &&
   1710      mCondition != NS_ERROR_PROXY_CONNECTION_REFUSED &&
   1711      mCondition != NS_ERROR_NET_TIMEOUT &&
   1712      mCondition != NS_ERROR_UNKNOWN_HOST &&
   1713      mCondition != NS_ERROR_UNKNOWN_PROXY_HOST) {
   1714    SOCKET_LOG(("  not a recoverable error %" PRIx32,
   1715                static_cast<uint32_t>(mCondition)));
   1716    return false;
   1717  }
   1718 
   1719  bool tryAgain = false;
   1720 
   1721  if ((mState == STATE_CONNECTING) && mDNSRecord) {
   1722    if (mNetAddr.raw.family == AF_INET) {
   1723      if (mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
   1724        glean::network::ipv4_and_ipv6_address_connectivity
   1725            .AccumulateSingleSample(UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS);
   1726      }
   1727    } else if (mNetAddr.raw.family == AF_INET6) {
   1728      if (mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
   1729        glean::network::ipv4_and_ipv6_address_connectivity
   1730            .AccumulateSingleSample(UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS);
   1731      }
   1732    }
   1733  }
   1734 
   1735  if (mConnectionFlags & RETRY_WITH_DIFFERENT_IP_FAMILY &&
   1736      mCondition == NS_ERROR_UNKNOWN_HOST && mState == STATE_RESOLVING &&
   1737      !mProxyTransparentResolvesHost) {
   1738    SOCKET_LOG(("  trying lookup again with opposite ip family\n"));
   1739    mConnectionFlags ^= (DISABLE_IPV6 | DISABLE_IPV4);
   1740    mConnectionFlags &= ~RETRY_WITH_DIFFERENT_IP_FAMILY;
   1741    // This will tell the consuming half-open to reset preference on the
   1742    // connection entry
   1743    mResetFamilyPreference = true;
   1744    tryAgain = true;
   1745  }
   1746 
   1747  // try next ip address only if past the resolver stage...
   1748  if (mState == STATE_CONNECTING && mDNSRecord) {
   1749    nsresult rv = mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
   1750    mDNSRecord->IsTRR(&mResolvedByTRR);
   1751    mDNSRecord->GetEffectiveTRRMode(&mEffectiveTRRMode);
   1752    mDNSRecord->GetTrrSkipReason(&mTRRSkipReason);
   1753    if (NS_SUCCEEDED(rv)) {
   1754      SOCKET_LOG(("  trying again with next ip address\n"));
   1755      tryAgain = true;
   1756    } else if (mExternalDNSResolution) {
   1757      mRetryDnsIfPossible = true;
   1758      bool trrEnabled;
   1759      mDNSRecord->IsTRR(&trrEnabled);
   1760      // Bug 1648147 - If the server responded with `0.0.0.0` or `::` then we
   1761      // should intentionally not fallback to regular DNS.
   1762      if (trrEnabled && !StaticPrefs::network_trr_fallback_on_zero_response() &&
   1763          ((mNetAddr.raw.family == AF_INET && mNetAddr.inet.ip == 0) ||
   1764           (mNetAddr.raw.family == AF_INET6 && mNetAddr.inet6.ip.u64[0] == 0 &&
   1765            mNetAddr.inet6.ip.u64[1] == 0))) {
   1766        SOCKET_LOG(("  TRR returned 0.0.0.0 and there are no other IPs"));
   1767        mRetryDnsIfPossible = false;
   1768      }
   1769    } else if (mConnectionFlags & RETRY_WITH_DIFFERENT_IP_FAMILY) {
   1770      SOCKET_LOG(("  failed to connect, trying with opposite ip family\n"));
   1771      // Drop state to closed.  This will trigger new round of DNS
   1772      // resolving bellow.
   1773      mState = STATE_CLOSED;
   1774      mConnectionFlags ^= (DISABLE_IPV6 | DISABLE_IPV4);
   1775      mConnectionFlags &= ~RETRY_WITH_DIFFERENT_IP_FAMILY;
   1776      // This will tell the consuming half-open to reset preference on the
   1777      // connection entry
   1778      mResetFamilyPreference = true;
   1779      tryAgain = true;
   1780    } else if (!(mConnectionFlags & DISABLE_TRR)) {
   1781      bool trrEnabled;
   1782      mDNSRecord->IsTRR(&trrEnabled);
   1783 
   1784      // Bug 1648147 - If the server responded with `0.0.0.0` or `::` then we
   1785      // should intentionally not fallback to regular DNS.
   1786      if (!StaticPrefs::network_trr_fallback_on_zero_response() &&
   1787          ((mNetAddr.raw.family == AF_INET && mNetAddr.inet.ip == 0) ||
   1788           (mNetAddr.raw.family == AF_INET6 && mNetAddr.inet6.ip.u64[0] == 0 &&
   1789            mNetAddr.inet6.ip.u64[1] == 0))) {
   1790        SOCKET_LOG(("  TRR returned 0.0.0.0 and there are no other IPs"));
   1791      } else if (trrEnabled) {
   1792        nsIRequest::TRRMode trrMode = nsIRequest::TRR_DEFAULT_MODE;
   1793        mDNSRecord->GetEffectiveTRRMode(&trrMode);
   1794        // If current trr mode is trr only, we should not retry.
   1795        if (trrMode != nsIRequest::TRR_ONLY_MODE) {
   1796          // Drop state to closed.  This will trigger a new round of
   1797          // DNS resolving. Bypass the cache this time since the
   1798          // cached data came from TRR and failed already!
   1799          SOCKET_LOG(("  failed to connect with TRR enabled, try w/o\n"));
   1800          mState = STATE_CLOSED;
   1801          mConnectionFlags |= DISABLE_TRR | BYPASS_CACHE | REFRESH_CACHE;
   1802          tryAgain = true;
   1803        }
   1804      }
   1805    }
   1806  }
   1807 
   1808  // prepare to try again.
   1809  if (tryAgain) {
   1810    uint32_t msg;
   1811 
   1812    if (mState == STATE_CONNECTING) {
   1813      mState = STATE_RESOLVING;
   1814      msg = MSG_DNS_LOOKUP_COMPLETE;
   1815    } else {
   1816      mState = STATE_CLOSED;
   1817      msg = MSG_ENSURE_CONNECT;
   1818    }
   1819 
   1820    rv = PostEvent(msg, NS_OK);
   1821    if (NS_FAILED(rv)) tryAgain = false;
   1822  }
   1823 
   1824  return tryAgain;
   1825 }
   1826 
   1827 // called on the socket thread only
   1828 void nsSocketTransport::OnMsgInputClosed(nsresult reason) {
   1829  SOCKET_LOG(("nsSocketTransport::OnMsgInputClosed [this=%p reason=%" PRIx32
   1830              "]\n",
   1831              this, static_cast<uint32_t>(reason)));
   1832 
   1833  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1834 
   1835  mInputClosed = true;
   1836  // check if event should affect entire transport
   1837  if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED)) {
   1838    mCondition = reason;  // XXX except if NS_FAILED(mCondition), right??
   1839  } else if (mOutputClosed) {
   1840    mCondition =
   1841        NS_BASE_STREAM_CLOSED;  // XXX except if NS_FAILED(mCondition), right??
   1842  } else {
   1843    if (mState == STATE_TRANSFERRING) mPollFlags &= ~PR_POLL_READ;
   1844    mInput->OnSocketReady(reason);
   1845  }
   1846 }
   1847 
   1848 // called on the socket thread only
   1849 void nsSocketTransport::OnMsgOutputClosed(nsresult reason) {
   1850  SOCKET_LOG(("nsSocketTransport::OnMsgOutputClosed [this=%p reason=%" PRIx32
   1851              "]\n",
   1852              this, static_cast<uint32_t>(reason)));
   1853 
   1854  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1855 
   1856  mOutputClosed = true;
   1857  // check if event should affect entire transport
   1858  if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED)) {
   1859    mCondition = reason;  // XXX except if NS_FAILED(mCondition), right??
   1860  } else if (mInputClosed) {
   1861    mCondition =
   1862        NS_BASE_STREAM_CLOSED;  // XXX except if NS_FAILED(mCondition), right??
   1863  } else {
   1864    if (mState == STATE_TRANSFERRING) mPollFlags &= ~PR_POLL_WRITE;
   1865    mOutput->OnSocketReady(reason);
   1866  }
   1867 }
   1868 
   1869 void nsSocketTransport::OnSocketConnected() {
   1870  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1871  SOCKET_LOG(("  advancing to STATE_TRANSFERRING\n"));
   1872 
   1873  mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
   1874  mState = STATE_TRANSFERRING;
   1875 
   1876  // Set the m*AddrIsSet flags only when state has reached TRANSFERRING
   1877  // because we need to make sure its value does not change due to failover
   1878  mNetAddrIsSet = true;
   1879 
   1880  // assign mFD (must do this within the transport lock), but take care not
   1881  // to trample over mFDref if mFD is already set.
   1882  {
   1883    MutexAutoLock lock(mLock);
   1884    NS_ASSERTION(mFD.IsInitialized(), "no socket");
   1885    NS_ASSERTION(mFDref == 1, "wrong socket ref count");
   1886    SetSocketName(mFD);
   1887    mFDconnected = true;
   1888    mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
   1889  }
   1890 
   1891  // Ensure keepalive is configured correctly if previously enabled.
   1892  if (mKeepaliveEnabled) {
   1893    nsresult rv = SetKeepaliveEnabledInternal(true);
   1894    if (NS_WARN_IF(NS_FAILED(rv))) {
   1895      SOCKET_LOG(("  SetKeepaliveEnabledInternal failed rv[0x%" PRIx32 "]",
   1896                  static_cast<uint32_t>(rv)));
   1897    }
   1898  }
   1899 
   1900  SendStatus(NS_NET_STATUS_CONNECTED_TO);
   1901 }
   1902 
   1903 void nsSocketTransport::SetSocketName(PRFileDesc* fd) {
   1904  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1905  if (mSelfAddrIsSet) {
   1906    return;
   1907  }
   1908 
   1909  PRNetAddr prAddr;
   1910  memset(&prAddr, 0, sizeof(prAddr));
   1911  if (PR_GetSockName(fd, &prAddr) == PR_SUCCESS) {
   1912    PRNetAddrToNetAddr(&prAddr, &mSelfAddr);
   1913    mSelfAddrIsSet = true;
   1914  }
   1915 }
   1916 
   1917 PRFileDesc* nsSocketTransport::GetFD_Locked() {
   1918  mLock.AssertCurrentThreadOwns();
   1919 
   1920  // mFD is not available to the streams while disconnected.
   1921  if (!mFDconnected) return nullptr;
   1922 
   1923  if (mFD.IsInitialized()) mFDref++;
   1924 
   1925  return mFD;
   1926 }
   1927 
   1928 class ThunkPRClose : public Runnable {
   1929 public:
   1930  explicit ThunkPRClose(PRFileDesc* fd)
   1931      : Runnable("net::ThunkPRClose"), mFD(fd) {}
   1932 
   1933  NS_IMETHOD Run() override {
   1934    nsSocketTransport::CloseSocket(
   1935        mFD, gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
   1936    return NS_OK;
   1937  }
   1938 
   1939 private:
   1940  PRFileDesc* mFD;
   1941 };
   1942 
   1943 void STS_PRCloseOnSocketTransport(PRFileDesc* fd, bool lingerPolarity,
   1944                                  int16_t lingerTimeout) {
   1945  if (gSocketTransportService) {
   1946    // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
   1947    gSocketTransportService->Dispatch(new ThunkPRClose(fd), NS_DISPATCH_NORMAL);
   1948  } else {
   1949    // something horrible has happened
   1950    NS_ASSERTION(gSocketTransportService, "No STS service");
   1951  }
   1952 }
   1953 
   1954 void nsSocketTransport::ReleaseFD_Locked(PRFileDesc* fd) {
   1955  mLock.AssertCurrentThreadOwns();
   1956 
   1957  NS_ASSERTION(mFD == fd, "wrong fd");
   1958 
   1959  if (--mFDref == 0) {
   1960    if (gIOService->IsNetTearingDown() &&
   1961        ((PR_IntervalNow() - gIOService->NetTearingDownStarted()) >
   1962         gSocketTransportService->MaxTimeForPrClosePref())) {
   1963      // If shutdown last to long, let the socket leak and do not close it.
   1964      SOCKET_LOG(("Intentional leak"));
   1965    } else {
   1966      if (mLingerPolarity || mLingerTimeout) {
   1967        PRSocketOptionData socket_linger;
   1968        socket_linger.option = PR_SockOpt_Linger;
   1969        socket_linger.value.linger.polarity = mLingerPolarity;
   1970        socket_linger.value.linger.linger = mLingerTimeout;
   1971        PR_SetSocketOption(mFD, &socket_linger);
   1972      }
   1973      if (OnSocketThread()) {
   1974        SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%p]\n", this));
   1975        CloseSocket(
   1976            mFD, mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
   1977      } else {
   1978        // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
   1979        STS_PRCloseOnSocketTransport(mFD, mLingerPolarity, mLingerTimeout);
   1980      }
   1981    }
   1982    mFD = nullptr;
   1983  }
   1984 }
   1985 
   1986 //-----------------------------------------------------------------------------
   1987 // socket event handler impl
   1988 
   1989 void nsSocketTransport::OnSocketEvent(uint32_t type, nsresult status,
   1990                                      nsISupports* param,
   1991                                      std::function<void()>&& task) {
   1992  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1993  SOCKET_LOG(
   1994      ("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%" PRIx32
   1995       " param=%p]\n",
   1996       this, type, static_cast<uint32_t>(status), param));
   1997 
   1998  if (NS_FAILED(mCondition)) {
   1999    // block event since we're apparently already dead.
   2000    SOCKET_LOG(("  blocking event [condition=%" PRIx32 "]\n",
   2001                static_cast<uint32_t>(mCondition)));
   2002    //
   2003    // notify input/output streams in case either has a pending notify.
   2004    //
   2005    mInput->OnSocketReady(mCondition);
   2006    mOutput->OnSocketReady(mCondition);
   2007    return;
   2008  }
   2009 
   2010  switch (type) {
   2011    case MSG_ENSURE_CONNECT:
   2012      SOCKET_LOG(("  MSG_ENSURE_CONNECT\n"));
   2013      if (task) {
   2014        task();
   2015      }
   2016 
   2017      // Apply port remapping here so that we do it on the socket thread and
   2018      // before we process the resolved DNS name or create the socket the first
   2019      // time.
   2020      if (!mPortRemappingApplied) {
   2021        mPortRemappingApplied = true;
   2022 
   2023        mSocketTransportService->ApplyPortRemap(&mPort);
   2024        mSocketTransportService->ApplyPortRemap(&mOriginPort);
   2025      }
   2026 
   2027      //
   2028      // ensure that we have created a socket, attached it, and have a
   2029      // connection.
   2030      //
   2031      if (mState == STATE_CLOSED) {
   2032        // Unix domain sockets are ready to connect; mNetAddr is all we
   2033        // need. Internet address families require a DNS lookup (or possibly
   2034        // several) before we can connect.
   2035 #if defined(XP_UNIX)
   2036        if (mNetAddrIsSet && mNetAddr.raw.family == AF_LOCAL) {
   2037          mCondition = InitiateSocket();
   2038        } else {
   2039 #else
   2040        {
   2041 #endif
   2042          mCondition = ResolveHost();
   2043        }
   2044 
   2045      } else {
   2046        SOCKET_LOG(("  ignoring redundant event\n"));
   2047      }
   2048      break;
   2049 
   2050    case MSG_DNS_LOOKUP_COMPLETE:
   2051      if (mDNSRequest) {  // only send this if we actually resolved anything
   2052        SendStatus(NS_NET_STATUS_RESOLVED_HOST);
   2053      }
   2054 
   2055      SOCKET_LOG(("  MSG_DNS_LOOKUP_COMPLETE\n"));
   2056      mDNSRequest = nullptr;
   2057 
   2058      if (mDNSRecord) {
   2059        mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
   2060        mDNSRecord->IsTRR(&mResolvedByTRR);
   2061        mDNSRecord->GetEffectiveTRRMode(&mEffectiveTRRMode);
   2062        mDNSRecord->GetTrrSkipReason(&mTRRSkipReason);
   2063      }
   2064      // status contains DNS lookup status
   2065      if (NS_FAILED(status)) {
   2066        // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP
   2067        // proxy host is not found, so we fixup the error code.
   2068        // For SOCKS proxies (mProxyTransparent == true), the socket
   2069        // transport resolves the real host here, so there's no fixup
   2070        // (see bug 226943).
   2071        if ((status == NS_ERROR_UNKNOWN_HOST) && !mProxyTransparent &&
   2072            !mProxyHost.IsEmpty()) {
   2073          mCondition = NS_ERROR_UNKNOWN_PROXY_HOST;
   2074        } else {
   2075          mCondition = status;
   2076        }
   2077      } else if (mState == STATE_RESOLVING) {
   2078        mCondition = InitiateSocket();
   2079      }
   2080      break;
   2081 
   2082    case MSG_RETRY_INIT_SOCKET:
   2083      mCondition = InitiateSocket();
   2084      break;
   2085 
   2086    case MSG_INPUT_CLOSED:
   2087      SOCKET_LOG(("  MSG_INPUT_CLOSED\n"));
   2088      OnMsgInputClosed(status);
   2089      break;
   2090 
   2091    case MSG_INPUT_PENDING:
   2092      SOCKET_LOG(("  MSG_INPUT_PENDING\n"));
   2093      OnMsgInputPending();
   2094      break;
   2095 
   2096    case MSG_OUTPUT_CLOSED:
   2097      SOCKET_LOG(("  MSG_OUTPUT_CLOSED\n"));
   2098      OnMsgOutputClosed(status);
   2099      break;
   2100 
   2101    case MSG_OUTPUT_PENDING:
   2102      SOCKET_LOG(("  MSG_OUTPUT_PENDING\n"));
   2103      OnMsgOutputPending();
   2104      break;
   2105    case MSG_TIMEOUT_CHANGED:
   2106      SOCKET_LOG(("  MSG_TIMEOUT_CHANGED\n"));
   2107      {
   2108        MutexAutoLock lock(mLock);
   2109        mPollTimeout =
   2110            mTimeouts[(mState == STATE_TRANSFERRING) ? TIMEOUT_READ_WRITE
   2111                                                     : TIMEOUT_CONNECT];
   2112      }
   2113      break;
   2114    default:
   2115      SOCKET_LOG(("  unhandled event!\n"));
   2116  }
   2117 
   2118  if (NS_FAILED(mCondition)) {
   2119    SOCKET_LOG(("  after event [this=%p cond=%" PRIx32 "]\n", this,
   2120                static_cast<uint32_t>(mCondition)));
   2121    if (!mAttached) {  // need to process this error ourselves...
   2122      OnSocketDetached(nullptr);
   2123    }
   2124  } else if (mPollFlags == PR_POLL_EXCEPT) {
   2125    mPollFlags = 0;  // make idle
   2126  }
   2127 }
   2128 
   2129 uint64_t nsSocketTransport::ByteCountReceived() { return mInput->ByteCount(); }
   2130 
   2131 uint64_t nsSocketTransport::ByteCountSent() { return mOutput->ByteCount(); }
   2132 
   2133 //-----------------------------------------------------------------------------
   2134 // socket handler impl
   2135 
   2136 void nsSocketTransport::OnSocketReady(PRFileDesc* fd, int16_t outFlags) {
   2137  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2138  SOCKET_LOG1(("nsSocketTransport::OnSocketReady [this=%p outFlags=%hd]\n",
   2139               this, outFlags));
   2140 
   2141  if (outFlags == -1) {
   2142    SOCKET_LOG(("socket timeout expired\n"));
   2143    mCondition = NS_ERROR_NET_TIMEOUT;
   2144    return;
   2145  }
   2146 
   2147  if (mState == STATE_TRANSFERRING) {
   2148    // if waiting to write and socket is writable or hit an exception.
   2149    if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
   2150      // assume that we won't need to poll any longer (the stream will
   2151      // request that we poll again if it is still pending).
   2152      mPollFlags &= ~PR_POLL_WRITE;
   2153      mOutput->OnSocketReady(NS_OK);
   2154    }
   2155    // if waiting to read and socket is readable or hit an exception.
   2156    if ((mPollFlags & PR_POLL_READ) && (outFlags & ~PR_POLL_WRITE)) {
   2157      // assume that we won't need to poll any longer (the stream will
   2158      // request that we poll again if it is still pending).
   2159      mPollFlags &= ~PR_POLL_READ;
   2160      mInput->OnSocketReady(NS_OK);
   2161    }
   2162    // Update poll timeout in case it was changed
   2163    {
   2164      MutexAutoLock lock(mLock);
   2165      mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
   2166    }
   2167  } else if ((mState == STATE_CONNECTING) && !gIOService->IsNetTearingDown()) {
   2168    // We do not need to do PR_ConnectContinue when we are already
   2169    // shutting down.
   2170 
   2171    // We use PRIntervalTime here because we need
   2172    // nsIOService::LastOfflineStateChange time and
   2173    // nsIOService::LastConectivityChange time to be atomic.
   2174    PRIntervalTime connectStarted = 0;
   2175    if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
   2176      connectStarted = PR_IntervalNow();
   2177    }
   2178 
   2179    PRStatus status = PR_ConnectContinue(fd, outFlags);
   2180 
   2181    if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
   2182        connectStarted) {
   2183      SendPRBlockingTelemetry(
   2184          connectStarted,
   2185          glean::networking::prconnectcontinue_blocking_time_normal,
   2186          glean::networking::prconnectcontinue_blocking_time_shutdown,
   2187          glean::networking::
   2188              prconnectcontinue_blocking_time_connectivity_change,
   2189          glean::networking::prconnectcontinue_blocking_time_link_change,
   2190          glean::networking::prconnectcontinue_blocking_time_offline);
   2191    }
   2192 
   2193    if (status == PR_SUCCESS) {
   2194      //
   2195      // we are connected!
   2196      //
   2197      OnSocketConnected();
   2198 
   2199      if (mNetAddr.raw.family == AF_INET) {
   2200        if (mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
   2201          glean::network::ipv4_and_ipv6_address_connectivity
   2202              .AccumulateSingleSample(SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS);
   2203        }
   2204      } else if (mNetAddr.raw.family == AF_INET6) {
   2205        if (mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
   2206          glean::network::ipv4_and_ipv6_address_connectivity
   2207              .AccumulateSingleSample(SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS);
   2208        }
   2209      }
   2210    } else {
   2211      PRErrorCode code = PR_GetError();
   2212 #if defined(TEST_CONNECT_ERRORS)
   2213      code = RandomizeConnectError(code);
   2214 #endif
   2215      //
   2216      // If the connect is still not ready, then continue polling...
   2217      //
   2218      if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
   2219        // Set up the select flags for connect...
   2220        mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
   2221        // Update poll timeout in case it was changed
   2222        {
   2223          MutexAutoLock lock(mLock);
   2224          mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
   2225        }
   2226      }
   2227      //
   2228      // The SOCKS proxy rejected our request. Find out why.
   2229      //
   2230      else if (PR_UNKNOWN_ERROR == code && mProxyTransparent &&
   2231               !mProxyHost.IsEmpty()) {
   2232        code = PR_GetOSError();
   2233        mCondition = ErrorAccordingToNSPR(code);
   2234      } else {
   2235        //
   2236        // else, the connection failed...
   2237        //
   2238        mCondition = ErrorAccordingToNSPR(code);
   2239        if ((mCondition == NS_ERROR_CONNECTION_REFUSED) &&
   2240            !mProxyHost.IsEmpty()) {
   2241          mCondition = NS_ERROR_PROXY_CONNECTION_REFUSED;
   2242        }
   2243        SOCKET_LOG(("  connection failed! [reason=%" PRIx32 "]\n",
   2244                    static_cast<uint32_t>(mCondition)));
   2245      }
   2246    }
   2247  } else if ((mState == STATE_CONNECTING) && gIOService->IsNetTearingDown()) {
   2248    // We do not need to do PR_ConnectContinue when we are already
   2249    // shutting down.
   2250    SOCKET_LOG(
   2251        ("We are in shutdown so skip PR_ConnectContinue and set "
   2252         "and error.\n"));
   2253    mCondition = NS_ERROR_ABORT;
   2254  } else {
   2255    NS_ERROR("unexpected socket state");
   2256    mCondition = NS_ERROR_UNEXPECTED;
   2257  }
   2258 
   2259  if (mPollFlags == PR_POLL_EXCEPT) mPollFlags = 0;  // make idle
   2260 }
   2261 
   2262 // called on the socket thread only
   2263 void nsSocketTransport::OnSocketDetached(PRFileDesc* fd) {
   2264  SOCKET_LOG(("nsSocketTransport::OnSocketDetached [this=%p cond=%" PRIx32
   2265              "]\n",
   2266              this, static_cast<uint32_t>(mCondition)));
   2267 
   2268  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2269 
   2270  mAttached = false;
   2271 
   2272  // if we didn't initiate this detach, then be sure to pass an error
   2273  // condition up to our consumers.  (e.g., STS is shutting down.)
   2274  if (NS_SUCCEEDED(mCondition)) {
   2275    if (gIOService->IsOffline()) {
   2276      mCondition = NS_ERROR_OFFLINE;
   2277    } else {
   2278      mCondition = NS_ERROR_ABORT;
   2279    }
   2280  }
   2281 
   2282  // If we are not shutting down try again.
   2283  if (!gIOService->IsNetTearingDown() && RecoverFromError()) {
   2284    mCondition = NS_OK;
   2285  } else {
   2286    mState = STATE_CLOSED;
   2287 
   2288    // make sure there isn't any pending DNS request
   2289    if (mDNSRequest) {
   2290      mDNSRequest->Cancel(NS_ERROR_ABORT);
   2291      mDNSRequest = nullptr;
   2292    }
   2293 
   2294    //
   2295    // notify input/output streams
   2296    //
   2297    mInput->OnSocketReady(mCondition);
   2298    mOutput->OnSocketReady(mCondition);
   2299    if (gIOService->IsNetTearingDown()) {
   2300      if (mInputCopyContext) {
   2301        NS_CancelAsyncCopy(mInputCopyContext, mCondition);
   2302      }
   2303      if (mOutputCopyContext) {
   2304        NS_CancelAsyncCopy(mOutputCopyContext, mCondition);
   2305      }
   2306    }
   2307  }
   2308 
   2309  if (mCondition == NS_ERROR_NET_RESET && mDNSRecord &&
   2310      mOutput->ByteCount() == 0) {
   2311    // If we are here, it's likely that we are retrying a transaction. Blocking
   2312    // the already used address could increase the successful rate of the retry.
   2313    mDNSRecord->ReportUnusable(SocketPort());
   2314  }
   2315 
   2316  // finally, release our reference to the socket (must do this within
   2317  // the transport lock) possibly closing the socket. Also release our
   2318  // listeners to break potential refcount cycles.
   2319 
   2320  // We should be careful not to release mEventSink and mCallbacks while
   2321  // we're locked, because releasing it might require acquiring the lock
   2322  // again, so we just null out mEventSink and mCallbacks while we're
   2323  // holding the lock, and let the stack based objects' destuctors take
   2324  // care of destroying it if needed.
   2325  nsCOMPtr<nsIInterfaceRequestor> ourCallbacks;
   2326  nsCOMPtr<nsITransportEventSink> ourEventSink;
   2327  {
   2328    MutexAutoLock lock(mLock);
   2329    if (mFD.IsInitialized()) {
   2330      ReleaseFD_Locked(mFD);
   2331      // flag mFD as unusable; this prevents other consumers from
   2332      // acquiring a reference to mFD.
   2333      mFDconnected = false;
   2334    }
   2335 
   2336    // We must release mCallbacks and mEventSink to avoid memory leak
   2337    // but only when RecoverFromError() above failed. Otherwise we lose
   2338    // link with UI and security callbacks on next connection attempt
   2339    // round. That would lead e.g. to a broken certificate exception page.
   2340    if (NS_FAILED(mCondition)) {
   2341      mCallbacks.swap(ourCallbacks);
   2342      mEventSink.swap(ourEventSink);
   2343    }
   2344  }
   2345 }
   2346 
   2347 void nsSocketTransport::IsLocal(bool* aIsLocal) {
   2348  {
   2349    MutexAutoLock lock(mLock);
   2350 
   2351 #if defined(XP_UNIX)
   2352    // Unix-domain sockets are always local.
   2353    if (mNetAddr.raw.family == PR_AF_LOCAL) {
   2354      *aIsLocal = true;
   2355      return;
   2356    }
   2357 #endif
   2358 
   2359    *aIsLocal = mNetAddr.IsLoopbackAddr();
   2360  }
   2361 }
   2362 
   2363 //-----------------------------------------------------------------------------
   2364 // xpcom api
   2365 
   2366 NS_IMPL_ISUPPORTS(nsSocketTransport, nsISocketTransport, nsITransport,
   2367                  nsIDNSListener, nsIClassInfo, nsIInterfaceRequestor)
   2368 NS_IMPL_CI_INTERFACE_GETTER(nsSocketTransport, nsISocketTransport, nsITransport,
   2369                            nsIDNSListener, nsIInterfaceRequestor)
   2370 
   2371 NS_IMETHODIMP
   2372 nsSocketTransport::OpenInputStream(uint32_t flags, uint32_t segsize,
   2373                                   uint32_t segcount,
   2374                                   nsIInputStream** aResult) {
   2375  SOCKET_LOG(
   2376      ("nsSocketTransport::OpenInputStream [this=%p flags=%x]\n", this, flags));
   2377 
   2378  NS_ENSURE_TRUE(!mInput->IsReferenced(), NS_ERROR_UNEXPECTED);
   2379 
   2380  nsresult rv;
   2381  nsCOMPtr<nsIAsyncInputStream> pipeIn;
   2382  nsCOMPtr<nsIInputStream> result;
   2383  nsCOMPtr<nsISupports> inputCopyContext;
   2384 
   2385  if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
   2386    // XXX if the caller wants blocking, then the caller also gets buffered!
   2387    // bool openBuffered = !(flags & OPEN_UNBUFFERED);
   2388    bool openBlocking = (flags & OPEN_BLOCKING);
   2389 
   2390    net_ResolveSegmentParams(segsize, segcount);
   2391 
   2392    // create a pipe
   2393    nsCOMPtr<nsIAsyncOutputStream> pipeOut;
   2394    NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), !openBlocking,
   2395                true, segsize, segcount);
   2396 
   2397    // async copy from socket to pipe
   2398    rv = NS_AsyncCopy(mInput.get(), pipeOut, mSocketTransportService,
   2399                      NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize, nullptr, nullptr,
   2400                      true, true, getter_AddRefs(inputCopyContext));
   2401    if (NS_FAILED(rv)) return rv;
   2402 
   2403    result = pipeIn;
   2404  } else {
   2405    result = mInput.get();
   2406  }
   2407 
   2408  // flag input stream as open
   2409  mInputClosed = false;
   2410  // mInputCopyContext can be only touched on socket thread
   2411  auto task = [self = RefPtr{this}, inputCopyContext(inputCopyContext)]() {
   2412    MOZ_ASSERT(OnSocketThread());
   2413    self->mInputCopyContext = inputCopyContext;
   2414  };
   2415  rv = PostEvent(MSG_ENSURE_CONNECT, NS_OK, nullptr, std::move(task));
   2416  if (NS_FAILED(rv)) {
   2417    return rv;
   2418  }
   2419 
   2420  result.forget(aResult);
   2421  return NS_OK;
   2422 }
   2423 
   2424 NS_IMETHODIMP
   2425 nsSocketTransport::OpenOutputStream(uint32_t flags, uint32_t segsize,
   2426                                    uint32_t segcount,
   2427                                    nsIOutputStream** aResult) {
   2428  SOCKET_LOG(("nsSocketTransport::OpenOutputStream [this=%p flags=%x]\n", this,
   2429              flags));
   2430 
   2431  NS_ENSURE_TRUE(!mOutput->IsReferenced(), NS_ERROR_UNEXPECTED);
   2432 
   2433  nsresult rv;
   2434  nsCOMPtr<nsIAsyncOutputStream> pipeOut;
   2435  nsCOMPtr<nsIOutputStream> result;
   2436  nsCOMPtr<nsISupports> outputCopyContext;
   2437  if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
   2438    // XXX if the caller wants blocking, then the caller also gets buffered!
   2439    // bool openBuffered = !(flags & OPEN_UNBUFFERED);
   2440    bool openBlocking = (flags & OPEN_BLOCKING);
   2441 
   2442    net_ResolveSegmentParams(segsize, segcount);
   2443 
   2444    // create a pipe
   2445    nsCOMPtr<nsIAsyncInputStream> pipeIn;
   2446    NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), true,
   2447                !openBlocking, segsize, segcount);
   2448 
   2449    // async copy from socket to pipe
   2450    rv = NS_AsyncCopy(pipeIn, mOutput.get(), mSocketTransportService,
   2451                      NS_ASYNCCOPY_VIA_READSEGMENTS, segsize, nullptr, nullptr,
   2452                      true, true, getter_AddRefs(outputCopyContext));
   2453    if (NS_FAILED(rv)) return rv;
   2454 
   2455    result = pipeOut;
   2456  } else {
   2457    result = mOutput.get();
   2458  }
   2459 
   2460  // flag output stream as open
   2461  mOutputClosed = false;
   2462 
   2463  // mOutputCopyContext can be only touched on socket thread
   2464  auto task = [self = RefPtr{this}, outputCopyContext(outputCopyContext)]() {
   2465    MOZ_ASSERT(OnSocketThread());
   2466    self->mOutputCopyContext = outputCopyContext;
   2467  };
   2468  rv = PostEvent(MSG_ENSURE_CONNECT, NS_OK, nullptr, std::move(task));
   2469  if (NS_FAILED(rv)) return rv;
   2470 
   2471  result.forget(aResult);
   2472  return NS_OK;
   2473 }
   2474 
   2475 NS_IMETHODIMP
   2476 nsSocketTransport::Close(nsresult reason) {
   2477  SOCKET_LOG(("nsSocketTransport::Close %p reason=%" PRIx32, this,
   2478              static_cast<uint32_t>(reason)));
   2479 
   2480  if (NS_SUCCEEDED(reason)) reason = NS_BASE_STREAM_CLOSED;
   2481 
   2482  mDoNotRetryToConnect = true;
   2483 
   2484  mInput->CloseWithStatus(reason);
   2485  mOutput->CloseWithStatus(reason);
   2486  return NS_OK;
   2487 }
   2488 
   2489 NS_IMETHODIMP
   2490 nsSocketTransport::GetTlsSocketControl(nsITLSSocketControl** tlsSocketControl) {
   2491  MutexAutoLock lock(mLock);
   2492  *tlsSocketControl = do_AddRef(mTLSSocketControl).take();
   2493  return NS_OK;
   2494 }
   2495 
   2496 NS_IMETHODIMP
   2497 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor** callbacks) {
   2498  MutexAutoLock lock(mLock);
   2499  *callbacks = do_AddRef(mCallbacks).take();
   2500  return NS_OK;
   2501 }
   2502 
   2503 NS_IMETHODIMP
   2504 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor* callbacks) {
   2505  nsCOMPtr<nsIInterfaceRequestor> threadsafeCallbacks;
   2506  NS_NewNotificationCallbacksAggregation(callbacks, nullptr,
   2507                                         GetCurrentSerialEventTarget(),
   2508                                         getter_AddRefs(threadsafeCallbacks));
   2509  MutexAutoLock lock(mLock);
   2510  mCallbacks = threadsafeCallbacks;
   2511  SOCKET_LOG(("Reset callbacks for tlsSocketInfo=%p callbacks=%p\n",
   2512              mTLSSocketControl.get(), mCallbacks.get()));
   2513  return NS_OK;
   2514 }
   2515 
   2516 NS_IMETHODIMP
   2517 nsSocketTransport::SetEventSink(nsITransportEventSink* sink,
   2518                                nsIEventTarget* target) {
   2519  nsCOMPtr<nsITransportEventSink> temp;
   2520  if (target) {
   2521    nsresult rv =
   2522        net_NewTransportEventSinkProxy(getter_AddRefs(temp), sink, target);
   2523    if (NS_FAILED(rv)) return rv;
   2524    sink = temp.get();
   2525  }
   2526 
   2527  MutexAutoLock lock(mLock);
   2528  mEventSink = sink;
   2529  return NS_OK;
   2530 }
   2531 
   2532 NS_IMETHODIMP
   2533 nsSocketTransport::IsAlive(bool* result) {
   2534  *result = false;
   2535 
   2536  nsresult conditionWhileLocked = NS_OK;
   2537  PRFileDescAutoLock fd(this, &conditionWhileLocked);
   2538  if (NS_FAILED(conditionWhileLocked) || !fd.IsInitialized()) {
   2539    return NS_OK;
   2540  }
   2541 
   2542  // XXX do some idle-time based checks??
   2543 
   2544  char c;
   2545  int32_t rval = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
   2546 
   2547  if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR)) {
   2548    *result = true;
   2549  }
   2550 
   2551  return NS_OK;
   2552 }
   2553 
   2554 NS_IMETHODIMP
   2555 nsSocketTransport::GetHost(nsACString& host) {
   2556  host = SocketHost();
   2557  return NS_OK;
   2558 }
   2559 
   2560 NS_IMETHODIMP
   2561 nsSocketTransport::GetPort(int32_t* port) {
   2562  *port = (int32_t)SocketPort();
   2563  return NS_OK;
   2564 }
   2565 
   2566 NS_IMETHODIMP
   2567 nsSocketTransport::GetScriptableOriginAttributes(
   2568    JSContext* aCx, JS::MutableHandle<JS::Value> aOriginAttributes) {
   2569  if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aOriginAttributes))) {
   2570    return NS_ERROR_FAILURE;
   2571  }
   2572  return NS_OK;
   2573 }
   2574 
   2575 NS_IMETHODIMP
   2576 nsSocketTransport::SetScriptableOriginAttributes(
   2577    JSContext* aCx, JS::Handle<JS::Value> aOriginAttributes) {
   2578  MutexAutoLock lock(mLock);
   2579  NS_ENSURE_FALSE(mFD.IsInitialized(), NS_ERROR_FAILURE);
   2580 
   2581  OriginAttributes attrs;
   2582  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
   2583    return NS_ERROR_INVALID_ARG;
   2584  }
   2585 
   2586  mOriginAttributes = attrs;
   2587  return NS_OK;
   2588 }
   2589 
   2590 nsresult nsSocketTransport::GetOriginAttributes(
   2591    OriginAttributes* aOriginAttributes) {
   2592  NS_ENSURE_ARG(aOriginAttributes);
   2593  *aOriginAttributes = mOriginAttributes;
   2594  return NS_OK;
   2595 }
   2596 
   2597 nsresult nsSocketTransport::SetOriginAttributes(
   2598    const OriginAttributes& aOriginAttributes) {
   2599  MutexAutoLock lock(mLock);
   2600  NS_ENSURE_FALSE(mFD.IsInitialized(), NS_ERROR_FAILURE);
   2601 
   2602  mOriginAttributes = aOriginAttributes;
   2603  return NS_OK;
   2604 }
   2605 
   2606 NS_IMETHODIMP
   2607 nsSocketTransport::GetPeerAddr(NetAddr* addr) {
   2608  // once we are in the connected state, mNetAddr will not change.
   2609  // so if we can verify that we are in the connected state, then
   2610  // we can freely access mNetAddr from any thread without being
   2611  // inside a critical section.
   2612 
   2613  if (!mNetAddrIsSet) {
   2614    SOCKET_LOG(
   2615        ("nsSocketTransport::GetPeerAddr [this=%p state=%d] "
   2616         "NOT_AVAILABLE because not yet connected.",
   2617         this, mState));
   2618    return NS_ERROR_NOT_AVAILABLE;
   2619  }
   2620 
   2621  *addr = mNetAddr;
   2622  return NS_OK;
   2623 }
   2624 
   2625 NS_IMETHODIMP
   2626 nsSocketTransport::GetSelfAddr(NetAddr* addr) {
   2627  // once we are in the connected state, mSelfAddr will not change.
   2628  // so if we can verify that we are in the connected state, then
   2629  // we can freely access mSelfAddr from any thread without being
   2630  // inside a critical section.
   2631 
   2632  if (!mSelfAddrIsSet) {
   2633    SOCKET_LOG(
   2634        ("nsSocketTransport::GetSelfAddr [this=%p state=%d] "
   2635         "NOT_AVAILABLE because not yet connected.",
   2636         this, mState));
   2637    return NS_ERROR_NOT_AVAILABLE;
   2638  }
   2639 
   2640  *addr = mSelfAddr;
   2641  return NS_OK;
   2642 }
   2643 
   2644 NS_IMETHODIMP
   2645 nsSocketTransport::Bind(NetAddr* aLocalAddr) {
   2646  NS_ENSURE_ARG(aLocalAddr);
   2647 
   2648  MutexAutoLock lock(mLock);
   2649  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2650  if (mAttached) {
   2651    return NS_ERROR_FAILURE;
   2652  }
   2653 
   2654  mBindAddr = MakeUnique<NetAddr>(*aLocalAddr);
   2655 
   2656  return NS_OK;
   2657 }
   2658 
   2659 NS_IMETHODIMP
   2660 nsSocketTransport::GetScriptablePeerAddr(nsINetAddr** addr) {
   2661  NetAddr rawAddr;
   2662 
   2663  nsresult rv;
   2664  rv = GetPeerAddr(&rawAddr);
   2665  if (NS_FAILED(rv)) return rv;
   2666 
   2667  RefPtr<nsNetAddr> netaddr = new nsNetAddr(&rawAddr);
   2668  netaddr.forget(addr);
   2669  return NS_OK;
   2670 }
   2671 
   2672 NS_IMETHODIMP
   2673 nsSocketTransport::GetScriptableSelfAddr(nsINetAddr** addr) {
   2674  NetAddr rawAddr;
   2675 
   2676  nsresult rv;
   2677  rv = GetSelfAddr(&rawAddr);
   2678  if (NS_FAILED(rv)) return rv;
   2679 
   2680  RefPtr<nsNetAddr> netaddr = new nsNetAddr(&rawAddr);
   2681  netaddr.forget(addr);
   2682 
   2683  return NS_OK;
   2684 }
   2685 
   2686 NS_IMETHODIMP
   2687 nsSocketTransport::GetTimeout(uint32_t type, uint32_t* value) {
   2688  NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
   2689  MutexAutoLock lock(mLock);
   2690  *value = (uint32_t)mTimeouts[type];
   2691  return NS_OK;
   2692 }
   2693 
   2694 NS_IMETHODIMP
   2695 nsSocketTransport::SetTimeout(uint32_t type, uint32_t value) {
   2696  NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
   2697 
   2698  SOCKET_LOG(("nsSocketTransport::SetTimeout %p type=%u, value=%u", this, type,
   2699              value));
   2700 
   2701  // truncate overly large timeout values.
   2702  {
   2703    MutexAutoLock lock(mLock);
   2704    mTimeouts[type] = (uint16_t)std::min<uint32_t>(value, UINT16_MAX);
   2705  }
   2706  PostEvent(MSG_TIMEOUT_CHANGED);
   2707  return NS_OK;
   2708 }
   2709 
   2710 NS_IMETHODIMP
   2711 nsSocketTransport::SetReuseAddrPort(bool reuseAddrPort) {
   2712  mReuseAddrPort = reuseAddrPort;
   2713  return NS_OK;
   2714 }
   2715 
   2716 NS_IMETHODIMP
   2717 nsSocketTransport::SetLinger(bool aPolarity, int16_t aTimeout) {
   2718  MutexAutoLock lock(mLock);
   2719 
   2720  mLingerPolarity = aPolarity;
   2721  mLingerTimeout = aTimeout;
   2722 
   2723  return NS_OK;
   2724 }
   2725 
   2726 NS_IMETHODIMP
   2727 nsSocketTransport::SetQoSBits(uint8_t aQoSBits) {
   2728  // Don't do any checking here of bits.  Why?  Because as of RFC-4594
   2729  // several different Class Selector and Assured Forwarding values
   2730  // have been defined, but that isn't to say more won't be added later.
   2731  // In that case, any checking would be an impediment to interoperating
   2732  // with newer QoS definitions.
   2733 
   2734  mQoSBits = aQoSBits;
   2735  return NS_OK;
   2736 }
   2737 
   2738 NS_IMETHODIMP
   2739 nsSocketTransport::GetQoSBits(uint8_t* aQoSBits) {
   2740  *aQoSBits = mQoSBits;
   2741  return NS_OK;
   2742 }
   2743 
   2744 NS_IMETHODIMP
   2745 nsSocketTransport::GetRecvBufferSize(uint32_t* aSize) {
   2746  PRFileDescAutoLock fd(this);
   2747  if (!fd.IsInitialized()) return NS_ERROR_NOT_CONNECTED;
   2748 
   2749  nsresult rv = NS_OK;
   2750  PRSocketOptionData opt;
   2751  opt.option = PR_SockOpt_RecvBufferSize;
   2752  if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS) {
   2753    *aSize = opt.value.recv_buffer_size;
   2754  } else {
   2755    rv = NS_ERROR_FAILURE;
   2756  }
   2757 
   2758  return rv;
   2759 }
   2760 
   2761 NS_IMETHODIMP
   2762 nsSocketTransport::GetSendBufferSize(uint32_t* aSize) {
   2763  PRFileDescAutoLock fd(this);
   2764  if (!fd.IsInitialized()) return NS_ERROR_NOT_CONNECTED;
   2765 
   2766  nsresult rv = NS_OK;
   2767  PRSocketOptionData opt;
   2768  opt.option = PR_SockOpt_SendBufferSize;
   2769  if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS) {
   2770    *aSize = opt.value.send_buffer_size;
   2771  } else {
   2772    rv = NS_ERROR_FAILURE;
   2773  }
   2774 
   2775  return rv;
   2776 }
   2777 
   2778 NS_IMETHODIMP
   2779 nsSocketTransport::SetRecvBufferSize(uint32_t aSize) {
   2780  PRFileDescAutoLock fd(this);
   2781  if (!fd.IsInitialized()) return NS_ERROR_NOT_CONNECTED;
   2782 
   2783  nsresult rv = NS_OK;
   2784  PRSocketOptionData opt;
   2785  opt.option = PR_SockOpt_RecvBufferSize;
   2786  opt.value.recv_buffer_size = aSize;
   2787  if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS) rv = NS_ERROR_FAILURE;
   2788 
   2789  return rv;
   2790 }
   2791 
   2792 NS_IMETHODIMP
   2793 nsSocketTransport::SetSendBufferSize(uint32_t aSize) {
   2794  PRFileDescAutoLock fd(this);
   2795  if (!fd.IsInitialized()) return NS_ERROR_NOT_CONNECTED;
   2796 
   2797  nsresult rv = NS_OK;
   2798  PRSocketOptionData opt;
   2799  opt.option = PR_SockOpt_SendBufferSize;
   2800  opt.value.send_buffer_size = aSize;
   2801  if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS) rv = NS_ERROR_FAILURE;
   2802 
   2803  return rv;
   2804 }
   2805 
   2806 NS_IMETHODIMP
   2807 nsSocketTransport::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec,
   2808                                    nsresult status) {
   2809  SOCKET_LOG(("nsSocketTransport::OnLookupComplete: this=%p status %" PRIx32
   2810              ".",
   2811              this, static_cast<uint32_t>(status)));
   2812 
   2813  if (NS_SUCCEEDED(status)) {
   2814    mDNSRecord = do_QueryInterface(rec);
   2815    MOZ_ASSERT(mDNSRecord);
   2816  }
   2817 
   2818  if (nsCOMPtr<nsIDNSAddrRecord> addrRecord = do_QueryInterface(rec)) {
   2819    addrRecord->IsTRR(&mResolvedByTRR);
   2820    addrRecord->GetEffectiveTRRMode(&mEffectiveTRRMode);
   2821    addrRecord->GetTrrSkipReason(&mTRRSkipReason);
   2822  }
   2823 
   2824  // flag host lookup complete for the benefit of the ResolveHost method.
   2825  mResolving = false;
   2826  nsresult rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, status, nullptr);
   2827 
   2828  // if posting a message fails, then we should assume that the socket
   2829  // transport has been shutdown.  this should never happen!  if it does
   2830  // it means that the socket transport service was shutdown before the
   2831  // DNS service.
   2832  if (NS_FAILED(rv)) {
   2833    NS_WARNING("unable to post DNS lookup complete message");
   2834  }
   2835 
   2836  return NS_OK;
   2837 }
   2838 
   2839 // nsIInterfaceRequestor
   2840 NS_IMETHODIMP
   2841 nsSocketTransport::GetInterface(const nsIID& iid, void** result) {
   2842  if (iid.Equals(NS_GET_IID(nsIDNSRecord)) ||
   2843      iid.Equals(NS_GET_IID(nsIDNSAddrRecord))) {
   2844    return mDNSRecord ? mDNSRecord->QueryInterface(iid, result)
   2845                      : NS_ERROR_NO_INTERFACE;
   2846  }
   2847  return this->QueryInterface(iid, result);
   2848 }
   2849 
   2850 NS_IMETHODIMP
   2851 nsSocketTransport::GetInterfaces(nsTArray<nsIID>& array) {
   2852  return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(array);
   2853 }
   2854 
   2855 NS_IMETHODIMP
   2856 nsSocketTransport::GetScriptableHelper(nsIXPCScriptable** _retval) {
   2857  *_retval = nullptr;
   2858  return NS_OK;
   2859 }
   2860 
   2861 NS_IMETHODIMP
   2862 nsSocketTransport::GetContractID(nsACString& aContractID) {
   2863  aContractID.SetIsVoid(true);
   2864  return NS_OK;
   2865 }
   2866 
   2867 NS_IMETHODIMP
   2868 nsSocketTransport::GetClassDescription(nsACString& aClassDescription) {
   2869  aClassDescription.SetIsVoid(true);
   2870  return NS_OK;
   2871 }
   2872 
   2873 NS_IMETHODIMP
   2874 nsSocketTransport::GetClassID(nsCID** aClassID) {
   2875  *aClassID = nullptr;
   2876  return NS_OK;
   2877 }
   2878 
   2879 NS_IMETHODIMP
   2880 nsSocketTransport::GetFlags(uint32_t* aFlags) {
   2881  *aFlags = nsIClassInfo::THREADSAFE;
   2882  return NS_OK;
   2883 }
   2884 
   2885 NS_IMETHODIMP
   2886 nsSocketTransport::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) {
   2887  return NS_ERROR_NOT_AVAILABLE;
   2888 }
   2889 
   2890 NS_IMETHODIMP
   2891 nsSocketTransport::GetConnectionFlags(uint32_t* value) {
   2892  *value = mConnectionFlags;
   2893  return NS_OK;
   2894 }
   2895 
   2896 NS_IMETHODIMP
   2897 nsSocketTransport::SetConnectionFlags(uint32_t value) {
   2898  SOCKET_LOG(
   2899      ("nsSocketTransport::SetConnectionFlags %p flags=%u", this, value));
   2900 
   2901  mConnectionFlags = value;
   2902  return NS_OK;
   2903 }
   2904 
   2905 NS_IMETHODIMP
   2906 nsSocketTransport::SetIsPrivate(bool aIsPrivate) {
   2907  mIsPrivate = aIsPrivate;
   2908  return NS_OK;
   2909 }
   2910 
   2911 NS_IMETHODIMP
   2912 nsSocketTransport::GetTlsFlags(uint32_t* value) {
   2913  *value = mTlsFlags;
   2914  return NS_OK;
   2915 }
   2916 
   2917 NS_IMETHODIMP
   2918 nsSocketTransport::SetTlsFlags(uint32_t value) {
   2919  mTlsFlags = value;
   2920  return NS_OK;
   2921 }
   2922 
   2923 void nsSocketTransport::OnKeepaliveEnabledPrefChange(bool aEnabled) {
   2924  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2925 
   2926  // The global pref toggles keepalive as a system feature; it only affects
   2927  // an individual socket if keepalive has been specifically enabled for it.
   2928  // So, ensure keepalive is configured correctly if previously enabled.
   2929  if (mKeepaliveEnabled) {
   2930    nsresult rv = SetKeepaliveEnabledInternal(aEnabled);
   2931    if (NS_WARN_IF(NS_FAILED(rv))) {
   2932      SOCKET_LOG(("  SetKeepaliveEnabledInternal [%s] failed rv[0x%" PRIx32 "]",
   2933                  aEnabled ? "enable" : "disable", static_cast<uint32_t>(rv)));
   2934    }
   2935  }
   2936 }
   2937 
   2938 nsresult nsSocketTransport::SetKeepaliveEnabledInternal(bool aEnable) {
   2939  MOZ_ASSERT(mKeepaliveIdleTimeS > 0 && mKeepaliveIdleTimeS <= kMaxTCPKeepIdle);
   2940  MOZ_ASSERT(mKeepaliveRetryIntervalS > 0 &&
   2941             mKeepaliveRetryIntervalS <= kMaxTCPKeepIntvl);
   2942  MOZ_ASSERT(mKeepaliveProbeCount > 0 &&
   2943             mKeepaliveProbeCount <= kMaxTCPKeepCount);
   2944 
   2945  PRFileDescAutoLock fd(this);
   2946  if (NS_WARN_IF(!fd.IsInitialized())) {
   2947    return NS_ERROR_NOT_INITIALIZED;
   2948  }
   2949 
   2950  // Only enable if keepalives are globally enabled, but ensure other
   2951  // options are set correctly on the fd.
   2952  bool enable = aEnable && mSocketTransportService->IsKeepaliveEnabled();
   2953  nsresult rv =
   2954      fd.SetKeepaliveVals(enable, mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS,
   2955                          mKeepaliveProbeCount);
   2956  if (NS_WARN_IF(NS_FAILED(rv))) {
   2957    SOCKET_LOG(("  SetKeepaliveVals failed rv[0x%" PRIx32 "]",
   2958                static_cast<uint32_t>(rv)));
   2959    return rv;
   2960  }
   2961  rv = fd.SetKeepaliveEnabled(enable);
   2962  if (NS_WARN_IF(NS_FAILED(rv))) {
   2963    SOCKET_LOG(("  SetKeepaliveEnabled failed rv[0x%" PRIx32 "]",
   2964                static_cast<uint32_t>(rv)));
   2965    return rv;
   2966  }
   2967  return NS_OK;
   2968 }
   2969 
   2970 NS_IMETHODIMP
   2971 nsSocketTransport::GetKeepaliveEnabled(bool* aResult) {
   2972  MOZ_ASSERT(aResult);
   2973 
   2974  *aResult = mKeepaliveEnabled;
   2975  return NS_OK;
   2976 }
   2977 
   2978 nsresult nsSocketTransport::EnsureKeepaliveValsAreInitialized() {
   2979  nsresult rv = NS_OK;
   2980  int32_t val = -1;
   2981  if (mKeepaliveIdleTimeS == -1) {
   2982    rv = mSocketTransportService->GetKeepaliveIdleTime(&val);
   2983    if (NS_WARN_IF(NS_FAILED(rv))) {
   2984      return rv;
   2985    }
   2986    mKeepaliveIdleTimeS = val;
   2987  }
   2988  if (mKeepaliveRetryIntervalS == -1) {
   2989    rv = mSocketTransportService->GetKeepaliveRetryInterval(&val);
   2990    if (NS_WARN_IF(NS_FAILED(rv))) {
   2991      return rv;
   2992    }
   2993    mKeepaliveRetryIntervalS = val;
   2994  }
   2995  if (mKeepaliveProbeCount == -1) {
   2996    rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
   2997    if (NS_WARN_IF(NS_FAILED(rv))) {
   2998      return rv;
   2999    }
   3000    mKeepaliveProbeCount = val;
   3001  }
   3002  return NS_OK;
   3003 }
   3004 
   3005 NS_IMETHODIMP
   3006 nsSocketTransport::SetKeepaliveEnabled(bool aEnable) {
   3007 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
   3008  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3009 
   3010  if (aEnable == mKeepaliveEnabled) {
   3011    SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] already %s.", this,
   3012                aEnable ? "enabled" : "disabled"));
   3013    return NS_OK;
   3014  }
   3015 
   3016  nsresult rv = NS_OK;
   3017  if (aEnable) {
   3018    rv = EnsureKeepaliveValsAreInitialized();
   3019    if (NS_WARN_IF(NS_FAILED(rv))) {
   3020      SOCKET_LOG(
   3021          ("  SetKeepaliveEnabled [%p] "
   3022           "error [0x%" PRIx32 "] initializing keepalive vals",
   3023           this, static_cast<uint32_t>(rv)));
   3024      return rv;
   3025    }
   3026  }
   3027  SOCKET_LOG(
   3028      ("nsSocketTransport::SetKeepaliveEnabled [%p] "
   3029       "%s, idle time[%ds] retry interval[%ds] packet count[%d]: "
   3030       "globally %s.",
   3031       this, aEnable ? "enabled" : "disabled", mKeepaliveIdleTimeS,
   3032       mKeepaliveRetryIntervalS, mKeepaliveProbeCount,
   3033       mSocketTransportService->IsKeepaliveEnabled() ? "enabled" : "disabled"));
   3034 
   3035  // Set mKeepaliveEnabled here so that state is maintained; it is possible
   3036  // that we're in between fds, e.g. the 1st IP address failed, so we're about
   3037  // to retry on a 2nd from the DNS record.
   3038  mKeepaliveEnabled = aEnable;
   3039 
   3040  rv = SetKeepaliveEnabledInternal(aEnable);
   3041  if (NS_WARN_IF(NS_FAILED(rv))) {
   3042    SOCKET_LOG(("  SetKeepaliveEnabledInternal failed rv[0x%" PRIx32 "]",
   3043                static_cast<uint32_t>(rv)));
   3044    return rv;
   3045  }
   3046 
   3047  return NS_OK;
   3048 #else /* !(defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)) */
   3049  SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled unsupported platform"));
   3050  return NS_ERROR_NOT_IMPLEMENTED;
   3051 #endif
   3052 }
   3053 
   3054 NS_IMETHODIMP
   3055 nsSocketTransport::SetKeepaliveVals(int32_t aIdleTime, int32_t aRetryInterval) {
   3056 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
   3057  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3058  if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
   3059    return NS_ERROR_INVALID_ARG;
   3060  }
   3061  if (NS_WARN_IF(aRetryInterval <= 0 || kMaxTCPKeepIntvl < aRetryInterval)) {
   3062    return NS_ERROR_INVALID_ARG;
   3063  }
   3064 
   3065  if (aIdleTime == mKeepaliveIdleTimeS &&
   3066      aRetryInterval == mKeepaliveRetryIntervalS) {
   3067    SOCKET_LOG(
   3068        ("nsSocketTransport::SetKeepaliveVals [%p] idle time "
   3069         "already %ds and retry interval already %ds.",
   3070         this, mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS));
   3071    return NS_OK;
   3072  }
   3073  mKeepaliveIdleTimeS = aIdleTime;
   3074  mKeepaliveRetryIntervalS = aRetryInterval;
   3075 
   3076  nsresult rv = NS_OK;
   3077  if (mKeepaliveProbeCount == -1) {
   3078    int32_t val = -1;
   3079    nsresult rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
   3080    if (NS_WARN_IF(NS_FAILED(rv))) {
   3081      return rv;
   3082    }
   3083    mKeepaliveProbeCount = val;
   3084  }
   3085 
   3086  SOCKET_LOG(
   3087      ("nsSocketTransport::SetKeepaliveVals [%p] "
   3088       "keepalive %s, idle time[%ds] retry interval[%ds] "
   3089       "packet count[%d]",
   3090       this, mKeepaliveEnabled ? "enabled" : "disabled", mKeepaliveIdleTimeS,
   3091       mKeepaliveRetryIntervalS, mKeepaliveProbeCount));
   3092 
   3093  PRFileDescAutoLock fd(this);
   3094  if (NS_WARN_IF(!fd.IsInitialized())) {
   3095    return NS_ERROR_NULL_POINTER;
   3096  }
   3097 
   3098  rv = fd.SetKeepaliveVals(mKeepaliveEnabled, mKeepaliveIdleTimeS,
   3099                           mKeepaliveRetryIntervalS, mKeepaliveProbeCount);
   3100  if (NS_WARN_IF(NS_FAILED(rv))) {
   3101    return rv;
   3102  }
   3103  return NS_OK;
   3104 #else
   3105  SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals unsupported platform"));
   3106  return NS_ERROR_NOT_IMPLEMENTED;
   3107 #endif
   3108 }
   3109 
   3110 #ifdef ENABLE_SOCKET_TRACING
   3111 
   3112 #  include <stdio.h>
   3113 #  include <ctype.h>
   3114 #  include "prenv.h"
   3115 
   3116 static void DumpBytesToFile(const char* path, const char* header,
   3117                            const char* buf, int32_t n) {
   3118  FILE* fp = fopen(path, "a");
   3119 
   3120  fprintf(fp, "\n%s [%d bytes]\n", header, n);
   3121 
   3122  const unsigned char* p;
   3123  while (n) {
   3124    p = (const unsigned char*)buf;
   3125 
   3126    int32_t i, row_max = std::min(16, n);
   3127 
   3128    for (i = 0; i < row_max; ++i) fprintf(fp, "%02x  ", *p++);
   3129    for (i = row_max; i < 16; ++i) fprintf(fp, "    ");
   3130 
   3131    p = (const unsigned char*)buf;
   3132    for (i = 0; i < row_max; ++i, ++p) {
   3133      if (isprint(*p))
   3134        fprintf(fp, "%c", *p);
   3135      else
   3136        fprintf(fp, ".");
   3137    }
   3138 
   3139    fprintf(fp, "\n");
   3140    buf += row_max;
   3141    n -= row_max;
   3142  }
   3143 
   3144  fprintf(fp, "\n");
   3145  fclose(fp);
   3146 }
   3147 
   3148 void nsSocketTransport::TraceInBuf(const char* buf, int32_t n) {
   3149  char* val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
   3150  if (!val || !*val) return;
   3151 
   3152  nsAutoCString header;
   3153  header.AssignLiteral("Reading from: ");
   3154  header.Append(mHost);
   3155  header.Append(':');
   3156  header.AppendInt(mPort);
   3157 
   3158  DumpBytesToFile(val, header.get(), buf, n);
   3159 }
   3160 
   3161 void nsSocketTransport::TraceOutBuf(const char* buf, int32_t n) {
   3162  char* val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
   3163  if (!val || !*val) return;
   3164 
   3165  nsAutoCString header;
   3166  header.AssignLiteral("Writing to: ");
   3167  header.Append(mHost);
   3168  header.Append(':');
   3169  header.AppendInt(mPort);
   3170 
   3171  DumpBytesToFile(val, header.get(), buf, n);
   3172 }
   3173 
   3174 #endif
   3175 
   3176 static void LogNSPRError(const char* aPrefix, const void* aObjPtr) {
   3177 #if defined(DEBUG)
   3178  PRErrorCode errCode = PR_GetError();
   3179  int errLen = PR_GetErrorTextLength();
   3180  nsAutoCString errStr;
   3181  if (errLen > 0) {
   3182    errStr.SetLength(errLen);
   3183    PR_GetErrorText(errStr.BeginWriting());
   3184  }
   3185  NS_WARNING(
   3186      nsPrintfCString("%s [%p] NSPR error[0x%x] %s.",
   3187                      aPrefix ? aPrefix : "nsSocketTransport", aObjPtr, errCode,
   3188                      errLen > 0 ? errStr.BeginReading() : "<no error text>")
   3189          .get());
   3190 #endif
   3191 }
   3192 
   3193 nsresult nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled(
   3194    bool aEnable) {
   3195  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3196  MOZ_ASSERT(!(aEnable && !gSocketTransportService->IsKeepaliveEnabled()),
   3197             "Cannot enable keepalive if global pref is disabled!");
   3198  if (aEnable && !gSocketTransportService->IsKeepaliveEnabled()) {
   3199    return NS_ERROR_ILLEGAL_VALUE;
   3200  }
   3201 
   3202  PRSocketOptionData opt;
   3203 
   3204  opt.option = PR_SockOpt_Keepalive;
   3205  opt.value.keep_alive = aEnable;
   3206  PRStatus status = PR_SetSocketOption(mFd, &opt);
   3207  if (NS_WARN_IF(status != PR_SUCCESS)) {
   3208    LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled",
   3209                 mSocketTransport);
   3210    return ErrorAccordingToNSPR(PR_GetError());
   3211  }
   3212  return NS_OK;
   3213 }
   3214 
   3215 static void LogOSError(const char* aPrefix, const void* aObjPtr) {
   3216 #if defined(DEBUG)
   3217  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3218 
   3219 #  ifdef XP_WIN
   3220  DWORD errCode = WSAGetLastError();
   3221  char* errMessage;
   3222  FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
   3223                     FORMAT_MESSAGE_IGNORE_INSERTS,
   3224                 NULL, errCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
   3225                 (LPSTR)&errMessage, 0, NULL);
   3226  NS_WARNING(nsPrintfCString("%s [%p] OS error[0x%lx] %s",
   3227                             aPrefix ? aPrefix : "nsSocketTransport", aObjPtr,
   3228                             errCode,
   3229                             errMessage ? errMessage : "<no error text>")
   3230                 .get());
   3231  LocalFree(errMessage);
   3232 #  else
   3233  int errCode = errno;
   3234  char* errMessage = strerror(errno);
   3235  NS_WARNING(nsPrintfCString("%s [%p] OS error[0x%x] %s",
   3236                             aPrefix ? aPrefix : "nsSocketTransport", aObjPtr,
   3237                             errCode,
   3238                             errMessage ? errMessage : "<no error text>")
   3239                 .get());
   3240 #  endif
   3241 #endif
   3242 }
   3243 
   3244 /* XXX PR_SetSockOpt does not support setting keepalive values, so native
   3245 * handles and platform specific apis (setsockopt, WSAIOCtl) are used in this
   3246 * file. Requires inclusion of NSPR private/pprio.h, and platform headers.
   3247 */
   3248 
   3249 nsresult nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(
   3250    bool aEnabled, int aIdleTime, int aRetryInterval, int aProbeCount) {
   3251 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
   3252  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3253  if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
   3254    return NS_ERROR_INVALID_ARG;
   3255  }
   3256  if (NS_WARN_IF(aRetryInterval <= 0 || kMaxTCPKeepIntvl < aRetryInterval)) {
   3257    return NS_ERROR_INVALID_ARG;
   3258  }
   3259  if (NS_WARN_IF(aProbeCount <= 0 || kMaxTCPKeepCount < aProbeCount)) {
   3260    return NS_ERROR_INVALID_ARG;
   3261  }
   3262 
   3263  PROsfd sock = PR_FileDesc2NativeHandle(mFd);
   3264  if (NS_WARN_IF(sock == -1)) {
   3265    LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals",
   3266                 mSocketTransport);
   3267    return ErrorAccordingToNSPR(PR_GetError());
   3268  }
   3269 #endif
   3270 
   3271 #if defined(XP_WIN)
   3272  // Windows allows idle time and retry interval to be set; NOT ping count.
   3273  struct tcp_keepalive keepalive_vals = {(u_long)aEnabled,
   3274                                         // Windows uses msec.
   3275                                         (u_long)(aIdleTime * 1000UL),
   3276                                         (u_long)(aRetryInterval * 1000UL)};
   3277  DWORD bytes_returned;
   3278  int err =
   3279      WSAIoctl(sock, SIO_KEEPALIVE_VALS, &keepalive_vals,
   3280               sizeof(keepalive_vals), NULL, 0, &bytes_returned, NULL, NULL);
   3281  if (NS_WARN_IF(err)) {
   3282    LogOSError("nsSocketTransport WSAIoctl failed", mSocketTransport);
   3283    return NS_ERROR_UNEXPECTED;
   3284  }
   3285  return NS_OK;
   3286 
   3287 #elif defined(XP_DARWIN)
   3288  // Darwin uses sec; only supports idle time being set.
   3289  int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &aIdleTime,
   3290                       sizeof(aIdleTime));
   3291  if (NS_WARN_IF(err)) {
   3292    LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
   3293               mSocketTransport);
   3294    return NS_ERROR_UNEXPECTED;
   3295  }
   3296  return NS_OK;
   3297 
   3298 #elif defined(XP_UNIX)
   3299  // Not all *nix OSes support the following setsockopt() options
   3300  // ... but we assume they are supported in the Android kernel;
   3301  // build errors will tell us if they are not.
   3302 #  if defined(ANDROID) || defined(TCP_KEEPIDLE)
   3303  // Idle time until first keepalive probe; interval between ack'd probes;
   3304  // seconds.
   3305  int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &aIdleTime,
   3306                       sizeof(aIdleTime));
   3307  if (NS_WARN_IF(err)) {
   3308    LogOSError("nsSocketTransport Failed setting TCP_KEEPIDLE",
   3309               mSocketTransport);
   3310    return NS_ERROR_UNEXPECTED;
   3311  }
   3312 
   3313 #  endif
   3314 #  if defined(ANDROID) || defined(TCP_KEEPINTVL)
   3315  // Interval between unack'd keepalive probes; seconds.
   3316  err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &aRetryInterval,
   3317                   sizeof(aRetryInterval));
   3318  if (NS_WARN_IF(err)) {
   3319    LogOSError("nsSocketTransport Failed setting TCP_KEEPINTVL",
   3320               mSocketTransport);
   3321    return NS_ERROR_UNEXPECTED;
   3322  }
   3323 
   3324 #  endif
   3325 #  if defined(ANDROID) || defined(TCP_KEEPCNT)
   3326  // Number of unack'd keepalive probes before connection times out.
   3327  err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &aProbeCount,
   3328                   sizeof(aProbeCount));
   3329  if (NS_WARN_IF(err)) {
   3330    LogOSError("nsSocketTransport Failed setting TCP_KEEPCNT",
   3331               mSocketTransport);
   3332    return NS_ERROR_UNEXPECTED;
   3333  }
   3334 
   3335 #  endif
   3336  return NS_OK;
   3337 #else
   3338  MOZ_ASSERT(false,
   3339             "nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals "
   3340             "called on unsupported platform!");
   3341  return NS_ERROR_UNEXPECTED;
   3342 #endif
   3343 }
   3344 
   3345 void nsSocketTransport::CloseSocket(PRFileDesc* aFd, bool aTelemetryEnabled) {
   3346 #if defined(XP_WIN)
   3347  AttachShutdownLayer(aFd);
   3348 #endif
   3349 
   3350  // We use PRIntervalTime here because we need
   3351  // nsIOService::LastOfflineStateChange time and
   3352  // nsIOService::LastConectivityChange time to be atomic.
   3353  PRIntervalTime closeStarted;
   3354  if (aTelemetryEnabled) {
   3355    closeStarted = PR_IntervalNow();
   3356  }
   3357 
   3358  PR_Close(aFd);
   3359 
   3360  if (aTelemetryEnabled) {
   3361    SendPRBlockingTelemetry(
   3362        closeStarted, glean::networking::prclose_tcp_blocking_time_normal,
   3363        glean::networking::prclose_tcp_blocking_time_shutdown,
   3364        glean::networking::prclose_tcp_blocking_time_connectivity_change,
   3365        glean::networking::prclose_tcp_blocking_time_link_change,
   3366        glean::networking::prclose_tcp_blocking_time_offline);
   3367  }
   3368 }
   3369 
   3370 void nsSocketTransport::SendPRBlockingTelemetry(
   3371    PRIntervalTime aStart,
   3372    const glean::impl::TimingDistributionMetric& aMetricNormal,
   3373    const glean::impl::TimingDistributionMetric& aMetricShutdown,
   3374    const glean::impl::TimingDistributionMetric& aMetricConnectivityChange,
   3375    const glean::impl::TimingDistributionMetric& aMetricLinkChange,
   3376    const glean::impl::TimingDistributionMetric& aMetricOffline) {
   3377  PRIntervalTime now = PR_IntervalNow();
   3378  TimeDuration delta =
   3379      TimeDuration::FromMilliseconds(PR_IntervalToMilliseconds(now - aStart));
   3380  if (gIOService->IsNetTearingDown()) {
   3381    aMetricShutdown.AccumulateRawDuration(delta);
   3382  } else if (PR_IntervalToSeconds(now - gIOService->LastConnectivityChange()) <
   3383             60) {
   3384    aMetricConnectivityChange.AccumulateRawDuration(delta);
   3385  } else if (PR_IntervalToSeconds(now - gIOService->LastNetworkLinkChange()) <
   3386             60) {
   3387    aMetricLinkChange.AccumulateRawDuration(delta);
   3388  } else if (PR_IntervalToSeconds(now - gIOService->LastOfflineStateChange()) <
   3389             60) {
   3390    aMetricOffline.AccumulateRawDuration(delta);
   3391  } else {
   3392    aMetricNormal.AccumulateRawDuration(delta);
   3393  }
   3394 }
   3395 
   3396 NS_IMETHODIMP
   3397 nsSocketTransport::GetResetIPFamilyPreference(bool* aReset) {
   3398  *aReset = mResetFamilyPreference;
   3399  return NS_OK;
   3400 }
   3401 
   3402 NS_IMETHODIMP
   3403 nsSocketTransport::GetEchConfigUsed(bool* aEchConfigUsed) {
   3404  *aEchConfigUsed = mEchConfigUsed;
   3405  return NS_OK;
   3406 }
   3407 
   3408 NS_IMETHODIMP
   3409 nsSocketTransport::SetEchConfig(const nsACString& aEchConfig) {
   3410  mEchConfig = aEchConfig;
   3411  return NS_OK;
   3412 }
   3413 
   3414 NS_IMETHODIMP
   3415 nsSocketTransport::ResolvedByTRR(bool* aResolvedByTRR) {
   3416  *aResolvedByTRR = mResolvedByTRR;
   3417  return NS_OK;
   3418 }
   3419 
   3420 NS_IMETHODIMP nsSocketTransport::GetEffectiveTRRMode(
   3421    nsIRequest::TRRMode* aEffectiveTRRMode) {
   3422  *aEffectiveTRRMode = mEffectiveTRRMode;
   3423  return NS_OK;
   3424 }
   3425 
   3426 NS_IMETHODIMP nsSocketTransport::GetTrrSkipReason(
   3427    nsITRRSkipReason::value* aSkipReason) {
   3428  *aSkipReason = mTRRSkipReason;
   3429  return NS_OK;
   3430 }
   3431 
   3432 NS_IMETHODIMP
   3433 nsSocketTransport::GetRetryDnsIfPossible(bool* aRetryDns) {
   3434  *aRetryDns = mRetryDnsIfPossible;
   3435  return NS_OK;
   3436 }
   3437 
   3438 NS_IMETHODIMP
   3439 nsSocketTransport::GetStatus(nsresult* aStatus) {
   3440  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3441 
   3442  *aStatus = mCondition;
   3443  return NS_OK;
   3444 }
   3445 
   3446 }  // namespace net
   3447 }  // namespace mozilla