LCOV - code coverage report
Current view: top level - netwerk/base - nsSocketTransport2.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 648 1416 45.8 %
Date: 2017-07-14 16:53:18 Functions: 72 125 57.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13