netwerk/base/src/nsSocketTransport2.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     7 #ifdef MOZ_LOGGING
     8 #define FORCE_PR_LOG
     9 #endif
    11 #include "nsSocketTransport2.h"
    13 #include "mozilla/Attributes.h"
    14 #include "nsIOService.h"
    15 #include "nsStreamUtils.h"
    16 #include "nsNetSegmentUtils.h"
    17 #include "nsNetAddr.h"
    18 #include "nsTransportUtils.h"
    19 #include "nsProxyInfo.h"
    20 #include "nsNetCID.h"
    21 #include "nsNetUtil.h"
    22 #include "nsAutoPtr.h"
    23 #include "nsCOMPtr.h"
    24 #include "plstr.h"
    25 #include "prerr.h"
    26 #include "NetworkActivityMonitor.h"
    27 #include "mozilla/VisualEventTracer.h"
    28 #include "nsThreadUtils.h"
    29 #include "nsISocketProviderService.h"
    30 #include "nsISocketProvider.h"
    31 #include "nsISSLSocketControl.h"
    32 #include "nsINSSErrorsService.h"
    33 #include "nsIPipe.h"
    34 #include "nsIProgrammingLanguage.h"
    35 #include "nsIClassInfoImpl.h"
    36 #include "nsURLHelper.h"
    37 #include "nsIDNSService.h"
    38 #include "nsIDNSRecord.h"
    39 #include "nsICancelable.h"
    40 #include <algorithm>
    42 #include "nsPrintfCString.h"
    44 #if defined(XP_WIN)
    45 #include "nsNativeConnectionHelper.h"
    46 #endif
    48 /* Following inclusions required for keepalive config not supported by NSPR. */
    49 #include "private/pprio.h"
    50 #if defined(XP_WIN)
    51 #include <winsock2.h>
    52 #include <mstcpip.h>
    53 #elif defined(XP_UNIX)
    54 #include <errno.h>
    55 #include <netinet/tcp.h>
    56 #endif
    57 /* End keepalive config inclusions. */
    59 using namespace mozilla;
    60 using namespace mozilla::net;
    62 //-----------------------------------------------------------------------------
    64 static NS_DEFINE_CID(kSocketProviderServiceCID, NS_SOCKETPROVIDERSERVICE_CID);
    65 static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
    67 //-----------------------------------------------------------------------------
    69 class nsSocketEvent : public nsRunnable
    70 {
    71 public:
    72     nsSocketEvent(nsSocketTransport *transport, uint32_t type,
    73                   nsresult status = NS_OK, nsISupports *param = nullptr)
    74         : mTransport(transport)
    75         , mType(type)
    76         , mStatus(status)
    77         , mParam(param)
    78     {}
    80     NS_IMETHOD Run()
    81     {
    82         mTransport->OnSocketEvent(mType, mStatus, mParam);
    83         return NS_OK;
    84     }
    86 private:
    87     nsRefPtr<nsSocketTransport> mTransport;
    89     uint32_t              mType;
    90     nsresult              mStatus;
    91     nsCOMPtr<nsISupports> mParam;
    92 };
    94 //-----------------------------------------------------------------------------
    96 //#define TEST_CONNECT_ERRORS
    97 #ifdef TEST_CONNECT_ERRORS
    98 #include <stdlib.h>
    99 static PRErrorCode RandomizeConnectError(PRErrorCode code)
   100 {
   101     //
   102     // To test out these errors, load http://www.yahoo.com/.  It should load
   103     // correctly despite the random occurrence of these errors.
   104     //
   105     int n = rand();
   106     if (n > RAND_MAX/2) {
   107         struct {
   108             PRErrorCode err_code;
   109             const char *err_name;
   110         } 
   111         errors[] = {
   112             //
   113             // These errors should be recoverable provided there is another
   114             // IP address in mDNSRecord.
   115             //
   116             { PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR" },
   117             { PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR" },
   118             //
   119             // This error will cause this socket transport to error out;
   120             // however, if the consumer is HTTP, then the HTTP transaction
   121             // should be restarted when this error occurs.
   122             //
   123             { PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR" },
   124         };
   125         n = n % (sizeof(errors)/sizeof(errors[0]));
   126         code = errors[n].err_code;
   127         SOCKET_LOG(("simulating NSPR error %d [%s]\n", code, errors[n].err_name));
   128     }
   129     return code;
   130 }
   131 #endif
   133 //-----------------------------------------------------------------------------
   135 static bool
   136 IsNSSErrorCode(PRErrorCode code)
   137 {
   138   return 
   139     ((code >= nsINSSErrorsService::NSS_SEC_ERROR_BASE) && 
   140       (code < nsINSSErrorsService::NSS_SEC_ERROR_LIMIT))
   141     ||
   142     ((code >= nsINSSErrorsService::NSS_SSL_ERROR_BASE) && 
   143       (code < nsINSSErrorsService::NSS_SSL_ERROR_LIMIT));
   144 }
   146 // this logic is duplicated from the implementation of
   147 // nsINSSErrorsService::getXPCOMFromNSSError
   148 // It might have been better to implement that interface here...
   149 static nsresult
   150 GetXPCOMFromNSSError(PRErrorCode code)
   151 {
   152     // XXX Don't make up nsresults, it's supposed to be an enum (bug 778113)
   153     return (nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
   154                                                -1 * code);
   155 }
   157 nsresult
   158 ErrorAccordingToNSPR(PRErrorCode errorCode)
   159 {
   160     nsresult rv = NS_ERROR_FAILURE;
   161     switch (errorCode) {
   162     case PR_WOULD_BLOCK_ERROR:
   163         rv = NS_BASE_STREAM_WOULD_BLOCK;
   164         break;
   165     case PR_CONNECT_ABORTED_ERROR:
   166     case PR_CONNECT_RESET_ERROR:
   167         rv = NS_ERROR_NET_RESET;
   168         break;
   169     case PR_END_OF_FILE_ERROR: // XXX document this correlation
   170         rv = NS_ERROR_NET_INTERRUPT;
   171         break;
   172     case PR_CONNECT_REFUSED_ERROR:
   173     // We lump the following NSPR codes in with PR_CONNECT_REFUSED_ERROR. We
   174     // could get better diagnostics by adding distinct XPCOM error codes for
   175     // each of these, but there are a lot of places in Gecko that check
   176     // specifically for NS_ERROR_CONNECTION_REFUSED, all of which would need to
   177     // be checked.
   178     case PR_NETWORK_UNREACHABLE_ERROR:
   179     case PR_HOST_UNREACHABLE_ERROR:
   180     case PR_ADDRESS_NOT_AVAILABLE_ERROR:
   181     // Treat EACCES as a soft error since (at least on Linux) connect() returns
   182     // EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
   183     case PR_NO_ACCESS_RIGHTS_ERROR:
   184         rv = NS_ERROR_CONNECTION_REFUSED;
   185         break;
   186     case PR_ADDRESS_NOT_SUPPORTED_ERROR:
   187         rv = NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
   188         break;
   189     case PR_IO_TIMEOUT_ERROR:
   190     case PR_CONNECT_TIMEOUT_ERROR:
   191         rv = NS_ERROR_NET_TIMEOUT;
   192         break;
   193     case PR_OUT_OF_MEMORY_ERROR:
   194     // These really indicate that the descriptor table filled up, or that the
   195     // kernel ran out of network buffers - but nobody really cares which part of
   196     // the system ran out of memory.
   197     case PR_PROC_DESC_TABLE_FULL_ERROR:
   198     case PR_SYS_DESC_TABLE_FULL_ERROR:
   199     case PR_INSUFFICIENT_RESOURCES_ERROR:
   200         rv = NS_ERROR_OUT_OF_MEMORY;
   201         break;
   202     case PR_ADDRESS_IN_USE_ERROR:
   203         rv = NS_ERROR_SOCKET_ADDRESS_IN_USE;
   204         break;
   205     // These filename-related errors can arise when using Unix-domain sockets.
   206     case PR_FILE_NOT_FOUND_ERROR:
   207         rv = NS_ERROR_FILE_NOT_FOUND;
   208         break;
   209     case PR_IS_DIRECTORY_ERROR:
   210         rv = NS_ERROR_FILE_IS_DIRECTORY;
   211         break;
   212     case PR_LOOP_ERROR:
   213         rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
   214         break;
   215     case PR_NAME_TOO_LONG_ERROR:
   216         rv = NS_ERROR_FILE_NAME_TOO_LONG;
   217         break;
   218     case PR_NO_DEVICE_SPACE_ERROR:
   219         rv = NS_ERROR_FILE_NO_DEVICE_SPACE;
   220         break;
   221     case PR_NOT_DIRECTORY_ERROR:
   222         rv = NS_ERROR_FILE_NOT_DIRECTORY;
   223         break;
   224     case PR_READ_ONLY_FILESYSTEM_ERROR:
   225         rv = NS_ERROR_FILE_READ_ONLY;
   226         break;
   227     default:
   228         if (IsNSSErrorCode(errorCode))
   229             rv = GetXPCOMFromNSSError(errorCode);
   230         break;
   232     // NSPR's socket code can return these, but they're not worth breaking out
   233     // into their own error codes, distinct from NS_ERROR_FAILURE:
   234     //
   235     // PR_BAD_DESCRIPTOR_ERROR
   236     // PR_INVALID_ARGUMENT_ERROR
   237     // PR_NOT_SOCKET_ERROR
   238     // PR_NOT_TCP_SOCKET_ERROR
   239     //   These would indicate a bug internal to the component.
   240     //
   241     // PR_PROTOCOL_NOT_SUPPORTED_ERROR
   242     //   This means that we can't use the given "protocol" (like
   243     //   IPPROTO_TCP or IPPROTO_UDP) with a socket of the given type. As
   244     //   above, this indicates an internal bug.
   245     //
   246     // PR_IS_CONNECTED_ERROR
   247     //   This indicates that we've applied a system call like 'bind' or
   248     //   'connect' to a socket that is already connected. The socket
   249     //   components manage each file descriptor's state, and in some cases
   250     //   handle this error result internally. We shouldn't be returning
   251     //   this to our callers.
   252     //
   253     // PR_IO_ERROR
   254     //   This is so vague that NS_ERROR_FAILURE is just as good.
   255     }
   256     SOCKET_LOG(("ErrorAccordingToNSPR [in=%d out=%x]\n", errorCode, rv));
   257     return rv;
   258 }
   260 //-----------------------------------------------------------------------------
   261 // socket input stream impl 
   262 //-----------------------------------------------------------------------------
   264 nsSocketInputStream::nsSocketInputStream(nsSocketTransport *trans)
   265     : mTransport(trans)
   266     , mReaderRefCnt(0)
   267     , mCondition(NS_OK)
   268     , mCallbackFlags(0)
   269     , mByteCount(0)
   270 {
   271 }
   273 nsSocketInputStream::~nsSocketInputStream()
   274 {
   275 }
   277 // called on the socket transport thread...
   278 //
   279 //   condition : failure code if socket has been closed
   280 //
   281 void
   282 nsSocketInputStream::OnSocketReady(nsresult condition)
   283 {
   284     SOCKET_LOG(("nsSocketInputStream::OnSocketReady [this=%p cond=%x]\n",
   285         this, condition));
   287     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   289     nsCOMPtr<nsIInputStreamCallback> callback;
   290     {
   291         MutexAutoLock lock(mTransport->mLock);
   293         // update condition, but be careful not to erase an already
   294         // existing error condition.
   295         if (NS_SUCCEEDED(mCondition))
   296             mCondition = condition;
   298         // ignore event if only waiting for closure and not closed.
   299         if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
   300             callback = mCallback;
   301             mCallback = nullptr;
   302             mCallbackFlags = 0;
   303         }
   304     }
   306     if (callback)
   307         callback->OnInputStreamReady(this);
   308 }
   310 NS_IMPL_QUERY_INTERFACE(nsSocketInputStream,
   311                         nsIInputStream,
   312                         nsIAsyncInputStream)
   314 NS_IMETHODIMP_(MozExternalRefCountType)
   315 nsSocketInputStream::AddRef()
   316 {
   317     ++mReaderRefCnt;
   318     return mTransport->AddRef();
   319 }
   321 NS_IMETHODIMP_(MozExternalRefCountType)
   322 nsSocketInputStream::Release()
   323 {
   324     if (--mReaderRefCnt == 0)
   325         Close();
   326     return mTransport->Release();
   327 }
   329 NS_IMETHODIMP
   330 nsSocketInputStream::Close()
   331 {
   332     return CloseWithStatus(NS_BASE_STREAM_CLOSED);
   333 }
   335 NS_IMETHODIMP
   336 nsSocketInputStream::Available(uint64_t *avail)
   337 {
   338     SOCKET_LOG(("nsSocketInputStream::Available [this=%p]\n", this));
   340     *avail = 0;
   342     PRFileDesc *fd;
   343     {
   344         MutexAutoLock lock(mTransport->mLock);
   346         if (NS_FAILED(mCondition))
   347             return mCondition;
   349         fd = mTransport->GetFD_Locked();
   350         if (!fd)
   351             return NS_OK;
   352     }
   354     // cannot hold lock while calling NSPR.  (worried about the fact that PSM
   355     // synchronously proxies notifications over to the UI thread, which could
   356     // mistakenly try to re-enter this code.)
   357     int32_t n = PR_Available(fd);
   359     // PSM does not implement PR_Available() so do a best approximation of it
   360     // with MSG_PEEK
   361     if ((n == -1) && (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR)) {
   362         char c;
   364         n = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
   365         SOCKET_LOG(("nsSocketInputStream::Available [this=%p] "
   366                     "using PEEK backup n=%d]\n", this, n));
   367     }
   369     nsresult rv;
   370     {
   371         MutexAutoLock lock(mTransport->mLock);
   373         mTransport->ReleaseFD_Locked(fd);
   375         if (n >= 0)
   376             *avail = n;
   377         else {
   378             PRErrorCode code = PR_GetError();
   379             if (code == PR_WOULD_BLOCK_ERROR)
   380                 return NS_OK;
   381             mCondition = ErrorAccordingToNSPR(code);
   382         }
   383         rv = mCondition;
   384     }
   385     if (NS_FAILED(rv))
   386         mTransport->OnInputClosed(rv);
   387     return rv;
   388 }
   390 NS_IMETHODIMP
   391 nsSocketInputStream::Read(char *buf, uint32_t count, uint32_t *countRead)
   392 {
   393     SOCKET_LOG(("nsSocketInputStream::Read [this=%p count=%u]\n", this, count));
   395     *countRead = 0;
   397     PRFileDesc* fd = nullptr;
   398     {
   399         MutexAutoLock lock(mTransport->mLock);
   401         if (NS_FAILED(mCondition))
   402             return (mCondition == NS_BASE_STREAM_CLOSED) ? NS_OK : mCondition;
   404         fd = mTransport->GetFD_Locked();
   405         if (!fd)
   406             return NS_BASE_STREAM_WOULD_BLOCK;
   407     }
   409     SOCKET_LOG(("  calling PR_Read [count=%u]\n", count));
   411     // cannot hold lock while calling NSPR.  (worried about the fact that PSM
   412     // synchronously proxies notifications over to the UI thread, which could
   413     // mistakenly try to re-enter this code.)
   414     int32_t n = PR_Read(fd, buf, count);
   416     SOCKET_LOG(("  PR_Read returned [n=%d]\n", n));
   418     nsresult rv = NS_OK;
   419     {
   420         MutexAutoLock lock(mTransport->mLock);
   422 #ifdef ENABLE_SOCKET_TRACING
   423         if (n > 0)
   424             mTransport->TraceInBuf(buf, n);
   425 #endif
   427         mTransport->ReleaseFD_Locked(fd);
   429         if (n > 0)
   430             mByteCount += (*countRead = n);
   431         else if (n < 0) {
   432             PRErrorCode code = PR_GetError();
   433             if (code == PR_WOULD_BLOCK_ERROR)
   434                 return NS_BASE_STREAM_WOULD_BLOCK;
   435             mCondition = ErrorAccordingToNSPR(code);
   436         }
   437         rv = mCondition;
   438     }
   439     if (NS_FAILED(rv))
   440         mTransport->OnInputClosed(rv);
   442     // only send this notification if we have indeed read some data.
   443     // see bug 196827 for an example of why this is important.
   444     if (n > 0)
   445         mTransport->SendStatus(NS_NET_STATUS_RECEIVING_FROM);
   446     return rv;
   447 }
   449 NS_IMETHODIMP
   450 nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
   451                                   uint32_t count, uint32_t *countRead)
   452 {
   453     // socket stream is unbuffered
   454     return NS_ERROR_NOT_IMPLEMENTED;
   455 }
   457 NS_IMETHODIMP
   458 nsSocketInputStream::IsNonBlocking(bool *nonblocking)
   459 {
   460     *nonblocking = true;
   461     return NS_OK;
   462 }
   464 NS_IMETHODIMP
   465 nsSocketInputStream::CloseWithStatus(nsresult reason)
   466 {
   467     SOCKET_LOG(("nsSocketInputStream::CloseWithStatus [this=%p reason=%x]\n", this, reason));
   469     // may be called from any thread
   471     nsresult rv;
   472     {
   473         MutexAutoLock lock(mTransport->mLock);
   475         if (NS_SUCCEEDED(mCondition))
   476             rv = mCondition = reason;
   477         else
   478             rv = NS_OK;
   479     }
   480     if (NS_FAILED(rv))
   481         mTransport->OnInputClosed(rv);
   482     return NS_OK;
   483 }
   485 NS_IMETHODIMP
   486 nsSocketInputStream::AsyncWait(nsIInputStreamCallback *callback,
   487                                uint32_t flags,
   488                                uint32_t amount,
   489                                nsIEventTarget *target)
   490 {
   491     SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%p]\n", this));
   493     bool hasError = false;
   494     {
   495         MutexAutoLock lock(mTransport->mLock);
   497         if (callback && target) {
   498             //
   499             // build event proxy
   500             //
   501             mCallback = NS_NewInputStreamReadyEvent(callback, target);
   502         }
   503         else
   504             mCallback = callback;
   505         mCallbackFlags = flags;
   507         hasError = NS_FAILED(mCondition);
   508     } // unlock mTransport->mLock
   510     if (hasError) {
   511         // OnSocketEvent will call OnInputStreamReady with an error code after
   512         // going through the event loop. We do this because most socket callers
   513         // do not expect AsyncWait() to synchronously execute the OnInputStreamReady
   514         // callback.
   515         mTransport->PostEvent(nsSocketTransport::MSG_INPUT_PENDING);
   516     } else {
   517         mTransport->OnInputPending();
   518     }
   520     return NS_OK;
   521 }
   523 //-----------------------------------------------------------------------------
   524 // socket output stream impl 
   525 //-----------------------------------------------------------------------------
   527 nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport *trans)
   528     : mTransport(trans)
   529     , mWriterRefCnt(0)
   530     , mCondition(NS_OK)
   531     , mCallbackFlags(0)
   532     , mByteCount(0)
   533 {
   534 }
   536 nsSocketOutputStream::~nsSocketOutputStream()
   537 {
   538 }
   540 // called on the socket transport thread...
   541 //
   542 //   condition : failure code if socket has been closed
   543 //
   544 void
   545 nsSocketOutputStream::OnSocketReady(nsresult condition)
   546 {
   547     SOCKET_LOG(("nsSocketOutputStream::OnSocketReady [this=%p cond=%x]\n",
   548         this, condition));
   550     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   552     nsCOMPtr<nsIOutputStreamCallback> callback;
   553     {
   554         MutexAutoLock lock(mTransport->mLock);
   556         // update condition, but be careful not to erase an already
   557         // existing error condition.
   558         if (NS_SUCCEEDED(mCondition))
   559             mCondition = condition;
   561         // ignore event if only waiting for closure and not closed.
   562         if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
   563             callback = mCallback;
   564             mCallback = nullptr;
   565             mCallbackFlags = 0;
   566         }
   567     }
   569     if (callback)
   570         callback->OnOutputStreamReady(this);
   571 }
   573 NS_IMPL_QUERY_INTERFACE(nsSocketOutputStream,
   574                         nsIOutputStream,
   575                         nsIAsyncOutputStream)
   577 NS_IMETHODIMP_(MozExternalRefCountType)
   578 nsSocketOutputStream::AddRef()
   579 {
   580     ++mWriterRefCnt;
   581     return mTransport->AddRef();
   582 }
   584 NS_IMETHODIMP_(MozExternalRefCountType)
   585 nsSocketOutputStream::Release()
   586 {
   587     if (--mWriterRefCnt == 0)
   588         Close();
   589     return mTransport->Release();
   590 }
   592 NS_IMETHODIMP
   593 nsSocketOutputStream::Close()
   594 {
   595     return CloseWithStatus(NS_BASE_STREAM_CLOSED);
   596 }
   598 NS_IMETHODIMP
   599 nsSocketOutputStream::Flush()
   600 {
   601     return NS_OK;
   602 }
   604 NS_IMETHODIMP
   605 nsSocketOutputStream::Write(const char *buf, uint32_t count, uint32_t *countWritten)
   606 {
   607     SOCKET_LOG(("nsSocketOutputStream::Write [this=%p count=%u]\n", this, count));
   609     *countWritten = 0;
   611     // A write of 0 bytes can be used to force the initial SSL handshake, so do
   612     // not reject that.
   614     PRFileDesc* fd = nullptr;
   615     {
   616         MutexAutoLock lock(mTransport->mLock);
   618         if (NS_FAILED(mCondition))
   619             return mCondition;
   621         fd = mTransport->GetFD_Locked();
   622         if (!fd)
   623             return NS_BASE_STREAM_WOULD_BLOCK;
   624     }
   626     SOCKET_LOG(("  calling PR_Write [count=%u]\n", count));
   628     // cannot hold lock while calling NSPR.  (worried about the fact that PSM
   629     // synchronously proxies notifications over to the UI thread, which could
   630     // mistakenly try to re-enter this code.)
   631     int32_t n = PR_Write(fd, buf, count);
   633     SOCKET_LOG(("  PR_Write returned [n=%d]\n", n));
   635     nsresult rv = NS_OK;
   636     {
   637         MutexAutoLock lock(mTransport->mLock);
   639 #ifdef ENABLE_SOCKET_TRACING
   640         if (n > 0)
   641             mTransport->TraceOutBuf(buf, n);
   642 #endif
   644         mTransport->ReleaseFD_Locked(fd);
   646         if (n > 0)
   647             mByteCount += (*countWritten = n);
   648         else if (n < 0) {
   649             PRErrorCode code = PR_GetError();
   650             if (code == PR_WOULD_BLOCK_ERROR)
   651                 return NS_BASE_STREAM_WOULD_BLOCK;
   652             mCondition = ErrorAccordingToNSPR(code);
   653         }
   654         rv = mCondition;
   655     }
   656     if (NS_FAILED(rv))
   657         mTransport->OnOutputClosed(rv);
   659     // only send this notification if we have indeed written some data.
   660     // see bug 196827 for an example of why this is important.
   661     if (n > 0)
   662         mTransport->SendStatus(NS_NET_STATUS_SENDING_TO);
   663     return rv;
   664 }
   666 NS_IMETHODIMP
   667 nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader, void *closure,
   668                                     uint32_t count, uint32_t *countRead)
   669 {
   670     // socket stream is unbuffered
   671     return NS_ERROR_NOT_IMPLEMENTED;
   672 }
   674 NS_METHOD
   675 nsSocketOutputStream::WriteFromSegments(nsIInputStream *input,
   676                                         void *closure,
   677                                         const char *fromSegment,
   678                                         uint32_t offset,
   679                                         uint32_t count,
   680                                         uint32_t *countRead)
   681 {
   682     nsSocketOutputStream *self = (nsSocketOutputStream *) closure;
   683     return self->Write(fromSegment, count, countRead);
   684 }
   686 NS_IMETHODIMP
   687 nsSocketOutputStream::WriteFrom(nsIInputStream *stream, uint32_t count, uint32_t *countRead)
   688 {
   689     return stream->ReadSegments(WriteFromSegments, this, count, countRead);
   690 }
   692 NS_IMETHODIMP
   693 nsSocketOutputStream::IsNonBlocking(bool *nonblocking)
   694 {
   695     *nonblocking = true;
   696     return NS_OK;
   697 }
   699 NS_IMETHODIMP
   700 nsSocketOutputStream::CloseWithStatus(nsresult reason)
   701 {
   702     SOCKET_LOG(("nsSocketOutputStream::CloseWithStatus [this=%p reason=%x]\n", this, reason));
   704     // may be called from any thread
   706     nsresult rv;
   707     {
   708         MutexAutoLock lock(mTransport->mLock);
   710         if (NS_SUCCEEDED(mCondition))
   711             rv = mCondition = reason;
   712         else
   713             rv = NS_OK;
   714     }
   715     if (NS_FAILED(rv))
   716         mTransport->OnOutputClosed(rv);
   717     return NS_OK;
   718 }
   720 NS_IMETHODIMP
   721 nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback *callback,
   722                                 uint32_t flags,
   723                                 uint32_t amount,
   724                                 nsIEventTarget *target)
   725 {
   726     SOCKET_LOG(("nsSocketOutputStream::AsyncWait [this=%p]\n", this));
   728     {
   729         MutexAutoLock lock(mTransport->mLock);
   731         if (callback && target) {
   732             //
   733             // build event proxy
   734             //
   735             mCallback = NS_NewOutputStreamReadyEvent(callback, target);
   736         }
   737         else
   738             mCallback = callback;
   740         mCallbackFlags = flags;
   741     }
   742     mTransport->OnOutputPending();
   743     return NS_OK;
   744 }
   746 //-----------------------------------------------------------------------------
   747 // socket transport impl
   748 //-----------------------------------------------------------------------------
   750 nsSocketTransport::nsSocketTransport()
   751     : mTypes(nullptr)
   752     , mTypeCount(0)
   753     , mPort(0)
   754     , mHttpsProxy(false)
   755     , mProxyUse(false)
   756     , mProxyTransparent(false)
   757     , mProxyTransparentResolvesHost(false)
   758     , mConnectionFlags(0)
   759     , mState(STATE_CLOSED)
   760     , mAttached(false)
   761     , mInputClosed(true)
   762     , mOutputClosed(true)
   763     , mResolving(false)
   764     , mNetAddrIsSet(false)
   765     , mLock("nsSocketTransport.mLock")
   766     , mFD(MOZ_THIS_IN_INITIALIZER_LIST())
   767     , mFDref(0)
   768     , mFDconnected(false)
   769     , mSocketTransportService(gSocketTransportService)
   770     , mInput(MOZ_THIS_IN_INITIALIZER_LIST())
   771     , mOutput(MOZ_THIS_IN_INITIALIZER_LIST())
   772     , mQoSBits(0x00)
   773     , mKeepaliveEnabled(false)
   774     , mKeepaliveIdleTimeS(-1)
   775     , mKeepaliveRetryIntervalS(-1)
   776     , mKeepaliveProbeCount(-1)
   777 {
   778     SOCKET_LOG(("creating nsSocketTransport @%p\n", this));
   780     mTimeouts[TIMEOUT_CONNECT]    = UINT16_MAX; // no timeout
   781     mTimeouts[TIMEOUT_READ_WRITE] = UINT16_MAX; // no timeout
   782 }
   784 nsSocketTransport::~nsSocketTransport()
   785 {
   786     SOCKET_LOG(("destroying nsSocketTransport @%p\n", this));
   788     // cleanup socket type info
   789     if (mTypes) {
   790         uint32_t i;
   791         for (i=0; i<mTypeCount; ++i)
   792             PL_strfree(mTypes[i]);
   793         free(mTypes);
   794     }
   795 }
   797 nsresult
   798 nsSocketTransport::Init(const char **types, uint32_t typeCount,
   799                         const nsACString &host, uint16_t port,
   800                         nsIProxyInfo *givenProxyInfo)
   801 {
   802     MOZ_EVENT_TRACER_NAME_OBJECT(this, host.BeginReading());
   804     nsCOMPtr<nsProxyInfo> proxyInfo;
   805     if (givenProxyInfo) {
   806         proxyInfo = do_QueryInterface(givenProxyInfo);
   807         NS_ENSURE_ARG(proxyInfo);
   808     }
   810     // init socket type info
   812     mPort = port;
   813     mHost = host;
   815     const char *proxyType = nullptr;
   816     if (proxyInfo) {
   817         mProxyInfo = proxyInfo;
   818         // grab proxy type (looking for "socks" for example)
   819         proxyType = proxyInfo->Type();
   820         if (proxyType && (strcmp(proxyType, "http") == 0 ||
   821                           strcmp(proxyType, "direct") == 0 ||
   822                           strcmp(proxyType, "unknown") == 0))
   823             proxyType = nullptr;
   825         mProxyUse = true;
   826         // check that we don't have a proxyInfo without proxy
   827         nsCString proxyHost;
   828         proxyInfo->GetHost(proxyHost);
   829         if (!proxyType || proxyHost.IsEmpty()) {
   830             mProxyUse = false;
   831         }
   832     }
   834     SOCKET_LOG(("nsSocketTransport::Init [this=%x host=%s:%hu proxy=%s]\n",
   835         this, mHost.get(), mPort, mProxyUse ? "yes" : "no"));
   837     // include proxy type as a socket type if proxy type is not "http"
   838     mTypeCount = typeCount + (proxyType != nullptr);
   839     if (!mTypeCount)
   840         return NS_OK;
   842     // if we have socket types, then the socket provider service had
   843     // better exist!
   844     nsresult rv;
   845     nsCOMPtr<nsISocketProviderService> spserv =
   846         do_GetService(kSocketProviderServiceCID, &rv);
   847     if (NS_FAILED(rv)) return rv;
   849     mTypes = (char **) malloc(mTypeCount * sizeof(char *));
   850     if (!mTypes)
   851         return NS_ERROR_OUT_OF_MEMORY;
   853     // now verify that each socket type has a registered socket provider.
   854     for (uint32_t i = 0, type = 0; i < mTypeCount; ++i) {
   855         // store socket types
   856         if (i == 0 && proxyType)
   857             mTypes[i] = PL_strdup(proxyType);
   858         else
   859             mTypes[i] = PL_strdup(types[type++]);
   861         if (!mTypes[i]) {
   862             mTypeCount = i;
   863             return NS_ERROR_OUT_OF_MEMORY;
   864         }
   865         nsCOMPtr<nsISocketProvider> provider;
   866         rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
   867         if (NS_FAILED(rv)) {
   868             NS_WARNING("no registered socket provider");
   869             return rv;
   870         }
   872         // note if socket type corresponds to a transparent proxy
   873         // XXX don't hardcode SOCKS here (use proxy info's flags instead).
   874         if ((strcmp(mTypes[i], "socks") == 0) ||
   875             (strcmp(mTypes[i], "socks4") == 0)) {
   876             mProxyTransparent = true;
   878             if (proxyInfo->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
   879                 // we want the SOCKS layer to send the hostname
   880                 // and port to the proxy and let it do the DNS.
   881                 mProxyTransparentResolvesHost = true;
   882             }
   883         }
   884     }
   886     return NS_OK;
   887 }
   889 nsresult
   890 nsSocketTransport::InitWithFilename(const char *filename)
   891 {
   892 #if defined(XP_UNIX)
   893     size_t filenameLength = strlen(filename);
   895     if (filenameLength > sizeof(mNetAddr.local.path) - 1)
   896         return NS_ERROR_FILE_NAME_TOO_LONG;
   898     mHost.Assign(filename);
   899     mPort = 0;
   900     mTypeCount = 0;
   902     mNetAddr.local.family = AF_LOCAL;
   903     memcpy(mNetAddr.local.path, filename, filenameLength);
   904     mNetAddr.local.path[filenameLength] = '\0';
   905     mNetAddrIsSet = true;
   907     return NS_OK;
   908 #else
   909     return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
   910 #endif
   911 }
   913 nsresult
   914 nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const NetAddr *addr)
   915 {
   916     NS_ASSERTION(!mFD.IsInitialized(), "already initialized");
   918     char buf[kNetAddrMaxCStrBufSize];
   919     NetAddrToString(addr, buf, sizeof(buf));
   920     mHost.Assign(buf);
   922     uint16_t port;
   923     if (addr->raw.family == AF_INET)
   924         port = addr->inet.port;
   925     else if (addr->raw.family == AF_INET6)
   926         port = addr->inet6.port;
   927     else
   928         port = 0;
   929     mPort = ntohs(port);
   931     memcpy(&mNetAddr, addr, sizeof(NetAddr));
   933     mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
   934     mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
   935     mState = STATE_TRANSFERRING;
   936     mNetAddrIsSet = true;
   938     {
   939         MutexAutoLock lock(mLock);
   941         mFD = fd;
   942         mFDref = 1;
   943         mFDconnected = 1;
   944     }
   946     // make sure new socket is non-blocking
   947     PRSocketOptionData opt;
   948     opt.option = PR_SockOpt_Nonblocking;
   949     opt.value.non_blocking = true;
   950     PR_SetSocketOption(fd, &opt);
   952     SOCKET_LOG(("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
   953         this, mHost.get(), mPort));
   955     // jump to InitiateSocket to get ourselves attached to the STS poll list.
   956     return PostEvent(MSG_RETRY_INIT_SOCKET);
   957 }
   959 nsresult
   960 nsSocketTransport::PostEvent(uint32_t type, nsresult status, nsISupports *param)
   961 {
   962     SOCKET_LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%x param=%p]\n",
   963         this, type, status, param));
   965     nsCOMPtr<nsIRunnable> event = new nsSocketEvent(this, type, status, param);
   966     if (!event)
   967         return NS_ERROR_OUT_OF_MEMORY;
   969     return mSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
   970 }
   972 void
   973 nsSocketTransport::SendStatus(nsresult status)
   974 {
   975     SOCKET_LOG(("nsSocketTransport::SendStatus [this=%p status=%x]\n", this, status));
   977     nsCOMPtr<nsITransportEventSink> sink;
   978     uint64_t progress;
   979     {
   980         MutexAutoLock lock(mLock);
   981         sink = mEventSink;
   982         switch (status) {
   983         case NS_NET_STATUS_SENDING_TO:
   984             progress = mOutput.ByteCount();
   985             break;
   986         case NS_NET_STATUS_RECEIVING_FROM:
   987             progress = mInput.ByteCount();
   988             break;
   989         default:
   990             progress = 0;
   991             break;
   992         }
   993     }
   994     if (sink)
   995         sink->OnTransportStatus(this, status, progress, UINT64_MAX);
   996 }
   998 nsresult
   999 nsSocketTransport::ResolveHost()
  1001     SOCKET_LOG(("nsSocketTransport::ResolveHost [this=%p %s:%d%s]\n",
  1002                 this, SocketHost().get(), SocketPort(),
  1003                 mConnectionFlags & nsSocketTransport::BYPASS_CACHE ?
  1004                 " bypass cache" : ""));
  1006     nsresult rv;
  1008     if (mProxyUse) {
  1009         if (!mProxyTransparent || mProxyTransparentResolvesHost) {
  1010 #if defined(XP_UNIX)
  1011             NS_ABORT_IF_FALSE(!mNetAddrIsSet || mNetAddr.raw.family != AF_LOCAL,
  1012                               "Unix domain sockets can't be used with proxies");
  1013 #endif
  1014             // When not resolving mHost locally, we still want to ensure that
  1015             // it only contains valid characters.  See bug 304904 for details.
  1016             if (!net_IsValidHostName(mHost))
  1017                 return NS_ERROR_UNKNOWN_HOST;
  1019         if (mProxyTransparentResolvesHost) {
  1020             // Name resolution is done on the server side.  Just pretend
  1021             // client resolution is complete, this will get picked up later.
  1022             // since we don't need to do DNS now, we bypass the resolving
  1023             // step by initializing mNetAddr to an empty address, but we
  1024             // must keep the port. The SOCKS IO layer will use the hostname
  1025             // we send it when it's created, rather than the empty address
  1026             // we send with the connect call.
  1027             mState = STATE_RESOLVING;
  1028             mNetAddr.raw.family = AF_INET;
  1029             mNetAddr.inet.port = htons(SocketPort());
  1030             mNetAddr.inet.ip = htonl(INADDR_ANY);
  1031             return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nullptr);
  1035     nsCOMPtr<nsIDNSService> dns = do_GetService(kDNSServiceCID, &rv);
  1036     if (NS_FAILED(rv)) return rv;
  1038     mResolving = true;
  1040     uint32_t dnsFlags = 0;
  1041     if (mConnectionFlags & nsSocketTransport::BYPASS_CACHE)
  1042         dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE;
  1043     if (mConnectionFlags & nsSocketTransport::DISABLE_IPV6)
  1044         dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
  1045     if (mConnectionFlags & nsSocketTransport::DISABLE_IPV4)
  1046         dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4;
  1048     NS_ASSERTION(!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) ||
  1049                  !(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4),
  1050                  "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");
  1052     SendStatus(NS_NET_STATUS_RESOLVING_HOST);
  1053     rv = dns->AsyncResolve(SocketHost(), dnsFlags, this, nullptr,
  1054                            getter_AddRefs(mDNSRequest));
  1055     if (NS_SUCCEEDED(rv)) {
  1056         SOCKET_LOG(("  advancing to STATE_RESOLVING\n"));
  1057         mState = STATE_RESOLVING;
  1059     return rv;
  1062 nsresult
  1063 nsSocketTransport::BuildSocket(PRFileDesc *&fd, bool &proxyTransparent, bool &usingSSL)
  1065     SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%p]\n", this));
  1067     nsresult rv;
  1069     proxyTransparent = false;
  1070     usingSSL = false;
  1072     if (mTypeCount == 0) {
  1073         fd = PR_OpenTCPSocket(mNetAddr.raw.family);
  1074         rv = fd ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
  1076     else {
  1077 #if defined(XP_UNIX)
  1078         NS_ABORT_IF_FALSE(!mNetAddrIsSet || mNetAddr.raw.family != AF_LOCAL,
  1079                           "Unix domain sockets can't be used with socket types");
  1080 #endif
  1082         fd = nullptr;
  1084         nsCOMPtr<nsISocketProviderService> spserv =
  1085             do_GetService(kSocketProviderServiceCID, &rv);
  1086         if (NS_FAILED(rv)) return rv;
  1088         const char *host       = mHost.get();
  1089         int32_t     port       = (int32_t) mPort;
  1090         uint32_t    proxyFlags = 0;
  1091         nsCOMPtr<nsIProxyInfo> proxy = mProxyInfo;
  1093         uint32_t i;
  1094         for (i=0; i<mTypeCount; ++i) {
  1095             nsCOMPtr<nsISocketProvider> provider;
  1097             SOCKET_LOG(("  pushing io layer [%u:%s]\n", i, mTypes[i]));
  1099             rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
  1100             if (NS_FAILED(rv))
  1101                 break;
  1103             if (mProxyTransparentResolvesHost)
  1104                 proxyFlags |= nsISocketProvider::PROXY_RESOLVES_HOST;
  1106             if (mConnectionFlags & nsISocketTransport::ANONYMOUS_CONNECT)
  1107                 proxyFlags |= nsISocketProvider::ANONYMOUS_CONNECT;
  1109             if (mConnectionFlags & nsISocketTransport::NO_PERMANENT_STORAGE)
  1110                 proxyFlags |= nsISocketProvider::NO_PERMANENT_STORAGE;
  1113             nsCOMPtr<nsISupports> secinfo;
  1114             if (i == 0) {
  1115                 // if this is the first type, we'll want the 
  1116                 // service to allocate a new socket
  1117                 nsCString proxyHost;
  1118                 GetHost(proxyHost);
  1119                 int32_t proxyPort;
  1120                 GetPort(&proxyPort);
  1121                 rv = provider->NewSocket(mNetAddr.raw.family,
  1122                                          mHttpsProxy ? proxyHost.get() : host,
  1123                                          mHttpsProxy ? proxyPort : port,
  1124                                          proxy,
  1125                                          proxyFlags, &fd,
  1126                                          getter_AddRefs(secinfo));
  1128                 if (NS_SUCCEEDED(rv) && !fd) {
  1129                     NS_NOTREACHED("NewSocket succeeded but failed to create a PRFileDesc");
  1130                     rv = NS_ERROR_UNEXPECTED;
  1133             else {
  1134                 // the socket has already been allocated, 
  1135                 // so we just want the service to add itself
  1136                 // to the stack (such as pushing an io layer)
  1137                 rv = provider->AddToSocket(mNetAddr.raw.family,
  1138                                            host, port, proxy,
  1139                                            proxyFlags, fd,
  1140                                            getter_AddRefs(secinfo));
  1142             // proxyFlags = 0; not used below this point...
  1143             if (NS_FAILED(rv))
  1144                 break;
  1146             // if the service was ssl or starttls, we want to hold onto the socket info
  1147             bool isSSL = (strcmp(mTypes[i], "ssl") == 0);
  1148             if (isSSL || (strcmp(mTypes[i], "starttls") == 0)) {
  1149                 // remember security info and give notification callbacks to PSM...
  1150                 nsCOMPtr<nsIInterfaceRequestor> callbacks;
  1152                     MutexAutoLock lock(mLock);
  1153                     mSecInfo = secinfo;
  1154                     callbacks = mCallbacks;
  1155                     SOCKET_LOG(("  [secinfo=%x callbacks=%x]\n", mSecInfo.get(), mCallbacks.get()));
  1157                 // don't call into PSM while holding mLock!!
  1158                 nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
  1159                 if (secCtrl)
  1160                     secCtrl->SetNotificationCallbacks(callbacks);
  1161                 // remember if socket type is SSL so we can ProxyStartSSL if need be.
  1162                 usingSSL = isSSL;
  1164             else if ((strcmp(mTypes[i], "socks") == 0) ||
  1165                      (strcmp(mTypes[i], "socks4") == 0)) {
  1166                 // since socks is transparent, any layers above
  1167                 // it do not have to worry about proxy stuff
  1168                 proxy = nullptr;
  1169                 proxyTransparent = true;
  1173         if (NS_FAILED(rv)) {
  1174             SOCKET_LOG(("  error pushing io layer [%u:%s rv=%x]\n", i, mTypes[i], rv));
  1175             if (fd)
  1176                 PR_Close(fd);
  1180     return rv;
  1183 nsresult
  1184 nsSocketTransport::InitiateSocket()
  1186     SOCKET_LOG(("nsSocketTransport::InitiateSocket [this=%p]\n", this));
  1188     static bool crashOnNonLocalConnections = !!getenv("MOZ_DISABLE_NONLOCAL_CONNECTIONS");
  1190     nsresult rv;
  1191     bool isLocal;
  1192     IsLocal(&isLocal);
  1194     if (gIOService->IsOffline()) {
  1195         if (!isLocal)
  1196             return NS_ERROR_OFFLINE;
  1197     } else if (!isLocal) {
  1198         if (NS_SUCCEEDED(mCondition) &&
  1199             crashOnNonLocalConnections &&
  1200             !(IsIPAddrAny(&mNetAddr) || IsIPAddrLocal(&mNetAddr))) {
  1201             nsAutoCString ipaddr;
  1202             nsRefPtr<nsNetAddr> netaddr = new nsNetAddr(&mNetAddr);
  1203             netaddr->GetAddress(ipaddr);
  1204             fprintf_stderr(stderr,
  1205                            "FATAL ERROR: Non-local network connections are disabled and a connection "
  1206                            "attempt to %s (%s) was made.\nYou should only access hostnames "
  1207                            "available via the test networking proxy (if running mochitests) "
  1208                            "or from a test-specific httpd.js server (if running xpcshell tests). "
  1209                            "Browser services should be disabled or redirected to a local server.\n",
  1210                            mHost.get(), ipaddr.get());
  1211             MOZ_CRASH("Attempting to connect to non-local address!");
  1215     // Hosts/Proxy Hosts that are Local IP Literals should not be speculatively
  1216     // connected - Bug 853423.
  1217     if (mConnectionFlags & nsISocketTransport::DISABLE_RFC1918 &&
  1218         IsIPAddrLocal(&mNetAddr)) {
  1219 #ifdef PR_LOGGING
  1220         if (SOCKET_LOG_ENABLED()) {
  1221             nsAutoCString netAddrCString;
  1222             netAddrCString.SetCapacity(kIPv6CStrBufSize);
  1223             if (!NetAddrToString(&mNetAddr,
  1224                                  netAddrCString.BeginWriting(),
  1225                                  kIPv6CStrBufSize))
  1226                 netAddrCString = NS_LITERAL_CSTRING("<IP-to-string failed>");
  1227             nsCString proxyHost;
  1228             GetHost(proxyHost);
  1229             int32_t proxyPort;
  1230             GetPort(&proxyPort);
  1231             SOCKET_LOG(("nsSocketTransport::InitiateSocket skipping "
  1232                         "speculative connection for host [%s:%d] proxy "
  1233                         "[%s:%d] with Local IP address [%s]",
  1234                         mHost.get(), mPort, proxyHost.get(), proxyPort,
  1235                         netAddrCString.get()));
  1237 #endif
  1238         return NS_ERROR_CONNECTION_REFUSED;
  1241     //
  1242     // find out if it is going to be ok to attach another socket to the STS.
  1243     // if not then we have to wait for the STS to tell us that it is ok.
  1244     // the notification is asynchronous, which means that when we could be
  1245     // in a race to call AttachSocket once notified.  for this reason, when
  1246     // we get notified, we just re-enter this function.  as a result, we are
  1247     // sure to ask again before calling AttachSocket.  in this way we deal
  1248     // with the race condition.  though it isn't the most elegant solution,
  1249     // it is far simpler than trying to build a system that would guarantee
  1250     // FIFO ordering (which wouldn't even be that valuable IMO).  see bug
  1251     // 194402 for more info.
  1252     //
  1253     if (!mSocketTransportService->CanAttachSocket()) {
  1254         nsCOMPtr<nsIRunnable> event =
  1255                 new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET);
  1256         if (!event)
  1257             return NS_ERROR_OUT_OF_MEMORY;
  1258         return mSocketTransportService->NotifyWhenCanAttachSocket(event);
  1261     //
  1262     // if we already have a connected socket, then just attach and return.
  1263     //
  1264     if (mFD.IsInitialized()) {
  1265         rv = mSocketTransportService->AttachSocket(mFD, this);
  1266         if (NS_SUCCEEDED(rv))
  1267             mAttached = true;
  1268         return rv;
  1271     //
  1272     // create new socket fd, push io layers, etc.
  1273     //
  1274     PRFileDesc *fd;
  1275     bool proxyTransparent;
  1276     bool usingSSL;
  1278     rv = BuildSocket(fd, proxyTransparent, usingSSL);
  1279     if (NS_FAILED(rv)) {
  1280         SOCKET_LOG(("  BuildSocket failed [rv=%x]\n", rv));
  1281         return rv;
  1284     // Attach network activity monitor
  1285     mozilla::net::NetworkActivityMonitor::AttachIOLayer(fd);
  1287     PRStatus status;
  1289     // Make the socket non-blocking...
  1290     PRSocketOptionData opt;
  1291     opt.option = PR_SockOpt_Nonblocking;
  1292     opt.value.non_blocking = true;
  1293     status = PR_SetSocketOption(fd, &opt);
  1294     NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
  1296     // disable the nagle algorithm - if we rely on it to coalesce writes into
  1297     // full packets the final packet of a multi segment POST/PUT or pipeline
  1298     // sequence is delayed a full rtt
  1299     opt.option = PR_SockOpt_NoDelay;
  1300     opt.value.no_delay = true;
  1301     PR_SetSocketOption(fd, &opt);
  1303     // if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF
  1304     // The Windows default of 8KB is too small and as of vista sp1, autotuning
  1305     // only applies to receive window
  1306     int32_t sndBufferSize;
  1307     mSocketTransportService->GetSendBufferSize(&sndBufferSize);
  1308     if (sndBufferSize > 0) {
  1309         opt.option = PR_SockOpt_SendBufferSize;
  1310         opt.value.send_buffer_size = sndBufferSize;
  1311         PR_SetSocketOption(fd, &opt);
  1314     if (mQoSBits) {
  1315         opt.option = PR_SockOpt_IpTypeOfService;
  1316         opt.value.tos = mQoSBits;
  1317         PR_SetSocketOption(fd, &opt);
  1320     // inform socket transport about this newly created socket...
  1321     rv = mSocketTransportService->AttachSocket(fd, this);
  1322     if (NS_FAILED(rv)) {
  1323         PR_Close(fd);
  1324         return rv;
  1326     mAttached = true;
  1328     // assign mFD so that we can properly handle OnSocketDetached before we've
  1329     // established a connection.
  1331         MutexAutoLock lock(mLock);
  1332         mFD = fd;
  1333         mFDref = 1;
  1334         mFDconnected = false;
  1337     SOCKET_LOG(("  advancing to STATE_CONNECTING\n"));
  1338     mState = STATE_CONNECTING;
  1339     mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
  1340     SendStatus(NS_NET_STATUS_CONNECTING_TO);
  1342 #if defined(PR_LOGGING)
  1343     if (SOCKET_LOG_ENABLED()) {
  1344         char buf[kNetAddrMaxCStrBufSize];
  1345         NetAddrToString(&mNetAddr, buf, sizeof(buf));
  1346         SOCKET_LOG(("  trying address: %s\n", buf));
  1348 #endif
  1350     //
  1351     // Initiate the connect() to the host...
  1352     //
  1353     PRNetAddr prAddr;
  1354     NetAddrToPRNetAddr(&mNetAddr, &prAddr);
  1356     MOZ_EVENT_TRACER_EXEC(this, "net::tcp::connect");
  1357     status = PR_Connect(fd, &prAddr, NS_SOCKET_CONNECT_TIMEOUT);
  1358     if (status == PR_SUCCESS) {
  1359         // 
  1360         // we are connected!
  1361         //
  1362         OnSocketConnected();
  1364     else {
  1365         PRErrorCode code = PR_GetError();
  1366 #if defined(TEST_CONNECT_ERRORS)
  1367         code = RandomizeConnectError(code);
  1368 #endif
  1369         //
  1370         // If the PR_Connect(...) would block, then poll for a connection.
  1371         //
  1372         if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code))
  1373             mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
  1374         //
  1375         // If the socket is already connected, then return success...
  1376         //
  1377         else if (PR_IS_CONNECTED_ERROR == code) {
  1378             //
  1379             // we are connected!
  1380             //
  1381             OnSocketConnected();
  1383             if (mSecInfo && mProxyUse && proxyTransparent && usingSSL) {
  1384                 // if the connection phase is finished, and the ssl layer has
  1385                 // been pushed, and we were proxying (transparently; ie. nothing
  1386                 // has to happen in the protocol layer above us), it's time for
  1387                 // the ssl to start doing it's thing.
  1388                 nsCOMPtr<nsISSLSocketControl> secCtrl =
  1389                     do_QueryInterface(mSecInfo);
  1390                 if (secCtrl) {
  1391                     SOCKET_LOG(("  calling ProxyStartSSL()\n"));
  1392                     secCtrl->ProxyStartSSL();
  1394                 // XXX what if we were forced to poll on the socket for a successful
  1395                 // connection... wouldn't we need to call ProxyStartSSL after a call
  1396                 // to PR_ConnectContinue indicates that we are connected?
  1397                 //
  1398                 // XXX this appears to be what the old socket transport did.  why
  1399                 // isn't this broken?
  1402         //
  1403         // A SOCKS request was rejected; get the actual error code from
  1404         // the OS error
  1405         //
  1406         else if (PR_UNKNOWN_ERROR == code &&
  1407                  mProxyUse && mProxyTransparent) {
  1408             code = PR_GetOSError();
  1409             rv = ErrorAccordingToNSPR(code);
  1411         //
  1412         // The connection was refused...
  1413         //
  1414         else {
  1415             rv = ErrorAccordingToNSPR(code);
  1416             if (rv == NS_ERROR_CONNECTION_REFUSED && mProxyUse)
  1417                 rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
  1420     return rv;
  1423 bool
  1424 nsSocketTransport::RecoverFromError()
  1426     NS_ASSERTION(NS_FAILED(mCondition), "there should be something wrong");
  1428     SOCKET_LOG(("nsSocketTransport::RecoverFromError [this=%p state=%x cond=%x]\n",
  1429         this, mState, mCondition));
  1431 #if defined(XP_UNIX)
  1432     // Unix domain connections don't have multiple addresses to try,
  1433     // so the recovery techniques here don't apply.
  1434     if (mNetAddrIsSet && mNetAddr.raw.family == AF_LOCAL)
  1435         return false;
  1436 #endif
  1438     // can only recover from errors in these states
  1439     if (mState != STATE_RESOLVING && mState != STATE_CONNECTING)
  1440         return false;
  1442     nsresult rv;
  1444     // OK to check this outside mLock
  1445     NS_ASSERTION(!mFDconnected, "socket should not be connected");
  1447     // all connection failures need to be reported to DNS so that the next
  1448     // time we will use a different address if available.
  1449     if (mState == STATE_CONNECTING && mDNSRecord) {
  1450         mDNSRecord->ReportUnusable(SocketPort());
  1453     // can only recover from these errors
  1454     if (mCondition != NS_ERROR_CONNECTION_REFUSED &&
  1455         mCondition != NS_ERROR_PROXY_CONNECTION_REFUSED &&
  1456         mCondition != NS_ERROR_NET_TIMEOUT &&
  1457         mCondition != NS_ERROR_UNKNOWN_HOST &&
  1458         mCondition != NS_ERROR_UNKNOWN_PROXY_HOST)
  1459         return false;
  1461     bool tryAgain = false;
  1463     if (mConnectionFlags & (DISABLE_IPV6 | DISABLE_IPV4) &&
  1464         mCondition == NS_ERROR_UNKNOWN_HOST &&
  1465         mState == STATE_RESOLVING &&
  1466         !mProxyTransparentResolvesHost) {
  1467         SOCKET_LOG(("  trying lookup again with both ipv4/ipv6 enabled\n"));
  1468         mConnectionFlags &= ~(DISABLE_IPV6 | DISABLE_IPV4);
  1469         tryAgain = true;
  1472     // try next ip address only if past the resolver stage...
  1473     if (mState == STATE_CONNECTING && mDNSRecord) {
  1474         nsresult rv = mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
  1475         if (NS_SUCCEEDED(rv)) {
  1476             SOCKET_LOG(("  trying again with next ip address\n"));
  1477             tryAgain = true;
  1479         else if (mConnectionFlags & (DISABLE_IPV6 | DISABLE_IPV4)) {
  1480             // Drop state to closed.  This will trigger new round of DNS
  1481             // resolving bellow.
  1482             // XXX Could be optimized to only switch the flags to save duplicate
  1483             // connection attempts.
  1484             SOCKET_LOG(("  failed to connect all ipv4-only or ipv6-only hosts,"
  1485                         " trying lookup/connect again with both ipv4/ipv6\n"));
  1486             mState = STATE_CLOSED;
  1487             mConnectionFlags &= ~(DISABLE_IPV6 | DISABLE_IPV4);
  1488             tryAgain = true;
  1492 #if defined(XP_WIN)
  1493     // If not trying next address, try to make a connection using dialup. 
  1494     // Retry if that connection is made.
  1495     if (!tryAgain) {
  1496         bool autodialEnabled;
  1497         mSocketTransportService->GetAutodialEnabled(&autodialEnabled);
  1498         if (autodialEnabled) {
  1499           tryAgain = nsNativeConnectionHelper::OnConnectionFailed(
  1500                        NS_ConvertUTF8toUTF16(SocketHost()).get());
  1503 #endif
  1505     // prepare to try again.
  1506     if (tryAgain) {
  1507         uint32_t msg;
  1509         if (mState == STATE_CONNECTING) {
  1510             mState = STATE_RESOLVING;
  1511             msg = MSG_DNS_LOOKUP_COMPLETE;
  1513         else {
  1514             mState = STATE_CLOSED;
  1515             msg = MSG_ENSURE_CONNECT;
  1518         rv = PostEvent(msg, NS_OK);
  1519         if (NS_FAILED(rv))
  1520             tryAgain = false;
  1523     return tryAgain;
  1526 // called on the socket thread only
  1527 void
  1528 nsSocketTransport::OnMsgInputClosed(nsresult reason)
  1530     SOCKET_LOG(("nsSocketTransport::OnMsgInputClosed [this=%p reason=%x]\n",
  1531         this, reason));
  1533     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1535     mInputClosed = true;
  1536     // check if event should affect entire transport
  1537     if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
  1538         mCondition = reason;                // XXX except if NS_FAILED(mCondition), right??
  1539     else if (mOutputClosed)
  1540         mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
  1541     else {
  1542         if (mState == STATE_TRANSFERRING)
  1543             mPollFlags &= ~PR_POLL_READ;
  1544         mInput.OnSocketReady(reason);
  1548 // called on the socket thread only
  1549 void
  1550 nsSocketTransport::OnMsgOutputClosed(nsresult reason)
  1552     SOCKET_LOG(("nsSocketTransport::OnMsgOutputClosed [this=%p reason=%x]\n",
  1553         this, reason));
  1555     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1557     mOutputClosed = true;
  1558     // check if event should affect entire transport
  1559     if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
  1560         mCondition = reason;                // XXX except if NS_FAILED(mCondition), right??
  1561     else if (mInputClosed)
  1562         mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
  1563     else {
  1564         if (mState == STATE_TRANSFERRING)
  1565             mPollFlags &= ~PR_POLL_WRITE;
  1566         mOutput.OnSocketReady(reason);
  1570 void
  1571 nsSocketTransport::OnSocketConnected()
  1573     SOCKET_LOG(("  advancing to STATE_TRANSFERRING\n"));
  1575     mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
  1576     mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
  1577     mState = STATE_TRANSFERRING;
  1579     // Set the mNetAddrIsSet flag only when state has reached TRANSFERRING
  1580     // because we need to make sure its value does not change due to failover
  1581     mNetAddrIsSet = true;
  1583     // assign mFD (must do this within the transport lock), but take care not
  1584     // to trample over mFDref if mFD is already set.
  1586         MutexAutoLock lock(mLock);
  1587         NS_ASSERTION(mFD.IsInitialized(), "no socket");
  1588         NS_ASSERTION(mFDref == 1, "wrong socket ref count");
  1589         mFDconnected = true;
  1592     // Ensure keepalive is configured correctly if previously enabled.
  1593     if (mKeepaliveEnabled) {
  1594         nsresult rv = SetKeepaliveEnabledInternal(true);
  1595         if (NS_WARN_IF(NS_FAILED(rv))) {
  1596             SOCKET_LOG(("  SetKeepaliveEnabledInternal failed rv[0x%x]", rv));
  1600     MOZ_EVENT_TRACER_DONE(this, "net::tcp::connect");
  1602     SendStatus(NS_NET_STATUS_CONNECTED_TO);
  1605 PRFileDesc *
  1606 nsSocketTransport::GetFD_Locked()
  1608     mLock.AssertCurrentThreadOwns();
  1610     // mFD is not available to the streams while disconnected.
  1611     if (!mFDconnected)
  1612         return nullptr;
  1614     if (mFD.IsInitialized())
  1615         mFDref++;
  1617     return mFD;
  1620 class ThunkPRClose : public nsRunnable
  1622 public:
  1623   ThunkPRClose(PRFileDesc *fd) : mFD(fd) {}
  1625   NS_IMETHOD Run()
  1627     PR_Close(mFD);
  1628     return NS_OK;
  1630 private:
  1631   PRFileDesc *mFD;
  1632 };
  1634 void
  1635 STS_PRCloseOnSocketTransport(PRFileDesc *fd)
  1637   if (gSocketTransportService) {
  1638     // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
  1639     // FIX - Should use RUN_ON_THREAD once it's generally available
  1640     // RUN_ON_THREAD(gSocketThread,WrapRunnableNM(&PR_Close, mFD);
  1641     gSocketTransportService->Dispatch(new ThunkPRClose(fd), NS_DISPATCH_NORMAL);
  1642   } else {
  1643     // something horrible has happened
  1644     NS_ASSERTION(gSocketTransportService, "No STS service");
  1648 void
  1649 nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd)
  1651     mLock.AssertCurrentThreadOwns();
  1653     NS_ASSERTION(mFD == fd, "wrong fd");
  1654     SOCKET_LOG(("JIMB: ReleaseFD_Locked: mFDref = %d\n", mFDref));
  1656     if (--mFDref == 0) {
  1657         if (PR_GetCurrentThread() == gSocketThread) {
  1658             SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%p]\n", this));
  1659             PR_Close(mFD);
  1660         } else {
  1661             // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
  1662             STS_PRCloseOnSocketTransport(mFD);
  1664         mFD = nullptr;
  1668 //-----------------------------------------------------------------------------
  1669 // socket event handler impl
  1671 void
  1672 nsSocketTransport::OnSocketEvent(uint32_t type, nsresult status, nsISupports *param)
  1674     SOCKET_LOG(("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%x param=%p]\n",
  1675         this, type, status, param));
  1677     if (NS_FAILED(mCondition)) {
  1678         // block event since we're apparently already dead.
  1679         SOCKET_LOG(("  blocking event [condition=%x]\n", mCondition));
  1680         //
  1681         // notify input/output streams in case either has a pending notify.
  1682         //
  1683         mInput.OnSocketReady(mCondition);
  1684         mOutput.OnSocketReady(mCondition);
  1685         return;
  1688     switch (type) {
  1689     case MSG_ENSURE_CONNECT:
  1690         SOCKET_LOG(("  MSG_ENSURE_CONNECT\n"));
  1691         //
  1692         // ensure that we have created a socket, attached it, and have a
  1693         // connection.
  1694         //
  1695         if (mState == STATE_CLOSED) {
  1696             // Unix domain sockets are ready to connect; mNetAddr is all we
  1697             // need. Internet address families require a DNS lookup (or possibly
  1698             // several) before we can connect.
  1699 #if defined(XP_UNIX)
  1700             if (mNetAddrIsSet && mNetAddr.raw.family == AF_LOCAL)
  1701                 mCondition = InitiateSocket();
  1702             else
  1703 #endif
  1704                 mCondition = ResolveHost();
  1706         } else {
  1707             SOCKET_LOG(("  ignoring redundant event\n"));
  1709         break;
  1711     case MSG_DNS_LOOKUP_COMPLETE:
  1712         if (mDNSRequest)  // only send this if we actually resolved anything
  1713             SendStatus(NS_NET_STATUS_RESOLVED_HOST);
  1715         SOCKET_LOG(("  MSG_DNS_LOOKUP_COMPLETE\n"));
  1716         mDNSRequest = 0;
  1717         if (param) {
  1718             mDNSRecord = static_cast<nsIDNSRecord *>(param);
  1719             mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
  1721         // status contains DNS lookup status
  1722         if (NS_FAILED(status)) {
  1723             // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP 
  1724             // proxy host is not found, so we fixup the error code.
  1725             // For SOCKS proxies (mProxyTransparent == true), the socket 
  1726             // transport resolves the real host here, so there's no fixup 
  1727             // (see bug 226943).
  1728             if (status == NS_ERROR_UNKNOWN_HOST && !mProxyTransparent &&
  1729                 mProxyUse)
  1730                 mCondition = NS_ERROR_UNKNOWN_PROXY_HOST;
  1731             else
  1732                 mCondition = status;
  1734         else if (mState == STATE_RESOLVING)
  1735             mCondition = InitiateSocket();
  1736         break;
  1738     case MSG_RETRY_INIT_SOCKET:
  1739         mCondition = InitiateSocket();
  1740         break;
  1742     case MSG_INPUT_CLOSED:
  1743         SOCKET_LOG(("  MSG_INPUT_CLOSED\n"));
  1744         OnMsgInputClosed(status);
  1745         break;
  1747     case MSG_INPUT_PENDING:
  1748         SOCKET_LOG(("  MSG_INPUT_PENDING\n"));
  1749         OnMsgInputPending();
  1750         break;
  1752     case MSG_OUTPUT_CLOSED:
  1753         SOCKET_LOG(("  MSG_OUTPUT_CLOSED\n"));
  1754         OnMsgOutputClosed(status);
  1755         break;
  1757     case MSG_OUTPUT_PENDING:
  1758         SOCKET_LOG(("  MSG_OUTPUT_PENDING\n"));
  1759         OnMsgOutputPending();
  1760         break;
  1761     case MSG_TIMEOUT_CHANGED:
  1762         SOCKET_LOG(("  MSG_TIMEOUT_CHANGED\n"));
  1763         mPollTimeout = mTimeouts[(mState == STATE_TRANSFERRING)
  1764           ? TIMEOUT_READ_WRITE : TIMEOUT_CONNECT];
  1765         break;
  1766     default:
  1767         SOCKET_LOG(("  unhandled event!\n"));
  1770     if (NS_FAILED(mCondition)) {
  1771         SOCKET_LOG(("  after event [this=%p cond=%x]\n", this, mCondition));
  1772         if (!mAttached) // need to process this error ourselves...
  1773             OnSocketDetached(nullptr);
  1775     else if (mPollFlags == PR_POLL_EXCEPT)
  1776         mPollFlags = 0; // make idle
  1779 //-----------------------------------------------------------------------------
  1780 // socket handler impl
  1782 void
  1783 nsSocketTransport::OnSocketReady(PRFileDesc *fd, int16_t outFlags)
  1785     SOCKET_LOG(("nsSocketTransport::OnSocketReady [this=%p outFlags=%hd]\n",
  1786         this, outFlags));
  1788     if (outFlags == -1) {
  1789         SOCKET_LOG(("socket timeout expired\n"));
  1790         mCondition = NS_ERROR_NET_TIMEOUT;
  1791         return;
  1794     if (mState == STATE_TRANSFERRING) {
  1795         // if waiting to write and socket is writable or hit an exception.
  1796         if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
  1797             // assume that we won't need to poll any longer (the stream will
  1798             // request that we poll again if it is still pending).
  1799             mPollFlags &= ~PR_POLL_WRITE;
  1800             mOutput.OnSocketReady(NS_OK);
  1802         // if waiting to read and socket is readable or hit an exception.
  1803         if ((mPollFlags & PR_POLL_READ) && (outFlags & ~PR_POLL_WRITE)) {
  1804             // assume that we won't need to poll any longer (the stream will
  1805             // request that we poll again if it is still pending).
  1806             mPollFlags &= ~PR_POLL_READ;
  1807             mInput.OnSocketReady(NS_OK);
  1809         // Update poll timeout in case it was changed
  1810         mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
  1813 //STATE_SENDINGGET: handshake proceeded to state "sent connect"
  1814 //one more poll to OnSocketReady will trigger the get request, and state STATE_SENTGET
  1815 //STATE_SENTGET: continue and finish handshake
  1816     else if (mState == STATE_SENDINGGET) {
  1817         if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
  1818             mOutput.OnSocketReady(NS_OK);
  1820         mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
  1821         mState = STATE_SENTGET;
  1824     else if (mState == STATE_CONNECTING || mState == STATE_SENTGET) {
  1825         PRStatus status = PR_ConnectContinue(fd, outFlags);
  1826         if (status == PR_SUCCESS && mState == STATE_CONNECTING) {
  1827             OnSocketConnected();
  1828             mState = STATE_SENDINGGET;
  1830         else if (status == PR_SUCCESS && mState == STATE_SENTGET) {
  1831             //
  1832             // we are connected!
  1833             //
  1834             OnSocketConnected();
  1836         else {
  1837             PRErrorCode code = PR_GetError();
  1838 #if defined(TEST_CONNECT_ERRORS)
  1839             code = RandomizeConnectError(code);
  1840 #endif
  1841             //
  1842             // If the connect is still not ready, then continue polling...
  1843             //
  1844             if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
  1845                 // Set up the select flags for connect...
  1846                 mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
  1847                 // Update poll timeout in case it was changed
  1848                 mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
  1850             //
  1851             // The SOCKS proxy rejected our request. Find out why.
  1852             //
  1853             else if (PR_UNKNOWN_ERROR == code &&
  1854                      mProxyUse && mProxyTransparent) {
  1855                 code = PR_GetOSError();
  1856                 mCondition = ErrorAccordingToNSPR(code);
  1858             else {
  1859                 //
  1860                 // else, the connection failed...
  1861                 //
  1862                 mCondition = ErrorAccordingToNSPR(code);
  1863                 if (mCondition == NS_ERROR_CONNECTION_REFUSED && mProxyUse)
  1864                     mCondition = NS_ERROR_PROXY_CONNECTION_REFUSED;
  1865                 SOCKET_LOG(("  connection failed! [reason=%x]\n", mCondition));
  1869     else {
  1870         NS_ERROR("unexpected socket state");
  1871         mCondition = NS_ERROR_UNEXPECTED;
  1874     if (mPollFlags == PR_POLL_EXCEPT)
  1875         mPollFlags = 0; // make idle
  1878 // called on the socket thread only
  1879 void
  1880 nsSocketTransport::OnSocketDetached(PRFileDesc *fd)
  1882     SOCKET_LOG(("nsSocketTransport::OnSocketDetached [this=%p cond=%x]\n",
  1883         this, mCondition));
  1885     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1887     // if we didn't initiate this detach, then be sure to pass an error
  1888     // condition up to our consumers.  (e.g., STS is shutting down.)
  1889     if (NS_SUCCEEDED(mCondition)) {
  1890         if (gIOService->IsOffline()) {
  1891           mCondition = NS_ERROR_OFFLINE;
  1893         else {
  1894           mCondition = NS_ERROR_ABORT;
  1898     if (RecoverFromError())
  1899         mCondition = NS_OK;
  1900     else {
  1901         mState = STATE_CLOSED;
  1903         // make sure there isn't any pending DNS request
  1904         if (mDNSRequest) {
  1905             mDNSRequest->Cancel(NS_ERROR_ABORT);
  1906             mDNSRequest = 0;
  1909         //
  1910         // notify input/output streams
  1911         //
  1912         mInput.OnSocketReady(mCondition);
  1913         mOutput.OnSocketReady(mCondition);
  1916     // break any potential reference cycle between the security info object
  1917     // and ourselves by resetting its notification callbacks object.  see
  1918     // bug 285991 for details.
  1919     nsCOMPtr<nsISSLSocketControl> secCtrl = do_QueryInterface(mSecInfo);
  1920     if (secCtrl)
  1921         secCtrl->SetNotificationCallbacks(nullptr);
  1923     // finally, release our reference to the socket (must do this within
  1924     // the transport lock) possibly closing the socket. Also release our
  1925     // listeners to break potential refcount cycles.
  1927     // We should be careful not to release mEventSink and mCallbacks while
  1928     // we're locked, because releasing it might require acquiring the lock
  1929     // again, so we just null out mEventSink and mCallbacks while we're
  1930     // holding the lock, and let the stack based objects' destuctors take
  1931     // care of destroying it if needed.
  1932     nsCOMPtr<nsIInterfaceRequestor> ourCallbacks;
  1933     nsCOMPtr<nsITransportEventSink> ourEventSink;
  1935         MutexAutoLock lock(mLock);
  1936         if (mFD.IsInitialized()) {
  1937             ReleaseFD_Locked(mFD);
  1938             // flag mFD as unusable; this prevents other consumers from 
  1939             // acquiring a reference to mFD.
  1940             mFDconnected = false;
  1943         // We must release mCallbacks and mEventSink to avoid memory leak
  1944         // but only when RecoverFromError() above failed. Otherwise we lose
  1945         // link with UI and security callbacks on next connection attempt 
  1946         // round. That would lead e.g. to a broken certificate exception page.
  1947         if (NS_FAILED(mCondition)) {
  1948             mCallbacks.swap(ourCallbacks);
  1949             mEventSink.swap(ourEventSink);
  1954 void
  1955 nsSocketTransport::IsLocal(bool *aIsLocal)
  1958         MutexAutoLock lock(mLock);
  1960 #if defined(XP_UNIX)
  1961         // Unix-domain sockets are always local.
  1962         if (mNetAddr.raw.family == PR_AF_LOCAL)
  1964             *aIsLocal = true;
  1965             return;
  1967 #endif
  1969         *aIsLocal = IsLoopBackAddress(&mNetAddr);
  1973 //-----------------------------------------------------------------------------
  1974 // xpcom api
  1976 NS_IMPL_ISUPPORTS(nsSocketTransport,
  1977                   nsISocketTransport,
  1978                   nsITransport,
  1979                   nsIDNSListener,
  1980                   nsIClassInfo)
  1981 NS_IMPL_CI_INTERFACE_GETTER(nsSocketTransport,
  1982                             nsISocketTransport,
  1983                             nsITransport,
  1984                             nsIDNSListener)
  1986 NS_IMETHODIMP
  1987 nsSocketTransport::OpenInputStream(uint32_t flags,
  1988                                    uint32_t segsize,
  1989                                    uint32_t segcount,
  1990                                    nsIInputStream **result)
  1992     SOCKET_LOG(("nsSocketTransport::OpenInputStream [this=%p flags=%x]\n",
  1993         this, flags));
  1995     NS_ENSURE_TRUE(!mInput.IsReferenced(), NS_ERROR_UNEXPECTED);
  1997     nsresult rv;
  1998     nsCOMPtr<nsIAsyncInputStream> pipeIn;
  2000     if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
  2001         // XXX if the caller wants blocking, then the caller also gets buffered!
  2002         //bool openBuffered = !(flags & OPEN_UNBUFFERED);
  2003         bool openBlocking =  (flags & OPEN_BLOCKING);
  2005         net_ResolveSegmentParams(segsize, segcount);
  2007         // create a pipe
  2008         nsCOMPtr<nsIAsyncOutputStream> pipeOut;
  2009         rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
  2010                          !openBlocking, true, segsize, segcount);
  2011         if (NS_FAILED(rv)) return rv;
  2013         // async copy from socket to pipe
  2014         rv = NS_AsyncCopy(&mInput, pipeOut, mSocketTransportService,
  2015                           NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
  2016         if (NS_FAILED(rv)) return rv;
  2018         *result = pipeIn;
  2020     else
  2021         *result = &mInput;
  2023     // flag input stream as open
  2024     mInputClosed = false;
  2026     rv = PostEvent(MSG_ENSURE_CONNECT);
  2027     if (NS_FAILED(rv)) return rv;
  2029     NS_ADDREF(*result);
  2030     return NS_OK;
  2033 NS_IMETHODIMP
  2034 nsSocketTransport::OpenOutputStream(uint32_t flags,
  2035                                     uint32_t segsize,
  2036                                     uint32_t segcount,
  2037                                     nsIOutputStream **result)
  2039     SOCKET_LOG(("nsSocketTransport::OpenOutputStream [this=%p flags=%x]\n",
  2040         this, flags));
  2042     NS_ENSURE_TRUE(!mOutput.IsReferenced(), NS_ERROR_UNEXPECTED);
  2044     nsresult rv;
  2045     nsCOMPtr<nsIAsyncOutputStream> pipeOut;
  2046     if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
  2047         // XXX if the caller wants blocking, then the caller also gets buffered!
  2048         //bool openBuffered = !(flags & OPEN_UNBUFFERED);
  2049         bool openBlocking =  (flags & OPEN_BLOCKING);
  2051         net_ResolveSegmentParams(segsize, segcount);
  2053         // create a pipe
  2054         nsCOMPtr<nsIAsyncInputStream> pipeIn;
  2055         rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
  2056                          true, !openBlocking, segsize, segcount);
  2057         if (NS_FAILED(rv)) return rv;
  2059         // async copy from socket to pipe
  2060         rv = NS_AsyncCopy(pipeIn, &mOutput, mSocketTransportService,
  2061                           NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
  2062         if (NS_FAILED(rv)) return rv;
  2064         *result = pipeOut;
  2066     else
  2067         *result = &mOutput;
  2069     // flag output stream as open
  2070     mOutputClosed = false;
  2072     rv = PostEvent(MSG_ENSURE_CONNECT);
  2073     if (NS_FAILED(rv)) return rv;
  2075     NS_ADDREF(*result);
  2076     return NS_OK;
  2079 NS_IMETHODIMP
  2080 nsSocketTransport::Close(nsresult reason)
  2082     if (NS_SUCCEEDED(reason))
  2083         reason = NS_BASE_STREAM_CLOSED;
  2085     mInput.CloseWithStatus(reason);
  2086     mOutput.CloseWithStatus(reason);
  2087     return NS_OK;
  2090 NS_IMETHODIMP
  2091 nsSocketTransport::GetSecurityInfo(nsISupports **secinfo)
  2093     MutexAutoLock lock(mLock);
  2094     NS_IF_ADDREF(*secinfo = mSecInfo);
  2095     return NS_OK;
  2098 NS_IMETHODIMP
  2099 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor **callbacks)
  2101     MutexAutoLock lock(mLock);
  2102     NS_IF_ADDREF(*callbacks = mCallbacks);
  2103     return NS_OK;
  2106 NS_IMETHODIMP
  2107 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor *callbacks)
  2109     nsCOMPtr<nsIInterfaceRequestor> threadsafeCallbacks;
  2110     NS_NewNotificationCallbacksAggregation(callbacks, nullptr,
  2111                                            NS_GetCurrentThread(),
  2112                                            getter_AddRefs(threadsafeCallbacks));
  2114     nsCOMPtr<nsISupports> secinfo;
  2116         MutexAutoLock lock(mLock);
  2117         mCallbacks = threadsafeCallbacks;
  2118         SOCKET_LOG(("Reset callbacks for secinfo=%p callbacks=%p\n",
  2119                     mSecInfo.get(), mCallbacks.get()));
  2121         secinfo = mSecInfo;
  2124     // don't call into PSM while holding mLock!!
  2125     nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
  2126     if (secCtrl)
  2127         secCtrl->SetNotificationCallbacks(threadsafeCallbacks);
  2129     return NS_OK;
  2132 NS_IMETHODIMP
  2133 nsSocketTransport::SetEventSink(nsITransportEventSink *sink,
  2134                                 nsIEventTarget *target)
  2136     nsCOMPtr<nsITransportEventSink> temp;
  2137     if (target) {
  2138         nsresult rv = net_NewTransportEventSinkProxy(getter_AddRefs(temp),
  2139                                                      sink, target);
  2140         if (NS_FAILED(rv))
  2141             return rv;
  2142         sink = temp.get();
  2145     MutexAutoLock lock(mLock);
  2146     mEventSink = sink;
  2147     return NS_OK;
  2150 NS_IMETHODIMP
  2151 nsSocketTransport::IsAlive(bool *result)
  2153     *result = false;
  2155     nsresult conditionWhileLocked = NS_OK;
  2156     PRFileDescAutoLock fd(this, &conditionWhileLocked);
  2157     if (NS_FAILED(conditionWhileLocked) || !fd.IsInitialized()) {
  2158         return NS_OK;
  2161     // XXX do some idle-time based checks??
  2163     char c;
  2164     int32_t rval = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
  2166     if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR))
  2167         *result = true;
  2169     return NS_OK;
  2172 NS_IMETHODIMP
  2173 nsSocketTransport::GetHost(nsACString &host)
  2175     host = SocketHost();
  2176     return NS_OK;
  2179 NS_IMETHODIMP
  2180 nsSocketTransport::GetPort(int32_t *port)
  2182     *port = (int32_t) SocketPort();
  2183     return NS_OK;
  2186 const nsCString &
  2187 nsSocketTransport::SocketHost()
  2189   if (mProxyInfo && !mProxyTransparent) {
  2190     if (mProxyHostCache.IsEmpty()) {
  2191       mProxyInfo->GetHost(mProxyHostCache);
  2193     return mProxyHostCache;
  2195   else
  2196     return mHost;
  2199 uint16_t
  2200 nsSocketTransport::SocketPort()
  2202   if (mProxyInfo && !mProxyTransparent) {
  2203     int32_t result;
  2204     mProxyInfo->GetPort(&result);
  2205     return (uint16_t) result;
  2207   else
  2208     return mPort;
  2211 NS_IMETHODIMP
  2212 nsSocketTransport::GetPeerAddr(NetAddr *addr)
  2214     // once we are in the connected state, mNetAddr will not change.
  2215     // so if we can verify that we are in the connected state, then
  2216     // we can freely access mNetAddr from any thread without being
  2217     // inside a critical section.
  2219     if (!mNetAddrIsSet) {
  2220         SOCKET_LOG(("nsSocketTransport::GetPeerAddr [this=%p state=%d] "
  2221                     "NOT_AVAILABLE because not yet connected.", this, mState));
  2222         return NS_ERROR_NOT_AVAILABLE;
  2225     memcpy(addr, &mNetAddr, sizeof(NetAddr));
  2226     return NS_OK;
  2229 NS_IMETHODIMP
  2230 nsSocketTransport::GetSelfAddr(NetAddr *addr)
  2232     // we must not call any PR methods on our file descriptor
  2233     // while holding mLock since those methods might re-enter
  2234     // socket transport code.
  2236     PRFileDescAutoLock fd(this);
  2237     if (!fd.IsInitialized()) {
  2238         return NS_ERROR_NOT_CONNECTED;
  2241     PRNetAddr prAddr;
  2243     // NSPR doesn't tell us the socket address's length (as provided by
  2244     // the 'getsockname' system call), so we can't distinguish between
  2245     // named, unnamed, and abstract Unix domain socket names. (Server
  2246     // sockets are never unnamed, obviously, but client sockets can use
  2247     // any kind of address.) Clear prAddr first, so that the path for
  2248     // unnamed and abstract addresses will at least be reliably empty,
  2249     // and not garbage for unnamed sockets.
  2250     memset(&prAddr, 0, sizeof(prAddr));
  2252     nsresult rv =
  2253         (PR_GetSockName(fd, &prAddr) == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
  2254     PRNetAddrToNetAddr(&prAddr, addr);
  2256     return rv;
  2259 /* nsINetAddr getScriptablePeerAddr (); */
  2260 NS_IMETHODIMP
  2261 nsSocketTransport::GetScriptablePeerAddr(nsINetAddr * *addr)
  2263     NetAddr rawAddr;
  2265     nsresult rv;
  2266     rv = GetPeerAddr(&rawAddr);
  2267     if (NS_FAILED(rv))
  2268         return rv;
  2270     NS_ADDREF(*addr = new nsNetAddr(&rawAddr));
  2272     return NS_OK;
  2275 /* nsINetAddr getScriptableSelfAddr (); */
  2276 NS_IMETHODIMP
  2277 nsSocketTransport::GetScriptableSelfAddr(nsINetAddr * *addr)
  2279     NetAddr rawAddr;
  2281     nsresult rv;
  2282     rv = GetSelfAddr(&rawAddr);
  2283     if (NS_FAILED(rv))
  2284         return rv;
  2286     NS_ADDREF(*addr = new nsNetAddr(&rawAddr));
  2288     return NS_OK;
  2291 NS_IMETHODIMP
  2292 nsSocketTransport::GetTimeout(uint32_t type, uint32_t *value)
  2294     NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
  2295     *value = (uint32_t) mTimeouts[type];
  2296     return NS_OK;
  2299 NS_IMETHODIMP
  2300 nsSocketTransport::SetTimeout(uint32_t type, uint32_t value)
  2302     NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
  2303     // truncate overly large timeout values.
  2304     mTimeouts[type] = (uint16_t) std::min<uint32_t>(value, UINT16_MAX);
  2305     PostEvent(MSG_TIMEOUT_CHANGED);
  2306     return NS_OK;
  2309 NS_IMETHODIMP
  2310 nsSocketTransport::SetQoSBits(uint8_t aQoSBits)
  2312     // Don't do any checking here of bits.  Why?  Because as of RFC-4594
  2313     // several different Class Selector and Assured Forwarding values
  2314     // have been defined, but that isn't to say more won't be added later.
  2315     // In that case, any checking would be an impediment to interoperating
  2316     // with newer QoS definitions.
  2318     mQoSBits = aQoSBits;
  2319     return NS_OK;
  2322 NS_IMETHODIMP
  2323 nsSocketTransport::GetQoSBits(uint8_t *aQoSBits)
  2325     *aQoSBits = mQoSBits;
  2326     return NS_OK;
  2329 NS_IMETHODIMP
  2330 nsSocketTransport::GetRecvBufferSize(uint32_t *aSize)
  2332     PRFileDescAutoLock fd(this);
  2333     if (!fd.IsInitialized())
  2334         return NS_ERROR_NOT_CONNECTED;
  2336     nsresult rv = NS_OK;
  2337     PRSocketOptionData opt;
  2338     opt.option = PR_SockOpt_RecvBufferSize;
  2339     if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS)
  2340         *aSize = opt.value.recv_buffer_size;
  2341     else
  2342         rv = NS_ERROR_FAILURE;
  2344     return rv;
  2347 NS_IMETHODIMP
  2348 nsSocketTransport::GetSendBufferSize(uint32_t *aSize)
  2350     PRFileDescAutoLock fd(this);
  2351     if (!fd.IsInitialized())
  2352         return NS_ERROR_NOT_CONNECTED;
  2354     nsresult rv = NS_OK;
  2355     PRSocketOptionData opt;
  2356     opt.option = PR_SockOpt_SendBufferSize;
  2357     if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS)
  2358         *aSize = opt.value.send_buffer_size;
  2359     else
  2360         rv = NS_ERROR_FAILURE;
  2362     return rv;
  2365 NS_IMETHODIMP
  2366 nsSocketTransport::SetRecvBufferSize(uint32_t aSize)
  2368     PRFileDescAutoLock fd(this);
  2369     if (!fd.IsInitialized())
  2370         return NS_ERROR_NOT_CONNECTED;
  2372     nsresult rv = NS_OK;
  2373     PRSocketOptionData opt;
  2374     opt.option = PR_SockOpt_RecvBufferSize;
  2375     opt.value.recv_buffer_size = aSize;
  2376     if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS)
  2377         rv = NS_ERROR_FAILURE;
  2379     return rv;
  2382 NS_IMETHODIMP
  2383 nsSocketTransport::SetSendBufferSize(uint32_t aSize)
  2385     PRFileDescAutoLock fd(this);
  2386     if (!fd.IsInitialized())
  2387         return NS_ERROR_NOT_CONNECTED;
  2389     nsresult rv = NS_OK;
  2390     PRSocketOptionData opt;
  2391     opt.option = PR_SockOpt_SendBufferSize;
  2392     opt.value.send_buffer_size = aSize;
  2393     if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS)
  2394         rv = NS_ERROR_FAILURE;
  2396     return rv;
  2399 NS_IMETHODIMP
  2400 nsSocketTransport::OnLookupComplete(nsICancelable *request,
  2401                                     nsIDNSRecord  *rec,
  2402                                     nsresult       status)
  2404     // flag host lookup complete for the benefit of the ResolveHost method.
  2405     mResolving = false;
  2407     MOZ_EVENT_TRACER_WAIT(this, "net::tcp::connect");
  2408     nsresult rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, status, rec);
  2410     // if posting a message fails, then we should assume that the socket
  2411     // transport has been shutdown.  this should never happen!  if it does
  2412     // it means that the socket transport service was shutdown before the
  2413     // DNS service.
  2414     if (NS_FAILED(rv))
  2415         NS_WARNING("unable to post DNS lookup complete message");
  2417     return NS_OK;
  2420 NS_IMETHODIMP
  2421 nsSocketTransport::GetInterfaces(uint32_t *count, nsIID * **array)
  2423     return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(count, array);
  2426 NS_IMETHODIMP
  2427 nsSocketTransport::GetHelperForLanguage(uint32_t language, nsISupports **_retval)
  2429     *_retval = nullptr;
  2430     return NS_OK;
  2433 NS_IMETHODIMP
  2434 nsSocketTransport::GetContractID(char * *aContractID)
  2436     *aContractID = nullptr;
  2437     return NS_OK;
  2440 NS_IMETHODIMP
  2441 nsSocketTransport::GetClassDescription(char * *aClassDescription)
  2443     *aClassDescription = nullptr;
  2444     return NS_OK;
  2447 NS_IMETHODIMP
  2448 nsSocketTransport::GetClassID(nsCID * *aClassID)
  2450     *aClassID = nullptr;
  2451     return NS_OK;
  2454 NS_IMETHODIMP
  2455 nsSocketTransport::GetImplementationLanguage(uint32_t *aImplementationLanguage)
  2457     *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
  2458     return NS_OK;
  2461 NS_IMETHODIMP
  2462 nsSocketTransport::GetFlags(uint32_t *aFlags)
  2464     *aFlags = nsIClassInfo::THREADSAFE;
  2465     return NS_OK;
  2468 NS_IMETHODIMP
  2469 nsSocketTransport::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
  2471     return NS_ERROR_NOT_AVAILABLE;
  2475 NS_IMETHODIMP
  2476 nsSocketTransport::GetConnectionFlags(uint32_t *value)
  2478     *value = mConnectionFlags;
  2479     return NS_OK;
  2482 NS_IMETHODIMP
  2483 nsSocketTransport::SetConnectionFlags(uint32_t value)
  2485     mConnectionFlags = value;
  2486     mIsPrivate = value & nsISocketTransport::NO_PERMANENT_STORAGE;
  2487     return NS_OK;
  2490 void
  2491 nsSocketTransport::OnKeepaliveEnabledPrefChange(bool aEnabled)
  2493     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  2495     // The global pref toggles keepalive as a system feature; it only affects
  2496     // an individual socket if keepalive has been specifically enabled for it.
  2497     // So, ensure keepalive is configured correctly if previously enabled.
  2498     if (mKeepaliveEnabled) {
  2499         nsresult rv = SetKeepaliveEnabledInternal(aEnabled);
  2500         if (NS_WARN_IF(NS_FAILED(rv))) {
  2501             SOCKET_LOG(("  SetKeepaliveEnabledInternal [%s] failed rv[0x%x]",
  2502                         aEnabled ? "enable" : "disable", rv));
  2507 nsresult
  2508 nsSocketTransport::SetKeepaliveEnabledInternal(bool aEnable)
  2510     MOZ_ASSERT(mKeepaliveIdleTimeS > 0 &&
  2511                mKeepaliveIdleTimeS <= kMaxTCPKeepIdle);
  2512     MOZ_ASSERT(mKeepaliveRetryIntervalS > 0 &&
  2513                mKeepaliveRetryIntervalS <= kMaxTCPKeepIntvl);
  2514     MOZ_ASSERT(mKeepaliveProbeCount > 0 &&
  2515                mKeepaliveProbeCount <= kMaxTCPKeepCount);
  2517     PRFileDescAutoLock fd(this);
  2518     if (NS_WARN_IF(!fd.IsInitialized())) {
  2519         return NS_ERROR_NOT_INITIALIZED;
  2522     // Only enable if keepalives are globally enabled, but ensure other
  2523     // options are set correctly on the fd.
  2524     bool enable = aEnable && mSocketTransportService->IsKeepaliveEnabled();
  2525     nsresult rv = fd.SetKeepaliveVals(enable,
  2526                                       mKeepaliveIdleTimeS,
  2527                                       mKeepaliveRetryIntervalS,
  2528                                       mKeepaliveProbeCount);
  2529     if (NS_WARN_IF(NS_FAILED(rv))) {
  2530         SOCKET_LOG(("  SetKeepaliveVals failed rv[0x%x]", rv));
  2531         return rv;
  2533     rv = fd.SetKeepaliveEnabled(enable);
  2534     if (NS_WARN_IF(NS_FAILED(rv))) {
  2535         SOCKET_LOG(("  SetKeepaliveEnabled failed rv[0x%x]", rv));
  2536         return rv;
  2538     return NS_OK;
  2541 NS_IMETHODIMP
  2542 nsSocketTransport::GetKeepaliveEnabled(bool *aResult)
  2544     MOZ_ASSERT(aResult);
  2546     *aResult = mKeepaliveEnabled;
  2547     return NS_OK;
  2550 nsresult
  2551 nsSocketTransport::EnsureKeepaliveValsAreInitialized()
  2553     nsresult rv = NS_OK;
  2554     int32_t val = -1;
  2555     if (mKeepaliveIdleTimeS == -1) {
  2556         rv = mSocketTransportService->GetKeepaliveIdleTime(&val);
  2557         if (NS_WARN_IF(NS_FAILED(rv))) {
  2558             return rv;
  2560         mKeepaliveIdleTimeS = val;
  2562     if (mKeepaliveRetryIntervalS == -1) {
  2563         rv = mSocketTransportService->GetKeepaliveRetryInterval(&val);
  2564         if (NS_WARN_IF(NS_FAILED(rv))) {
  2565             return rv;
  2567         mKeepaliveRetryIntervalS = val;
  2569     if (mKeepaliveProbeCount == -1) {
  2570         rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
  2571         if (NS_WARN_IF(NS_FAILED(rv))) {
  2572             return rv;
  2574         mKeepaliveProbeCount = val;
  2576     return NS_OK;
  2579 NS_IMETHODIMP
  2580 nsSocketTransport::SetKeepaliveEnabled(bool aEnable)
  2582 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
  2583     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  2585     if (aEnable == mKeepaliveEnabled) {
  2586         SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] already %s.",
  2587                     this, aEnable ? "enabled" : "disabled"));
  2588         return NS_OK;
  2591     nsresult rv = NS_OK;
  2592     if (aEnable) {
  2593         rv = EnsureKeepaliveValsAreInitialized();
  2594         if (NS_WARN_IF(NS_FAILED(rv))) {
  2595             SOCKET_LOG(("  SetKeepaliveEnabled [%p] "
  2596                         "error [0x%x] initializing keepalive vals",
  2597                         this, rv));
  2598             return rv;
  2601     SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] "
  2602                 "%s, idle time[%ds] retry interval[%ds] packet count[%d]: "
  2603                 "globally %s.",
  2604                 this, aEnable ? "enabled" : "disabled",
  2605                 mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS,
  2606                 mKeepaliveProbeCount,
  2607                 mSocketTransportService->IsKeepaliveEnabled() ?
  2608                 "enabled" : "disabled"));
  2610     // Set mKeepaliveEnabled here so that state is maintained; it is possible
  2611     // that we're in between fds, e.g. the 1st IP address failed, so we're about
  2612     // to retry on a 2nd from the DNS record.
  2613     mKeepaliveEnabled = aEnable;
  2615     rv = SetKeepaliveEnabledInternal(aEnable);
  2616     if (NS_WARN_IF(NS_FAILED(rv))) {
  2617         SOCKET_LOG(("  SetKeepaliveEnabledInternal failed rv[0x%x]", rv));
  2618         return rv;
  2621     return NS_OK;
  2622 #else /* !(defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)) */
  2623     SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled unsupported platform"));
  2624     return NS_ERROR_NOT_IMPLEMENTED;
  2625 #endif
  2628 NS_IMETHODIMP
  2629 nsSocketTransport::SetKeepaliveVals(int32_t aIdleTime,
  2630                                     int32_t aRetryInterval)
  2632 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
  2633     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  2634     if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
  2635         return NS_ERROR_INVALID_ARG;
  2637     if (NS_WARN_IF(aRetryInterval <= 0 ||
  2638                    kMaxTCPKeepIntvl < aRetryInterval)) {
  2639         return NS_ERROR_INVALID_ARG;
  2642     if (aIdleTime == mKeepaliveIdleTimeS &&
  2643         aRetryInterval == mKeepaliveRetryIntervalS) {
  2644         SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals [%p] idle time "
  2645                     "already %ds and retry interval already %ds.",
  2646                     this, mKeepaliveIdleTimeS,
  2647                     mKeepaliveRetryIntervalS));
  2648         return NS_OK;
  2650     mKeepaliveIdleTimeS = aIdleTime;
  2651     mKeepaliveRetryIntervalS = aRetryInterval;
  2653     nsresult rv = NS_OK;
  2654     if (mKeepaliveProbeCount == -1) {
  2655         int32_t val = -1;
  2656         nsresult rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
  2657         if (NS_WARN_IF(NS_FAILED(rv))) {
  2658             return rv;
  2660         mKeepaliveProbeCount = val;
  2663     SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals [%p] "
  2664                 "keepalive %s, idle time[%ds] retry interval[%ds] "
  2665                 "packet count[%d]",
  2666                 this, mKeepaliveEnabled ? "enabled" : "disabled",
  2667                 mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS,
  2668                 mKeepaliveProbeCount));
  2670     PRFileDescAutoLock fd(this);
  2671     if (NS_WARN_IF(!fd.IsInitialized())) {
  2672         return NS_ERROR_NULL_POINTER;
  2675     rv = fd.SetKeepaliveVals(mKeepaliveEnabled,
  2676                              mKeepaliveIdleTimeS,
  2677                              mKeepaliveRetryIntervalS,
  2678                              mKeepaliveProbeCount);
  2679     if (NS_WARN_IF(NS_FAILED(rv))) {
  2680         return rv;
  2682     return NS_OK;
  2683 #else
  2684     SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals unsupported platform"));
  2685     return NS_ERROR_NOT_IMPLEMENTED;
  2686 #endif
  2689 #ifdef ENABLE_SOCKET_TRACING
  2691 #include <stdio.h>
  2692 #include <ctype.h>
  2693 #include "prenv.h"
  2695 static void
  2696 DumpBytesToFile(const char *path, const char *header, const char *buf, int32_t n)
  2698     FILE *fp = fopen(path, "a");
  2700     fprintf(fp, "\n%s [%d bytes]\n", header, n);
  2702     const unsigned char *p;
  2703     while (n) {
  2704         p = (const unsigned char *) buf;
  2706         int32_t i, row_max = std::min(16, n);
  2708         for (i = 0; i < row_max; ++i)
  2709             fprintf(fp, "%02x  ", *p++);
  2710         for (i = row_max; i < 16; ++i)
  2711             fprintf(fp, "    ");
  2713         p = (const unsigned char *) buf;
  2714         for (i = 0; i < row_max; ++i, ++p) {
  2715             if (isprint(*p))
  2716                 fprintf(fp, "%c", *p);
  2717             else
  2718                 fprintf(fp, ".");
  2721         fprintf(fp, "\n");
  2722         buf += row_max;
  2723         n -= row_max;
  2726     fprintf(fp, "\n");
  2727     fclose(fp);
  2730 void
  2731 nsSocketTransport::TraceInBuf(const char *buf, int32_t n)
  2733     char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
  2734     if (!val || !*val)
  2735         return;
  2737     nsAutoCString header;
  2738     header.Assign(NS_LITERAL_CSTRING("Reading from: ") + mHost);
  2739     header.Append(':');
  2740     header.AppendInt(mPort);
  2742     DumpBytesToFile(val, header.get(), buf, n);
  2745 void
  2746 nsSocketTransport::TraceOutBuf(const char *buf, int32_t n)
  2748     char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
  2749     if (!val || !*val)
  2750         return;
  2752     nsAutoCString header;
  2753     header.Assign(NS_LITERAL_CSTRING("Writing to: ") + mHost);
  2754     header.Append(':');
  2755     header.AppendInt(mPort);
  2757     DumpBytesToFile(val, header.get(), buf, n);
  2760 #endif
  2762 static void LogNSPRError(const char* aPrefix, const void *aObjPtr)
  2764 #if defined(PR_LOGGING) && defined(DEBUG)
  2765     PRErrorCode errCode = PR_GetError();
  2766     int errLen = PR_GetErrorTextLength();
  2767     nsAutoCString errStr;
  2768     if (errLen > 0) {
  2769         errStr.SetLength(errLen);
  2770         PR_GetErrorText(errStr.BeginWriting());
  2772     NS_WARNING(nsPrintfCString(
  2773                "%s [%p] NSPR error[0x%x] %s.",
  2774                aPrefix ? aPrefix : "nsSocketTransport", aObjPtr, errCode,
  2775                errLen > 0 ? errStr.BeginReading() : "<no error text>").get());
  2776 #endif
  2779 nsresult
  2780 nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled(bool aEnable)
  2782     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  2783     MOZ_ASSERT(!(aEnable && !gSocketTransportService->IsKeepaliveEnabled()),
  2784                "Cannot enable keepalive if global pref is disabled!");
  2785     if (aEnable && !gSocketTransportService->IsKeepaliveEnabled()) {
  2786         return NS_ERROR_ILLEGAL_VALUE;
  2789     PRSocketOptionData opt;
  2791     opt.option = PR_SockOpt_Keepalive;
  2792     opt.value.keep_alive = aEnable;
  2793     PRStatus status = PR_SetSocketOption(mFd, &opt);
  2794     if (NS_WARN_IF(status != PR_SUCCESS)) {
  2795         LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled",
  2796                      mSocketTransport);
  2797         return ErrorAccordingToNSPR(PR_GetError());
  2799     return NS_OK;
  2802 static void LogOSError(const char *aPrefix, const void *aObjPtr)
  2804 #if defined(PR_LOGGING) && defined(DEBUG)
  2805     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  2807 #ifdef XP_WIN
  2808     DWORD errCode = WSAGetLastError();
  2809     LPVOID errMessage;
  2810     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  2811                   FORMAT_MESSAGE_FROM_SYSTEM |
  2812                   FORMAT_MESSAGE_IGNORE_INSERTS,
  2813                   NULL,
  2814                   errCode,
  2815                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  2816                   (LPTSTR) &errMessage,
  2817                   0, NULL);
  2818 #else
  2819     int errCode = errno;
  2820     char *errMessage = strerror(errno);
  2821 #endif
  2822     NS_WARNING(nsPrintfCString(
  2823                "%s [%p] OS error[0x%x] %s",
  2824                aPrefix ? aPrefix : "nsSocketTransport", aObjPtr, errCode,
  2825                errMessage ? errMessage : "<no error text>").get());
  2826 #ifdef XP_WIN
  2827     LocalFree(errMessage);
  2828 #endif
  2829 #endif
  2832 /* XXX PR_SetSockOpt does not support setting keepalive values, so native
  2833  * handles and platform specific apis (setsockopt, WSAIOCtl) are used in this
  2834  * file. Requires inclusion of NSPR private/pprio.h, and platform headers.
  2835  */
  2837 nsresult
  2838 nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(bool aEnabled,
  2839                                                         int aIdleTime,
  2840                                                         int aRetryInterval,
  2841                                                         int aProbeCount)
  2843 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
  2844     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  2845     if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
  2846         return NS_ERROR_INVALID_ARG;
  2848     if (NS_WARN_IF(aRetryInterval <= 0 ||
  2849                    kMaxTCPKeepIntvl < aRetryInterval)) {
  2850         return NS_ERROR_INVALID_ARG;
  2852     if (NS_WARN_IF(aProbeCount <= 0 || kMaxTCPKeepCount < aProbeCount)) {
  2853         return NS_ERROR_INVALID_ARG;
  2856     PROsfd sock = PR_FileDesc2NativeHandle(mFd);
  2857     if (NS_WARN_IF(sock == -1)) {
  2858         LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals",
  2859                      mSocketTransport);
  2860         return ErrorAccordingToNSPR(PR_GetError());
  2862 #endif
  2864 #if defined(XP_WIN)
  2865     // Windows allows idle time and retry interval to be set; NOT ping count.
  2866     struct tcp_keepalive keepalive_vals = {
  2867         (int)aEnabled,
  2868         // Windows uses msec.
  2869         aIdleTime * 1000,
  2870         aRetryInterval * 1000
  2871     };
  2872     DWORD bytes_returned;
  2873     int err = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &keepalive_vals,
  2874                        sizeof(keepalive_vals), NULL, 0, &bytes_returned, NULL,
  2875                        NULL);
  2876     if (NS_WARN_IF(err)) {
  2877         LogOSError("nsSocketTransport WSAIoctl failed", mSocketTransport);
  2878         return NS_ERROR_UNEXPECTED;
  2880     return NS_OK;
  2882 #elif defined(XP_MACOSX)
  2883     // OS X uses sec; only supports idle time being set.
  2884     int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE,
  2885                          &aIdleTime, sizeof(aIdleTime));
  2886     if (NS_WARN_IF(err)) {
  2887         LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
  2888                    mSocketTransport);
  2889         return NS_ERROR_UNEXPECTED;
  2891     return NS_OK;
  2893 #elif defined(XP_UNIX)
  2894     // Not all *nix OSes support the following setsockopt() options
  2895     // ... but we assume they are supported in the Android kernel;
  2896     // build errors will tell us if they are not.
  2897 #if defined(ANDROID) || defined(TCP_KEEPIDLE)
  2898     // Idle time until first keepalive probe; interval between ack'd probes; seconds.
  2899     int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE,
  2900                          &aIdleTime, sizeof(aIdleTime));
  2901     if (NS_WARN_IF(err)) {
  2902         LogOSError("nsSocketTransport Failed setting TCP_KEEPIDLE",
  2903                    mSocketTransport);
  2904         return NS_ERROR_UNEXPECTED;
  2907 #endif
  2908 #if defined(ANDROID) || defined(TCP_KEEPINTVL)
  2909     // Interval between unack'd keepalive probes; seconds.
  2910     err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL,
  2911                         &aRetryInterval, sizeof(aRetryInterval));
  2912     if (NS_WARN_IF(err)) {
  2913         LogOSError("nsSocketTransport Failed setting TCP_KEEPINTVL",
  2914                    mSocketTransport);
  2915         return NS_ERROR_UNEXPECTED;
  2918 #endif
  2919 #if defined(ANDROID) || defined(TCP_KEEPCNT)
  2920     // Number of unack'd keepalive probes before connection times out.
  2921     err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT,
  2922                      &aProbeCount, sizeof(aProbeCount));
  2923     if (NS_WARN_IF(err)) {
  2924         LogOSError("nsSocketTransport Failed setting TCP_KEEPCNT",
  2925                    mSocketTransport);
  2926         return NS_ERROR_UNEXPECTED;
  2929 #endif
  2930     return NS_OK;
  2931 #else
  2932     MOZ_ASSERT(false, "nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals "
  2933                "called on unsupported platform!");
  2934     return NS_ERROR_UNEXPECTED;
  2935 #endif

mercurial