netwerk/base/src/nsSocketTransport2.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/base/src/nsSocketTransport2.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2936 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim:set ts=4 sw=4 et cindent: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#ifdef MOZ_LOGGING
    1.11 +#define FORCE_PR_LOG
    1.12 +#endif
    1.13 +
    1.14 +#include "nsSocketTransport2.h"
    1.15 +
    1.16 +#include "mozilla/Attributes.h"
    1.17 +#include "nsIOService.h"
    1.18 +#include "nsStreamUtils.h"
    1.19 +#include "nsNetSegmentUtils.h"
    1.20 +#include "nsNetAddr.h"
    1.21 +#include "nsTransportUtils.h"
    1.22 +#include "nsProxyInfo.h"
    1.23 +#include "nsNetCID.h"
    1.24 +#include "nsNetUtil.h"
    1.25 +#include "nsAutoPtr.h"
    1.26 +#include "nsCOMPtr.h"
    1.27 +#include "plstr.h"
    1.28 +#include "prerr.h"
    1.29 +#include "NetworkActivityMonitor.h"
    1.30 +#include "mozilla/VisualEventTracer.h"
    1.31 +#include "nsThreadUtils.h"
    1.32 +#include "nsISocketProviderService.h"
    1.33 +#include "nsISocketProvider.h"
    1.34 +#include "nsISSLSocketControl.h"
    1.35 +#include "nsINSSErrorsService.h"
    1.36 +#include "nsIPipe.h"
    1.37 +#include "nsIProgrammingLanguage.h"
    1.38 +#include "nsIClassInfoImpl.h"
    1.39 +#include "nsURLHelper.h"
    1.40 +#include "nsIDNSService.h"
    1.41 +#include "nsIDNSRecord.h"
    1.42 +#include "nsICancelable.h"
    1.43 +#include <algorithm>
    1.44 +
    1.45 +#include "nsPrintfCString.h"
    1.46 +
    1.47 +#if defined(XP_WIN)
    1.48 +#include "nsNativeConnectionHelper.h"
    1.49 +#endif
    1.50 +
    1.51 +/* Following inclusions required for keepalive config not supported by NSPR. */
    1.52 +#include "private/pprio.h"
    1.53 +#if defined(XP_WIN)
    1.54 +#include <winsock2.h>
    1.55 +#include <mstcpip.h>
    1.56 +#elif defined(XP_UNIX)
    1.57 +#include <errno.h>
    1.58 +#include <netinet/tcp.h>
    1.59 +#endif
    1.60 +/* End keepalive config inclusions. */
    1.61 +
    1.62 +using namespace mozilla;
    1.63 +using namespace mozilla::net;
    1.64 +
    1.65 +//-----------------------------------------------------------------------------
    1.66 +
    1.67 +static NS_DEFINE_CID(kSocketProviderServiceCID, NS_SOCKETPROVIDERSERVICE_CID);
    1.68 +static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
    1.69 +
    1.70 +//-----------------------------------------------------------------------------
    1.71 +
    1.72 +class nsSocketEvent : public nsRunnable
    1.73 +{
    1.74 +public:
    1.75 +    nsSocketEvent(nsSocketTransport *transport, uint32_t type,
    1.76 +                  nsresult status = NS_OK, nsISupports *param = nullptr)
    1.77 +        : mTransport(transport)
    1.78 +        , mType(type)
    1.79 +        , mStatus(status)
    1.80 +        , mParam(param)
    1.81 +    {}
    1.82 +
    1.83 +    NS_IMETHOD Run()
    1.84 +    {
    1.85 +        mTransport->OnSocketEvent(mType, mStatus, mParam);
    1.86 +        return NS_OK;
    1.87 +    }
    1.88 +
    1.89 +private:
    1.90 +    nsRefPtr<nsSocketTransport> mTransport;
    1.91 +
    1.92 +    uint32_t              mType;
    1.93 +    nsresult              mStatus;
    1.94 +    nsCOMPtr<nsISupports> mParam;
    1.95 +};
    1.96 +
    1.97 +//-----------------------------------------------------------------------------
    1.98 +
    1.99 +//#define TEST_CONNECT_ERRORS
   1.100 +#ifdef TEST_CONNECT_ERRORS
   1.101 +#include <stdlib.h>
   1.102 +static PRErrorCode RandomizeConnectError(PRErrorCode code)
   1.103 +{
   1.104 +    //
   1.105 +    // To test out these errors, load http://www.yahoo.com/.  It should load
   1.106 +    // correctly despite the random occurrence of these errors.
   1.107 +    //
   1.108 +    int n = rand();
   1.109 +    if (n > RAND_MAX/2) {
   1.110 +        struct {
   1.111 +            PRErrorCode err_code;
   1.112 +            const char *err_name;
   1.113 +        } 
   1.114 +        errors[] = {
   1.115 +            //
   1.116 +            // These errors should be recoverable provided there is another
   1.117 +            // IP address in mDNSRecord.
   1.118 +            //
   1.119 +            { PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR" },
   1.120 +            { PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR" },
   1.121 +            //
   1.122 +            // This error will cause this socket transport to error out;
   1.123 +            // however, if the consumer is HTTP, then the HTTP transaction
   1.124 +            // should be restarted when this error occurs.
   1.125 +            //
   1.126 +            { PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR" },
   1.127 +        };
   1.128 +        n = n % (sizeof(errors)/sizeof(errors[0]));
   1.129 +        code = errors[n].err_code;
   1.130 +        SOCKET_LOG(("simulating NSPR error %d [%s]\n", code, errors[n].err_name));
   1.131 +    }
   1.132 +    return code;
   1.133 +}
   1.134 +#endif
   1.135 +
   1.136 +//-----------------------------------------------------------------------------
   1.137 +
   1.138 +static bool
   1.139 +IsNSSErrorCode(PRErrorCode code)
   1.140 +{
   1.141 +  return 
   1.142 +    ((code >= nsINSSErrorsService::NSS_SEC_ERROR_BASE) && 
   1.143 +      (code < nsINSSErrorsService::NSS_SEC_ERROR_LIMIT))
   1.144 +    ||
   1.145 +    ((code >= nsINSSErrorsService::NSS_SSL_ERROR_BASE) && 
   1.146 +      (code < nsINSSErrorsService::NSS_SSL_ERROR_LIMIT));
   1.147 +}
   1.148 +
   1.149 +// this logic is duplicated from the implementation of
   1.150 +// nsINSSErrorsService::getXPCOMFromNSSError
   1.151 +// It might have been better to implement that interface here...
   1.152 +static nsresult
   1.153 +GetXPCOMFromNSSError(PRErrorCode code)
   1.154 +{
   1.155 +    // XXX Don't make up nsresults, it's supposed to be an enum (bug 778113)
   1.156 +    return (nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
   1.157 +                                               -1 * code);
   1.158 +}
   1.159 +
   1.160 +nsresult
   1.161 +ErrorAccordingToNSPR(PRErrorCode errorCode)
   1.162 +{
   1.163 +    nsresult rv = NS_ERROR_FAILURE;
   1.164 +    switch (errorCode) {
   1.165 +    case PR_WOULD_BLOCK_ERROR:
   1.166 +        rv = NS_BASE_STREAM_WOULD_BLOCK;
   1.167 +        break;
   1.168 +    case PR_CONNECT_ABORTED_ERROR:
   1.169 +    case PR_CONNECT_RESET_ERROR:
   1.170 +        rv = NS_ERROR_NET_RESET;
   1.171 +        break;
   1.172 +    case PR_END_OF_FILE_ERROR: // XXX document this correlation
   1.173 +        rv = NS_ERROR_NET_INTERRUPT;
   1.174 +        break;
   1.175 +    case PR_CONNECT_REFUSED_ERROR:
   1.176 +    // We lump the following NSPR codes in with PR_CONNECT_REFUSED_ERROR. We
   1.177 +    // could get better diagnostics by adding distinct XPCOM error codes for
   1.178 +    // each of these, but there are a lot of places in Gecko that check
   1.179 +    // specifically for NS_ERROR_CONNECTION_REFUSED, all of which would need to
   1.180 +    // be checked.
   1.181 +    case PR_NETWORK_UNREACHABLE_ERROR:
   1.182 +    case PR_HOST_UNREACHABLE_ERROR:
   1.183 +    case PR_ADDRESS_NOT_AVAILABLE_ERROR:
   1.184 +    // Treat EACCES as a soft error since (at least on Linux) connect() returns
   1.185 +    // EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
   1.186 +    case PR_NO_ACCESS_RIGHTS_ERROR:
   1.187 +        rv = NS_ERROR_CONNECTION_REFUSED;
   1.188 +        break;
   1.189 +    case PR_ADDRESS_NOT_SUPPORTED_ERROR:
   1.190 +        rv = NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
   1.191 +        break;
   1.192 +    case PR_IO_TIMEOUT_ERROR:
   1.193 +    case PR_CONNECT_TIMEOUT_ERROR:
   1.194 +        rv = NS_ERROR_NET_TIMEOUT;
   1.195 +        break;
   1.196 +    case PR_OUT_OF_MEMORY_ERROR:
   1.197 +    // These really indicate that the descriptor table filled up, or that the
   1.198 +    // kernel ran out of network buffers - but nobody really cares which part of
   1.199 +    // the system ran out of memory.
   1.200 +    case PR_PROC_DESC_TABLE_FULL_ERROR:
   1.201 +    case PR_SYS_DESC_TABLE_FULL_ERROR:
   1.202 +    case PR_INSUFFICIENT_RESOURCES_ERROR:
   1.203 +        rv = NS_ERROR_OUT_OF_MEMORY;
   1.204 +        break;
   1.205 +    case PR_ADDRESS_IN_USE_ERROR:
   1.206 +        rv = NS_ERROR_SOCKET_ADDRESS_IN_USE;
   1.207 +        break;
   1.208 +    // These filename-related errors can arise when using Unix-domain sockets.
   1.209 +    case PR_FILE_NOT_FOUND_ERROR:
   1.210 +        rv = NS_ERROR_FILE_NOT_FOUND;
   1.211 +        break;
   1.212 +    case PR_IS_DIRECTORY_ERROR:
   1.213 +        rv = NS_ERROR_FILE_IS_DIRECTORY;
   1.214 +        break;
   1.215 +    case PR_LOOP_ERROR:
   1.216 +        rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
   1.217 +        break;
   1.218 +    case PR_NAME_TOO_LONG_ERROR:
   1.219 +        rv = NS_ERROR_FILE_NAME_TOO_LONG;
   1.220 +        break;
   1.221 +    case PR_NO_DEVICE_SPACE_ERROR:
   1.222 +        rv = NS_ERROR_FILE_NO_DEVICE_SPACE;
   1.223 +        break;
   1.224 +    case PR_NOT_DIRECTORY_ERROR:
   1.225 +        rv = NS_ERROR_FILE_NOT_DIRECTORY;
   1.226 +        break;
   1.227 +    case PR_READ_ONLY_FILESYSTEM_ERROR:
   1.228 +        rv = NS_ERROR_FILE_READ_ONLY;
   1.229 +        break;
   1.230 +    default:
   1.231 +        if (IsNSSErrorCode(errorCode))
   1.232 +            rv = GetXPCOMFromNSSError(errorCode);
   1.233 +        break;
   1.234 +
   1.235 +    // NSPR's socket code can return these, but they're not worth breaking out
   1.236 +    // into their own error codes, distinct from NS_ERROR_FAILURE:
   1.237 +    //
   1.238 +    // PR_BAD_DESCRIPTOR_ERROR
   1.239 +    // PR_INVALID_ARGUMENT_ERROR
   1.240 +    // PR_NOT_SOCKET_ERROR
   1.241 +    // PR_NOT_TCP_SOCKET_ERROR
   1.242 +    //   These would indicate a bug internal to the component.
   1.243 +    //
   1.244 +    // PR_PROTOCOL_NOT_SUPPORTED_ERROR
   1.245 +    //   This means that we can't use the given "protocol" (like
   1.246 +    //   IPPROTO_TCP or IPPROTO_UDP) with a socket of the given type. As
   1.247 +    //   above, this indicates an internal bug.
   1.248 +    //
   1.249 +    // PR_IS_CONNECTED_ERROR
   1.250 +    //   This indicates that we've applied a system call like 'bind' or
   1.251 +    //   'connect' to a socket that is already connected. The socket
   1.252 +    //   components manage each file descriptor's state, and in some cases
   1.253 +    //   handle this error result internally. We shouldn't be returning
   1.254 +    //   this to our callers.
   1.255 +    //
   1.256 +    // PR_IO_ERROR
   1.257 +    //   This is so vague that NS_ERROR_FAILURE is just as good.
   1.258 +    }
   1.259 +    SOCKET_LOG(("ErrorAccordingToNSPR [in=%d out=%x]\n", errorCode, rv));
   1.260 +    return rv;
   1.261 +}
   1.262 +
   1.263 +//-----------------------------------------------------------------------------
   1.264 +// socket input stream impl 
   1.265 +//-----------------------------------------------------------------------------
   1.266 +
   1.267 +nsSocketInputStream::nsSocketInputStream(nsSocketTransport *trans)
   1.268 +    : mTransport(trans)
   1.269 +    , mReaderRefCnt(0)
   1.270 +    , mCondition(NS_OK)
   1.271 +    , mCallbackFlags(0)
   1.272 +    , mByteCount(0)
   1.273 +{
   1.274 +}
   1.275 +
   1.276 +nsSocketInputStream::~nsSocketInputStream()
   1.277 +{
   1.278 +}
   1.279 +
   1.280 +// called on the socket transport thread...
   1.281 +//
   1.282 +//   condition : failure code if socket has been closed
   1.283 +//
   1.284 +void
   1.285 +nsSocketInputStream::OnSocketReady(nsresult condition)
   1.286 +{
   1.287 +    SOCKET_LOG(("nsSocketInputStream::OnSocketReady [this=%p cond=%x]\n",
   1.288 +        this, condition));
   1.289 +
   1.290 +    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   1.291 +
   1.292 +    nsCOMPtr<nsIInputStreamCallback> callback;
   1.293 +    {
   1.294 +        MutexAutoLock lock(mTransport->mLock);
   1.295 +
   1.296 +        // update condition, but be careful not to erase an already
   1.297 +        // existing error condition.
   1.298 +        if (NS_SUCCEEDED(mCondition))
   1.299 +            mCondition = condition;
   1.300 +
   1.301 +        // ignore event if only waiting for closure and not closed.
   1.302 +        if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
   1.303 +            callback = mCallback;
   1.304 +            mCallback = nullptr;
   1.305 +            mCallbackFlags = 0;
   1.306 +        }
   1.307 +    }
   1.308 +
   1.309 +    if (callback)
   1.310 +        callback->OnInputStreamReady(this);
   1.311 +}
   1.312 +
   1.313 +NS_IMPL_QUERY_INTERFACE(nsSocketInputStream,
   1.314 +                        nsIInputStream,
   1.315 +                        nsIAsyncInputStream)
   1.316 +
   1.317 +NS_IMETHODIMP_(MozExternalRefCountType)
   1.318 +nsSocketInputStream::AddRef()
   1.319 +{
   1.320 +    ++mReaderRefCnt;
   1.321 +    return mTransport->AddRef();
   1.322 +}
   1.323 +
   1.324 +NS_IMETHODIMP_(MozExternalRefCountType)
   1.325 +nsSocketInputStream::Release()
   1.326 +{
   1.327 +    if (--mReaderRefCnt == 0)
   1.328 +        Close();
   1.329 +    return mTransport->Release();
   1.330 +}
   1.331 +
   1.332 +NS_IMETHODIMP
   1.333 +nsSocketInputStream::Close()
   1.334 +{
   1.335 +    return CloseWithStatus(NS_BASE_STREAM_CLOSED);
   1.336 +}
   1.337 +
   1.338 +NS_IMETHODIMP
   1.339 +nsSocketInputStream::Available(uint64_t *avail)
   1.340 +{
   1.341 +    SOCKET_LOG(("nsSocketInputStream::Available [this=%p]\n", this));
   1.342 +
   1.343 +    *avail = 0;
   1.344 +
   1.345 +    PRFileDesc *fd;
   1.346 +    {
   1.347 +        MutexAutoLock lock(mTransport->mLock);
   1.348 +
   1.349 +        if (NS_FAILED(mCondition))
   1.350 +            return mCondition;
   1.351 +
   1.352 +        fd = mTransport->GetFD_Locked();
   1.353 +        if (!fd)
   1.354 +            return NS_OK;
   1.355 +    }
   1.356 +
   1.357 +    // cannot hold lock while calling NSPR.  (worried about the fact that PSM
   1.358 +    // synchronously proxies notifications over to the UI thread, which could
   1.359 +    // mistakenly try to re-enter this code.)
   1.360 +    int32_t n = PR_Available(fd);
   1.361 +
   1.362 +    // PSM does not implement PR_Available() so do a best approximation of it
   1.363 +    // with MSG_PEEK
   1.364 +    if ((n == -1) && (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR)) {
   1.365 +        char c;
   1.366 +
   1.367 +        n = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
   1.368 +        SOCKET_LOG(("nsSocketInputStream::Available [this=%p] "
   1.369 +                    "using PEEK backup n=%d]\n", this, n));
   1.370 +    }
   1.371 +
   1.372 +    nsresult rv;
   1.373 +    {
   1.374 +        MutexAutoLock lock(mTransport->mLock);
   1.375 +
   1.376 +        mTransport->ReleaseFD_Locked(fd);
   1.377 +
   1.378 +        if (n >= 0)
   1.379 +            *avail = n;
   1.380 +        else {
   1.381 +            PRErrorCode code = PR_GetError();
   1.382 +            if (code == PR_WOULD_BLOCK_ERROR)
   1.383 +                return NS_OK;
   1.384 +            mCondition = ErrorAccordingToNSPR(code);
   1.385 +        }
   1.386 +        rv = mCondition;
   1.387 +    }
   1.388 +    if (NS_FAILED(rv))
   1.389 +        mTransport->OnInputClosed(rv);
   1.390 +    return rv;
   1.391 +}
   1.392 +
   1.393 +NS_IMETHODIMP
   1.394 +nsSocketInputStream::Read(char *buf, uint32_t count, uint32_t *countRead)
   1.395 +{
   1.396 +    SOCKET_LOG(("nsSocketInputStream::Read [this=%p count=%u]\n", this, count));
   1.397 +
   1.398 +    *countRead = 0;
   1.399 +
   1.400 +    PRFileDesc* fd = nullptr;
   1.401 +    {
   1.402 +        MutexAutoLock lock(mTransport->mLock);
   1.403 +
   1.404 +        if (NS_FAILED(mCondition))
   1.405 +            return (mCondition == NS_BASE_STREAM_CLOSED) ? NS_OK : mCondition;
   1.406 +
   1.407 +        fd = mTransport->GetFD_Locked();
   1.408 +        if (!fd)
   1.409 +            return NS_BASE_STREAM_WOULD_BLOCK;
   1.410 +    }
   1.411 +
   1.412 +    SOCKET_LOG(("  calling PR_Read [count=%u]\n", count));
   1.413 +
   1.414 +    // cannot hold lock while calling NSPR.  (worried about the fact that PSM
   1.415 +    // synchronously proxies notifications over to the UI thread, which could
   1.416 +    // mistakenly try to re-enter this code.)
   1.417 +    int32_t n = PR_Read(fd, buf, count);
   1.418 +
   1.419 +    SOCKET_LOG(("  PR_Read returned [n=%d]\n", n));
   1.420 +
   1.421 +    nsresult rv = NS_OK;
   1.422 +    {
   1.423 +        MutexAutoLock lock(mTransport->mLock);
   1.424 +
   1.425 +#ifdef ENABLE_SOCKET_TRACING
   1.426 +        if (n > 0)
   1.427 +            mTransport->TraceInBuf(buf, n);
   1.428 +#endif
   1.429 +
   1.430 +        mTransport->ReleaseFD_Locked(fd);
   1.431 +
   1.432 +        if (n > 0)
   1.433 +            mByteCount += (*countRead = n);
   1.434 +        else if (n < 0) {
   1.435 +            PRErrorCode code = PR_GetError();
   1.436 +            if (code == PR_WOULD_BLOCK_ERROR)
   1.437 +                return NS_BASE_STREAM_WOULD_BLOCK;
   1.438 +            mCondition = ErrorAccordingToNSPR(code);
   1.439 +        }
   1.440 +        rv = mCondition;
   1.441 +    }
   1.442 +    if (NS_FAILED(rv))
   1.443 +        mTransport->OnInputClosed(rv);
   1.444 +
   1.445 +    // only send this notification if we have indeed read some data.
   1.446 +    // see bug 196827 for an example of why this is important.
   1.447 +    if (n > 0)
   1.448 +        mTransport->SendStatus(NS_NET_STATUS_RECEIVING_FROM);
   1.449 +    return rv;
   1.450 +}
   1.451 +
   1.452 +NS_IMETHODIMP
   1.453 +nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
   1.454 +                                  uint32_t count, uint32_t *countRead)
   1.455 +{
   1.456 +    // socket stream is unbuffered
   1.457 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.458 +}
   1.459 +
   1.460 +NS_IMETHODIMP
   1.461 +nsSocketInputStream::IsNonBlocking(bool *nonblocking)
   1.462 +{
   1.463 +    *nonblocking = true;
   1.464 +    return NS_OK;
   1.465 +}
   1.466 +
   1.467 +NS_IMETHODIMP
   1.468 +nsSocketInputStream::CloseWithStatus(nsresult reason)
   1.469 +{
   1.470 +    SOCKET_LOG(("nsSocketInputStream::CloseWithStatus [this=%p reason=%x]\n", this, reason));
   1.471 +
   1.472 +    // may be called from any thread
   1.473 + 
   1.474 +    nsresult rv;
   1.475 +    {
   1.476 +        MutexAutoLock lock(mTransport->mLock);
   1.477 +
   1.478 +        if (NS_SUCCEEDED(mCondition))
   1.479 +            rv = mCondition = reason;
   1.480 +        else
   1.481 +            rv = NS_OK;
   1.482 +    }
   1.483 +    if (NS_FAILED(rv))
   1.484 +        mTransport->OnInputClosed(rv);
   1.485 +    return NS_OK;
   1.486 +}
   1.487 +
   1.488 +NS_IMETHODIMP
   1.489 +nsSocketInputStream::AsyncWait(nsIInputStreamCallback *callback,
   1.490 +                               uint32_t flags,
   1.491 +                               uint32_t amount,
   1.492 +                               nsIEventTarget *target)
   1.493 +{
   1.494 +    SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%p]\n", this));
   1.495 +
   1.496 +    bool hasError = false;
   1.497 +    {
   1.498 +        MutexAutoLock lock(mTransport->mLock);
   1.499 +
   1.500 +        if (callback && target) {
   1.501 +            //
   1.502 +            // build event proxy
   1.503 +            //
   1.504 +            mCallback = NS_NewInputStreamReadyEvent(callback, target);
   1.505 +        }
   1.506 +        else
   1.507 +            mCallback = callback;
   1.508 +        mCallbackFlags = flags;
   1.509 +
   1.510 +        hasError = NS_FAILED(mCondition);
   1.511 +    } // unlock mTransport->mLock
   1.512 +
   1.513 +    if (hasError) {
   1.514 +        // OnSocketEvent will call OnInputStreamReady with an error code after
   1.515 +        // going through the event loop. We do this because most socket callers
   1.516 +        // do not expect AsyncWait() to synchronously execute the OnInputStreamReady
   1.517 +        // callback.
   1.518 +        mTransport->PostEvent(nsSocketTransport::MSG_INPUT_PENDING);
   1.519 +    } else {
   1.520 +        mTransport->OnInputPending();
   1.521 +    }
   1.522 +
   1.523 +    return NS_OK;
   1.524 +}
   1.525 +
   1.526 +//-----------------------------------------------------------------------------
   1.527 +// socket output stream impl 
   1.528 +//-----------------------------------------------------------------------------
   1.529 +
   1.530 +nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport *trans)
   1.531 +    : mTransport(trans)
   1.532 +    , mWriterRefCnt(0)
   1.533 +    , mCondition(NS_OK)
   1.534 +    , mCallbackFlags(0)
   1.535 +    , mByteCount(0)
   1.536 +{
   1.537 +}
   1.538 +
   1.539 +nsSocketOutputStream::~nsSocketOutputStream()
   1.540 +{
   1.541 +}
   1.542 +
   1.543 +// called on the socket transport thread...
   1.544 +//
   1.545 +//   condition : failure code if socket has been closed
   1.546 +//
   1.547 +void
   1.548 +nsSocketOutputStream::OnSocketReady(nsresult condition)
   1.549 +{
   1.550 +    SOCKET_LOG(("nsSocketOutputStream::OnSocketReady [this=%p cond=%x]\n",
   1.551 +        this, condition));
   1.552 +
   1.553 +    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   1.554 +
   1.555 +    nsCOMPtr<nsIOutputStreamCallback> callback;
   1.556 +    {
   1.557 +        MutexAutoLock lock(mTransport->mLock);
   1.558 +
   1.559 +        // update condition, but be careful not to erase an already
   1.560 +        // existing error condition.
   1.561 +        if (NS_SUCCEEDED(mCondition))
   1.562 +            mCondition = condition;
   1.563 +
   1.564 +        // ignore event if only waiting for closure and not closed.
   1.565 +        if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
   1.566 +            callback = mCallback;
   1.567 +            mCallback = nullptr;
   1.568 +            mCallbackFlags = 0;
   1.569 +        }
   1.570 +    }
   1.571 +
   1.572 +    if (callback)
   1.573 +        callback->OnOutputStreamReady(this);
   1.574 +}
   1.575 +
   1.576 +NS_IMPL_QUERY_INTERFACE(nsSocketOutputStream,
   1.577 +                        nsIOutputStream,
   1.578 +                        nsIAsyncOutputStream)
   1.579 +
   1.580 +NS_IMETHODIMP_(MozExternalRefCountType)
   1.581 +nsSocketOutputStream::AddRef()
   1.582 +{
   1.583 +    ++mWriterRefCnt;
   1.584 +    return mTransport->AddRef();
   1.585 +}
   1.586 +
   1.587 +NS_IMETHODIMP_(MozExternalRefCountType)
   1.588 +nsSocketOutputStream::Release()
   1.589 +{
   1.590 +    if (--mWriterRefCnt == 0)
   1.591 +        Close();
   1.592 +    return mTransport->Release();
   1.593 +}
   1.594 +
   1.595 +NS_IMETHODIMP
   1.596 +nsSocketOutputStream::Close()
   1.597 +{
   1.598 +    return CloseWithStatus(NS_BASE_STREAM_CLOSED);
   1.599 +}
   1.600 +
   1.601 +NS_IMETHODIMP
   1.602 +nsSocketOutputStream::Flush()
   1.603 +{
   1.604 +    return NS_OK;
   1.605 +}
   1.606 +
   1.607 +NS_IMETHODIMP
   1.608 +nsSocketOutputStream::Write(const char *buf, uint32_t count, uint32_t *countWritten)
   1.609 +{
   1.610 +    SOCKET_LOG(("nsSocketOutputStream::Write [this=%p count=%u]\n", this, count));
   1.611 +
   1.612 +    *countWritten = 0;
   1.613 +
   1.614 +    // A write of 0 bytes can be used to force the initial SSL handshake, so do
   1.615 +    // not reject that.
   1.616 +
   1.617 +    PRFileDesc* fd = nullptr;
   1.618 +    {
   1.619 +        MutexAutoLock lock(mTransport->mLock);
   1.620 +
   1.621 +        if (NS_FAILED(mCondition))
   1.622 +            return mCondition;
   1.623 +        
   1.624 +        fd = mTransport->GetFD_Locked();
   1.625 +        if (!fd)
   1.626 +            return NS_BASE_STREAM_WOULD_BLOCK;
   1.627 +    }
   1.628 +
   1.629 +    SOCKET_LOG(("  calling PR_Write [count=%u]\n", count));
   1.630 +
   1.631 +    // cannot hold lock while calling NSPR.  (worried about the fact that PSM
   1.632 +    // synchronously proxies notifications over to the UI thread, which could
   1.633 +    // mistakenly try to re-enter this code.)
   1.634 +    int32_t n = PR_Write(fd, buf, count);
   1.635 +
   1.636 +    SOCKET_LOG(("  PR_Write returned [n=%d]\n", n));
   1.637 +
   1.638 +    nsresult rv = NS_OK;
   1.639 +    {
   1.640 +        MutexAutoLock lock(mTransport->mLock);
   1.641 +
   1.642 +#ifdef ENABLE_SOCKET_TRACING
   1.643 +        if (n > 0)
   1.644 +            mTransport->TraceOutBuf(buf, n);
   1.645 +#endif
   1.646 +
   1.647 +        mTransport->ReleaseFD_Locked(fd);
   1.648 +
   1.649 +        if (n > 0)
   1.650 +            mByteCount += (*countWritten = n);
   1.651 +        else if (n < 0) {
   1.652 +            PRErrorCode code = PR_GetError();
   1.653 +            if (code == PR_WOULD_BLOCK_ERROR)
   1.654 +                return NS_BASE_STREAM_WOULD_BLOCK;
   1.655 +            mCondition = ErrorAccordingToNSPR(code);
   1.656 +        }
   1.657 +        rv = mCondition;
   1.658 +    }
   1.659 +    if (NS_FAILED(rv))
   1.660 +        mTransport->OnOutputClosed(rv);
   1.661 +
   1.662 +    // only send this notification if we have indeed written some data.
   1.663 +    // see bug 196827 for an example of why this is important.
   1.664 +    if (n > 0)
   1.665 +        mTransport->SendStatus(NS_NET_STATUS_SENDING_TO);
   1.666 +    return rv;
   1.667 +}
   1.668 +
   1.669 +NS_IMETHODIMP
   1.670 +nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader, void *closure,
   1.671 +                                    uint32_t count, uint32_t *countRead)
   1.672 +{
   1.673 +    // socket stream is unbuffered
   1.674 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.675 +}
   1.676 +
   1.677 +NS_METHOD
   1.678 +nsSocketOutputStream::WriteFromSegments(nsIInputStream *input,
   1.679 +                                        void *closure,
   1.680 +                                        const char *fromSegment,
   1.681 +                                        uint32_t offset,
   1.682 +                                        uint32_t count,
   1.683 +                                        uint32_t *countRead)
   1.684 +{
   1.685 +    nsSocketOutputStream *self = (nsSocketOutputStream *) closure;
   1.686 +    return self->Write(fromSegment, count, countRead);
   1.687 +}
   1.688 +
   1.689 +NS_IMETHODIMP
   1.690 +nsSocketOutputStream::WriteFrom(nsIInputStream *stream, uint32_t count, uint32_t *countRead)
   1.691 +{
   1.692 +    return stream->ReadSegments(WriteFromSegments, this, count, countRead);
   1.693 +}
   1.694 +
   1.695 +NS_IMETHODIMP
   1.696 +nsSocketOutputStream::IsNonBlocking(bool *nonblocking)
   1.697 +{
   1.698 +    *nonblocking = true;
   1.699 +    return NS_OK;
   1.700 +}
   1.701 +
   1.702 +NS_IMETHODIMP
   1.703 +nsSocketOutputStream::CloseWithStatus(nsresult reason)
   1.704 +{
   1.705 +    SOCKET_LOG(("nsSocketOutputStream::CloseWithStatus [this=%p reason=%x]\n", this, reason));
   1.706 +
   1.707 +    // may be called from any thread
   1.708 + 
   1.709 +    nsresult rv;
   1.710 +    {
   1.711 +        MutexAutoLock lock(mTransport->mLock);
   1.712 +
   1.713 +        if (NS_SUCCEEDED(mCondition))
   1.714 +            rv = mCondition = reason;
   1.715 +        else
   1.716 +            rv = NS_OK;
   1.717 +    }
   1.718 +    if (NS_FAILED(rv))
   1.719 +        mTransport->OnOutputClosed(rv);
   1.720 +    return NS_OK;
   1.721 +}
   1.722 +
   1.723 +NS_IMETHODIMP
   1.724 +nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback *callback,
   1.725 +                                uint32_t flags,
   1.726 +                                uint32_t amount,
   1.727 +                                nsIEventTarget *target)
   1.728 +{
   1.729 +    SOCKET_LOG(("nsSocketOutputStream::AsyncWait [this=%p]\n", this));
   1.730 +
   1.731 +    {
   1.732 +        MutexAutoLock lock(mTransport->mLock);
   1.733 +
   1.734 +        if (callback && target) {
   1.735 +            //
   1.736 +            // build event proxy
   1.737 +            //
   1.738 +            mCallback = NS_NewOutputStreamReadyEvent(callback, target);
   1.739 +        }
   1.740 +        else
   1.741 +            mCallback = callback;
   1.742 +
   1.743 +        mCallbackFlags = flags;
   1.744 +    }
   1.745 +    mTransport->OnOutputPending();
   1.746 +    return NS_OK;
   1.747 +}
   1.748 +
   1.749 +//-----------------------------------------------------------------------------
   1.750 +// socket transport impl
   1.751 +//-----------------------------------------------------------------------------
   1.752 +
   1.753 +nsSocketTransport::nsSocketTransport()
   1.754 +    : mTypes(nullptr)
   1.755 +    , mTypeCount(0)
   1.756 +    , mPort(0)
   1.757 +    , mHttpsProxy(false)
   1.758 +    , mProxyUse(false)
   1.759 +    , mProxyTransparent(false)
   1.760 +    , mProxyTransparentResolvesHost(false)
   1.761 +    , mConnectionFlags(0)
   1.762 +    , mState(STATE_CLOSED)
   1.763 +    , mAttached(false)
   1.764 +    , mInputClosed(true)
   1.765 +    , mOutputClosed(true)
   1.766 +    , mResolving(false)
   1.767 +    , mNetAddrIsSet(false)
   1.768 +    , mLock("nsSocketTransport.mLock")
   1.769 +    , mFD(MOZ_THIS_IN_INITIALIZER_LIST())
   1.770 +    , mFDref(0)
   1.771 +    , mFDconnected(false)
   1.772 +    , mSocketTransportService(gSocketTransportService)
   1.773 +    , mInput(MOZ_THIS_IN_INITIALIZER_LIST())
   1.774 +    , mOutput(MOZ_THIS_IN_INITIALIZER_LIST())
   1.775 +    , mQoSBits(0x00)
   1.776 +    , mKeepaliveEnabled(false)
   1.777 +    , mKeepaliveIdleTimeS(-1)
   1.778 +    , mKeepaliveRetryIntervalS(-1)
   1.779 +    , mKeepaliveProbeCount(-1)
   1.780 +{
   1.781 +    SOCKET_LOG(("creating nsSocketTransport @%p\n", this));
   1.782 +
   1.783 +    mTimeouts[TIMEOUT_CONNECT]    = UINT16_MAX; // no timeout
   1.784 +    mTimeouts[TIMEOUT_READ_WRITE] = UINT16_MAX; // no timeout
   1.785 +}
   1.786 +
   1.787 +nsSocketTransport::~nsSocketTransport()
   1.788 +{
   1.789 +    SOCKET_LOG(("destroying nsSocketTransport @%p\n", this));
   1.790 +
   1.791 +    // cleanup socket type info
   1.792 +    if (mTypes) {
   1.793 +        uint32_t i;
   1.794 +        for (i=0; i<mTypeCount; ++i)
   1.795 +            PL_strfree(mTypes[i]);
   1.796 +        free(mTypes);
   1.797 +    }
   1.798 +}
   1.799 +
   1.800 +nsresult
   1.801 +nsSocketTransport::Init(const char **types, uint32_t typeCount,
   1.802 +                        const nsACString &host, uint16_t port,
   1.803 +                        nsIProxyInfo *givenProxyInfo)
   1.804 +{
   1.805 +    MOZ_EVENT_TRACER_NAME_OBJECT(this, host.BeginReading());
   1.806 +
   1.807 +    nsCOMPtr<nsProxyInfo> proxyInfo;
   1.808 +    if (givenProxyInfo) {
   1.809 +        proxyInfo = do_QueryInterface(givenProxyInfo);
   1.810 +        NS_ENSURE_ARG(proxyInfo);
   1.811 +    }
   1.812 +
   1.813 +    // init socket type info
   1.814 +
   1.815 +    mPort = port;
   1.816 +    mHost = host;
   1.817 +
   1.818 +    const char *proxyType = nullptr;
   1.819 +    if (proxyInfo) {
   1.820 +        mProxyInfo = proxyInfo;
   1.821 +        // grab proxy type (looking for "socks" for example)
   1.822 +        proxyType = proxyInfo->Type();
   1.823 +        if (proxyType && (strcmp(proxyType, "http") == 0 ||
   1.824 +                          strcmp(proxyType, "direct") == 0 ||
   1.825 +                          strcmp(proxyType, "unknown") == 0))
   1.826 +            proxyType = nullptr;
   1.827 +
   1.828 +        mProxyUse = true;
   1.829 +        // check that we don't have a proxyInfo without proxy
   1.830 +        nsCString proxyHost;
   1.831 +        proxyInfo->GetHost(proxyHost);
   1.832 +        if (!proxyType || proxyHost.IsEmpty()) {
   1.833 +            mProxyUse = false;
   1.834 +        }
   1.835 +    }
   1.836 +
   1.837 +    SOCKET_LOG(("nsSocketTransport::Init [this=%x host=%s:%hu proxy=%s]\n",
   1.838 +        this, mHost.get(), mPort, mProxyUse ? "yes" : "no"));
   1.839 +
   1.840 +    // include proxy type as a socket type if proxy type is not "http"
   1.841 +    mTypeCount = typeCount + (proxyType != nullptr);
   1.842 +    if (!mTypeCount)
   1.843 +        return NS_OK;
   1.844 +
   1.845 +    // if we have socket types, then the socket provider service had
   1.846 +    // better exist!
   1.847 +    nsresult rv;
   1.848 +    nsCOMPtr<nsISocketProviderService> spserv =
   1.849 +        do_GetService(kSocketProviderServiceCID, &rv);
   1.850 +    if (NS_FAILED(rv)) return rv;
   1.851 +
   1.852 +    mTypes = (char **) malloc(mTypeCount * sizeof(char *));
   1.853 +    if (!mTypes)
   1.854 +        return NS_ERROR_OUT_OF_MEMORY;
   1.855 +
   1.856 +    // now verify that each socket type has a registered socket provider.
   1.857 +    for (uint32_t i = 0, type = 0; i < mTypeCount; ++i) {
   1.858 +        // store socket types
   1.859 +        if (i == 0 && proxyType)
   1.860 +            mTypes[i] = PL_strdup(proxyType);
   1.861 +        else
   1.862 +            mTypes[i] = PL_strdup(types[type++]);
   1.863 +
   1.864 +        if (!mTypes[i]) {
   1.865 +            mTypeCount = i;
   1.866 +            return NS_ERROR_OUT_OF_MEMORY;
   1.867 +        }
   1.868 +        nsCOMPtr<nsISocketProvider> provider;
   1.869 +        rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
   1.870 +        if (NS_FAILED(rv)) {
   1.871 +            NS_WARNING("no registered socket provider");
   1.872 +            return rv;
   1.873 +        }
   1.874 +
   1.875 +        // note if socket type corresponds to a transparent proxy
   1.876 +        // XXX don't hardcode SOCKS here (use proxy info's flags instead).
   1.877 +        if ((strcmp(mTypes[i], "socks") == 0) ||
   1.878 +            (strcmp(mTypes[i], "socks4") == 0)) {
   1.879 +            mProxyTransparent = true;
   1.880 +
   1.881 +            if (proxyInfo->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
   1.882 +                // we want the SOCKS layer to send the hostname
   1.883 +                // and port to the proxy and let it do the DNS.
   1.884 +                mProxyTransparentResolvesHost = true;
   1.885 +            }
   1.886 +        }
   1.887 +    }
   1.888 +
   1.889 +    return NS_OK;
   1.890 +}
   1.891 +
   1.892 +nsresult
   1.893 +nsSocketTransport::InitWithFilename(const char *filename)
   1.894 +{
   1.895 +#if defined(XP_UNIX)
   1.896 +    size_t filenameLength = strlen(filename);
   1.897 +
   1.898 +    if (filenameLength > sizeof(mNetAddr.local.path) - 1)
   1.899 +        return NS_ERROR_FILE_NAME_TOO_LONG;
   1.900 +
   1.901 +    mHost.Assign(filename);
   1.902 +    mPort = 0;
   1.903 +    mTypeCount = 0;
   1.904 +
   1.905 +    mNetAddr.local.family = AF_LOCAL;
   1.906 +    memcpy(mNetAddr.local.path, filename, filenameLength);
   1.907 +    mNetAddr.local.path[filenameLength] = '\0';
   1.908 +    mNetAddrIsSet = true;
   1.909 +
   1.910 +    return NS_OK;
   1.911 +#else
   1.912 +    return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
   1.913 +#endif
   1.914 +}
   1.915 +
   1.916 +nsresult
   1.917 +nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const NetAddr *addr)
   1.918 +{
   1.919 +    NS_ASSERTION(!mFD.IsInitialized(), "already initialized");
   1.920 +
   1.921 +    char buf[kNetAddrMaxCStrBufSize];
   1.922 +    NetAddrToString(addr, buf, sizeof(buf));
   1.923 +    mHost.Assign(buf);
   1.924 +
   1.925 +    uint16_t port;
   1.926 +    if (addr->raw.family == AF_INET)
   1.927 +        port = addr->inet.port;
   1.928 +    else if (addr->raw.family == AF_INET6)
   1.929 +        port = addr->inet6.port;
   1.930 +    else
   1.931 +        port = 0;
   1.932 +    mPort = ntohs(port);
   1.933 +
   1.934 +    memcpy(&mNetAddr, addr, sizeof(NetAddr));
   1.935 +
   1.936 +    mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
   1.937 +    mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
   1.938 +    mState = STATE_TRANSFERRING;
   1.939 +    mNetAddrIsSet = true;
   1.940 +
   1.941 +    {
   1.942 +        MutexAutoLock lock(mLock);
   1.943 +
   1.944 +        mFD = fd;
   1.945 +        mFDref = 1;
   1.946 +        mFDconnected = 1;
   1.947 +    }
   1.948 +
   1.949 +    // make sure new socket is non-blocking
   1.950 +    PRSocketOptionData opt;
   1.951 +    opt.option = PR_SockOpt_Nonblocking;
   1.952 +    opt.value.non_blocking = true;
   1.953 +    PR_SetSocketOption(fd, &opt);
   1.954 +
   1.955 +    SOCKET_LOG(("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
   1.956 +        this, mHost.get(), mPort));
   1.957 +
   1.958 +    // jump to InitiateSocket to get ourselves attached to the STS poll list.
   1.959 +    return PostEvent(MSG_RETRY_INIT_SOCKET);
   1.960 +}
   1.961 +
   1.962 +nsresult
   1.963 +nsSocketTransport::PostEvent(uint32_t type, nsresult status, nsISupports *param)
   1.964 +{
   1.965 +    SOCKET_LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%x param=%p]\n",
   1.966 +        this, type, status, param));
   1.967 +
   1.968 +    nsCOMPtr<nsIRunnable> event = new nsSocketEvent(this, type, status, param);
   1.969 +    if (!event)
   1.970 +        return NS_ERROR_OUT_OF_MEMORY;
   1.971 +
   1.972 +    return mSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
   1.973 +}
   1.974 +
   1.975 +void
   1.976 +nsSocketTransport::SendStatus(nsresult status)
   1.977 +{
   1.978 +    SOCKET_LOG(("nsSocketTransport::SendStatus [this=%p status=%x]\n", this, status));
   1.979 +
   1.980 +    nsCOMPtr<nsITransportEventSink> sink;
   1.981 +    uint64_t progress;
   1.982 +    {
   1.983 +        MutexAutoLock lock(mLock);
   1.984 +        sink = mEventSink;
   1.985 +        switch (status) {
   1.986 +        case NS_NET_STATUS_SENDING_TO:
   1.987 +            progress = mOutput.ByteCount();
   1.988 +            break;
   1.989 +        case NS_NET_STATUS_RECEIVING_FROM:
   1.990 +            progress = mInput.ByteCount();
   1.991 +            break;
   1.992 +        default:
   1.993 +            progress = 0;
   1.994 +            break;
   1.995 +        }
   1.996 +    }
   1.997 +    if (sink)
   1.998 +        sink->OnTransportStatus(this, status, progress, UINT64_MAX);
   1.999 +}
  1.1000 +
  1.1001 +nsresult
  1.1002 +nsSocketTransport::ResolveHost()
  1.1003 +{
  1.1004 +    SOCKET_LOG(("nsSocketTransport::ResolveHost [this=%p %s:%d%s]\n",
  1.1005 +                this, SocketHost().get(), SocketPort(),
  1.1006 +                mConnectionFlags & nsSocketTransport::BYPASS_CACHE ?
  1.1007 +                " bypass cache" : ""));
  1.1008 +
  1.1009 +    nsresult rv;
  1.1010 +
  1.1011 +    if (mProxyUse) {
  1.1012 +        if (!mProxyTransparent || mProxyTransparentResolvesHost) {
  1.1013 +#if defined(XP_UNIX)
  1.1014 +            NS_ABORT_IF_FALSE(!mNetAddrIsSet || mNetAddr.raw.family != AF_LOCAL,
  1.1015 +                              "Unix domain sockets can't be used with proxies");
  1.1016 +#endif
  1.1017 +            // When not resolving mHost locally, we still want to ensure that
  1.1018 +            // it only contains valid characters.  See bug 304904 for details.
  1.1019 +            if (!net_IsValidHostName(mHost))
  1.1020 +                return NS_ERROR_UNKNOWN_HOST;
  1.1021 +        }
  1.1022 +        if (mProxyTransparentResolvesHost) {
  1.1023 +            // Name resolution is done on the server side.  Just pretend
  1.1024 +            // client resolution is complete, this will get picked up later.
  1.1025 +            // since we don't need to do DNS now, we bypass the resolving
  1.1026 +            // step by initializing mNetAddr to an empty address, but we
  1.1027 +            // must keep the port. The SOCKS IO layer will use the hostname
  1.1028 +            // we send it when it's created, rather than the empty address
  1.1029 +            // we send with the connect call.
  1.1030 +            mState = STATE_RESOLVING;
  1.1031 +            mNetAddr.raw.family = AF_INET;
  1.1032 +            mNetAddr.inet.port = htons(SocketPort());
  1.1033 +            mNetAddr.inet.ip = htonl(INADDR_ANY);
  1.1034 +            return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nullptr);
  1.1035 +        }
  1.1036 +    }
  1.1037 +
  1.1038 +    nsCOMPtr<nsIDNSService> dns = do_GetService(kDNSServiceCID, &rv);
  1.1039 +    if (NS_FAILED(rv)) return rv;
  1.1040 +
  1.1041 +    mResolving = true;
  1.1042 +
  1.1043 +    uint32_t dnsFlags = 0;
  1.1044 +    if (mConnectionFlags & nsSocketTransport::BYPASS_CACHE)
  1.1045 +        dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE;
  1.1046 +    if (mConnectionFlags & nsSocketTransport::DISABLE_IPV6)
  1.1047 +        dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
  1.1048 +    if (mConnectionFlags & nsSocketTransport::DISABLE_IPV4)
  1.1049 +        dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4;
  1.1050 +
  1.1051 +    NS_ASSERTION(!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) ||
  1.1052 +                 !(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4),
  1.1053 +                 "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");
  1.1054 +
  1.1055 +    SendStatus(NS_NET_STATUS_RESOLVING_HOST);
  1.1056 +    rv = dns->AsyncResolve(SocketHost(), dnsFlags, this, nullptr,
  1.1057 +                           getter_AddRefs(mDNSRequest));
  1.1058 +    if (NS_SUCCEEDED(rv)) {
  1.1059 +        SOCKET_LOG(("  advancing to STATE_RESOLVING\n"));
  1.1060 +        mState = STATE_RESOLVING;
  1.1061 +    }
  1.1062 +    return rv;
  1.1063 +}
  1.1064 +
  1.1065 +nsresult
  1.1066 +nsSocketTransport::BuildSocket(PRFileDesc *&fd, bool &proxyTransparent, bool &usingSSL)
  1.1067 +{
  1.1068 +    SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%p]\n", this));
  1.1069 +
  1.1070 +    nsresult rv;
  1.1071 +
  1.1072 +    proxyTransparent = false;
  1.1073 +    usingSSL = false;
  1.1074 +
  1.1075 +    if (mTypeCount == 0) {
  1.1076 +        fd = PR_OpenTCPSocket(mNetAddr.raw.family);
  1.1077 +        rv = fd ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
  1.1078 +    }
  1.1079 +    else {
  1.1080 +#if defined(XP_UNIX)
  1.1081 +        NS_ABORT_IF_FALSE(!mNetAddrIsSet || mNetAddr.raw.family != AF_LOCAL,
  1.1082 +                          "Unix domain sockets can't be used with socket types");
  1.1083 +#endif
  1.1084 +
  1.1085 +        fd = nullptr;
  1.1086 +
  1.1087 +        nsCOMPtr<nsISocketProviderService> spserv =
  1.1088 +            do_GetService(kSocketProviderServiceCID, &rv);
  1.1089 +        if (NS_FAILED(rv)) return rv;
  1.1090 +
  1.1091 +        const char *host       = mHost.get();
  1.1092 +        int32_t     port       = (int32_t) mPort;
  1.1093 +        uint32_t    proxyFlags = 0;
  1.1094 +        nsCOMPtr<nsIProxyInfo> proxy = mProxyInfo;
  1.1095 +
  1.1096 +        uint32_t i;
  1.1097 +        for (i=0; i<mTypeCount; ++i) {
  1.1098 +            nsCOMPtr<nsISocketProvider> provider;
  1.1099 +
  1.1100 +            SOCKET_LOG(("  pushing io layer [%u:%s]\n", i, mTypes[i]));
  1.1101 +
  1.1102 +            rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
  1.1103 +            if (NS_FAILED(rv))
  1.1104 +                break;
  1.1105 +
  1.1106 +            if (mProxyTransparentResolvesHost)
  1.1107 +                proxyFlags |= nsISocketProvider::PROXY_RESOLVES_HOST;
  1.1108 +            
  1.1109 +            if (mConnectionFlags & nsISocketTransport::ANONYMOUS_CONNECT)
  1.1110 +                proxyFlags |= nsISocketProvider::ANONYMOUS_CONNECT;
  1.1111 +
  1.1112 +            if (mConnectionFlags & nsISocketTransport::NO_PERMANENT_STORAGE)
  1.1113 +                proxyFlags |= nsISocketProvider::NO_PERMANENT_STORAGE;
  1.1114 +
  1.1115 +
  1.1116 +            nsCOMPtr<nsISupports> secinfo;
  1.1117 +            if (i == 0) {
  1.1118 +                // if this is the first type, we'll want the 
  1.1119 +                // service to allocate a new socket
  1.1120 +                nsCString proxyHost;
  1.1121 +                GetHost(proxyHost);
  1.1122 +                int32_t proxyPort;
  1.1123 +                GetPort(&proxyPort);
  1.1124 +                rv = provider->NewSocket(mNetAddr.raw.family,
  1.1125 +                                         mHttpsProxy ? proxyHost.get() : host,
  1.1126 +                                         mHttpsProxy ? proxyPort : port,
  1.1127 +                                         proxy,
  1.1128 +                                         proxyFlags, &fd,
  1.1129 +                                         getter_AddRefs(secinfo));
  1.1130 +
  1.1131 +                if (NS_SUCCEEDED(rv) && !fd) {
  1.1132 +                    NS_NOTREACHED("NewSocket succeeded but failed to create a PRFileDesc");
  1.1133 +                    rv = NS_ERROR_UNEXPECTED;
  1.1134 +                }
  1.1135 +            }
  1.1136 +            else {
  1.1137 +                // the socket has already been allocated, 
  1.1138 +                // so we just want the service to add itself
  1.1139 +                // to the stack (such as pushing an io layer)
  1.1140 +                rv = provider->AddToSocket(mNetAddr.raw.family,
  1.1141 +                                           host, port, proxy,
  1.1142 +                                           proxyFlags, fd,
  1.1143 +                                           getter_AddRefs(secinfo));
  1.1144 +            }
  1.1145 +            // proxyFlags = 0; not used below this point...
  1.1146 +            if (NS_FAILED(rv))
  1.1147 +                break;
  1.1148 +
  1.1149 +            // if the service was ssl or starttls, we want to hold onto the socket info
  1.1150 +            bool isSSL = (strcmp(mTypes[i], "ssl") == 0);
  1.1151 +            if (isSSL || (strcmp(mTypes[i], "starttls") == 0)) {
  1.1152 +                // remember security info and give notification callbacks to PSM...
  1.1153 +                nsCOMPtr<nsIInterfaceRequestor> callbacks;
  1.1154 +                {
  1.1155 +                    MutexAutoLock lock(mLock);
  1.1156 +                    mSecInfo = secinfo;
  1.1157 +                    callbacks = mCallbacks;
  1.1158 +                    SOCKET_LOG(("  [secinfo=%x callbacks=%x]\n", mSecInfo.get(), mCallbacks.get()));
  1.1159 +                }
  1.1160 +                // don't call into PSM while holding mLock!!
  1.1161 +                nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
  1.1162 +                if (secCtrl)
  1.1163 +                    secCtrl->SetNotificationCallbacks(callbacks);
  1.1164 +                // remember if socket type is SSL so we can ProxyStartSSL if need be.
  1.1165 +                usingSSL = isSSL;
  1.1166 +            }
  1.1167 +            else if ((strcmp(mTypes[i], "socks") == 0) ||
  1.1168 +                     (strcmp(mTypes[i], "socks4") == 0)) {
  1.1169 +                // since socks is transparent, any layers above
  1.1170 +                // it do not have to worry about proxy stuff
  1.1171 +                proxy = nullptr;
  1.1172 +                proxyTransparent = true;
  1.1173 +            }
  1.1174 +        }
  1.1175 +
  1.1176 +        if (NS_FAILED(rv)) {
  1.1177 +            SOCKET_LOG(("  error pushing io layer [%u:%s rv=%x]\n", i, mTypes[i], rv));
  1.1178 +            if (fd)
  1.1179 +                PR_Close(fd);
  1.1180 +        }
  1.1181 +    }
  1.1182 +
  1.1183 +    return rv;
  1.1184 +}
  1.1185 +
  1.1186 +nsresult
  1.1187 +nsSocketTransport::InitiateSocket()
  1.1188 +{
  1.1189 +    SOCKET_LOG(("nsSocketTransport::InitiateSocket [this=%p]\n", this));
  1.1190 +
  1.1191 +    static bool crashOnNonLocalConnections = !!getenv("MOZ_DISABLE_NONLOCAL_CONNECTIONS");
  1.1192 +
  1.1193 +    nsresult rv;
  1.1194 +    bool isLocal;
  1.1195 +    IsLocal(&isLocal);
  1.1196 +
  1.1197 +    if (gIOService->IsOffline()) {
  1.1198 +        if (!isLocal)
  1.1199 +            return NS_ERROR_OFFLINE;
  1.1200 +    } else if (!isLocal) {
  1.1201 +        if (NS_SUCCEEDED(mCondition) &&
  1.1202 +            crashOnNonLocalConnections &&
  1.1203 +            !(IsIPAddrAny(&mNetAddr) || IsIPAddrLocal(&mNetAddr))) {
  1.1204 +            nsAutoCString ipaddr;
  1.1205 +            nsRefPtr<nsNetAddr> netaddr = new nsNetAddr(&mNetAddr);
  1.1206 +            netaddr->GetAddress(ipaddr);
  1.1207 +            fprintf_stderr(stderr,
  1.1208 +                           "FATAL ERROR: Non-local network connections are disabled and a connection "
  1.1209 +                           "attempt to %s (%s) was made.\nYou should only access hostnames "
  1.1210 +                           "available via the test networking proxy (if running mochitests) "
  1.1211 +                           "or from a test-specific httpd.js server (if running xpcshell tests). "
  1.1212 +                           "Browser services should be disabled or redirected to a local server.\n",
  1.1213 +                           mHost.get(), ipaddr.get());
  1.1214 +            MOZ_CRASH("Attempting to connect to non-local address!");
  1.1215 +        }
  1.1216 +    }
  1.1217 +
  1.1218 +    // Hosts/Proxy Hosts that are Local IP Literals should not be speculatively
  1.1219 +    // connected - Bug 853423.
  1.1220 +    if (mConnectionFlags & nsISocketTransport::DISABLE_RFC1918 &&
  1.1221 +        IsIPAddrLocal(&mNetAddr)) {
  1.1222 +#ifdef PR_LOGGING
  1.1223 +        if (SOCKET_LOG_ENABLED()) {
  1.1224 +            nsAutoCString netAddrCString;
  1.1225 +            netAddrCString.SetCapacity(kIPv6CStrBufSize);
  1.1226 +            if (!NetAddrToString(&mNetAddr,
  1.1227 +                                 netAddrCString.BeginWriting(),
  1.1228 +                                 kIPv6CStrBufSize))
  1.1229 +                netAddrCString = NS_LITERAL_CSTRING("<IP-to-string failed>");
  1.1230 +            nsCString proxyHost;
  1.1231 +            GetHost(proxyHost);
  1.1232 +            int32_t proxyPort;
  1.1233 +            GetPort(&proxyPort);
  1.1234 +            SOCKET_LOG(("nsSocketTransport::InitiateSocket skipping "
  1.1235 +                        "speculative connection for host [%s:%d] proxy "
  1.1236 +                        "[%s:%d] with Local IP address [%s]",
  1.1237 +                        mHost.get(), mPort, proxyHost.get(), proxyPort,
  1.1238 +                        netAddrCString.get()));
  1.1239 +        }
  1.1240 +#endif
  1.1241 +        return NS_ERROR_CONNECTION_REFUSED;
  1.1242 +    }
  1.1243 +
  1.1244 +    //
  1.1245 +    // find out if it is going to be ok to attach another socket to the STS.
  1.1246 +    // if not then we have to wait for the STS to tell us that it is ok.
  1.1247 +    // the notification is asynchronous, which means that when we could be
  1.1248 +    // in a race to call AttachSocket once notified.  for this reason, when
  1.1249 +    // we get notified, we just re-enter this function.  as a result, we are
  1.1250 +    // sure to ask again before calling AttachSocket.  in this way we deal
  1.1251 +    // with the race condition.  though it isn't the most elegant solution,
  1.1252 +    // it is far simpler than trying to build a system that would guarantee
  1.1253 +    // FIFO ordering (which wouldn't even be that valuable IMO).  see bug
  1.1254 +    // 194402 for more info.
  1.1255 +    //
  1.1256 +    if (!mSocketTransportService->CanAttachSocket()) {
  1.1257 +        nsCOMPtr<nsIRunnable> event =
  1.1258 +                new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET);
  1.1259 +        if (!event)
  1.1260 +            return NS_ERROR_OUT_OF_MEMORY;
  1.1261 +        return mSocketTransportService->NotifyWhenCanAttachSocket(event);
  1.1262 +    }
  1.1263 +
  1.1264 +    //
  1.1265 +    // if we already have a connected socket, then just attach and return.
  1.1266 +    //
  1.1267 +    if (mFD.IsInitialized()) {
  1.1268 +        rv = mSocketTransportService->AttachSocket(mFD, this);
  1.1269 +        if (NS_SUCCEEDED(rv))
  1.1270 +            mAttached = true;
  1.1271 +        return rv;
  1.1272 +    }
  1.1273 +
  1.1274 +    //
  1.1275 +    // create new socket fd, push io layers, etc.
  1.1276 +    //
  1.1277 +    PRFileDesc *fd;
  1.1278 +    bool proxyTransparent;
  1.1279 +    bool usingSSL;
  1.1280 +
  1.1281 +    rv = BuildSocket(fd, proxyTransparent, usingSSL);
  1.1282 +    if (NS_FAILED(rv)) {
  1.1283 +        SOCKET_LOG(("  BuildSocket failed [rv=%x]\n", rv));
  1.1284 +        return rv;
  1.1285 +    }
  1.1286 +
  1.1287 +    // Attach network activity monitor
  1.1288 +    mozilla::net::NetworkActivityMonitor::AttachIOLayer(fd);
  1.1289 +
  1.1290 +    PRStatus status;
  1.1291 +
  1.1292 +    // Make the socket non-blocking...
  1.1293 +    PRSocketOptionData opt;
  1.1294 +    opt.option = PR_SockOpt_Nonblocking;
  1.1295 +    opt.value.non_blocking = true;
  1.1296 +    status = PR_SetSocketOption(fd, &opt);
  1.1297 +    NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
  1.1298 +
  1.1299 +    // disable the nagle algorithm - if we rely on it to coalesce writes into
  1.1300 +    // full packets the final packet of a multi segment POST/PUT or pipeline
  1.1301 +    // sequence is delayed a full rtt
  1.1302 +    opt.option = PR_SockOpt_NoDelay;
  1.1303 +    opt.value.no_delay = true;
  1.1304 +    PR_SetSocketOption(fd, &opt);
  1.1305 +
  1.1306 +    // if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF
  1.1307 +    // The Windows default of 8KB is too small and as of vista sp1, autotuning
  1.1308 +    // only applies to receive window
  1.1309 +    int32_t sndBufferSize;
  1.1310 +    mSocketTransportService->GetSendBufferSize(&sndBufferSize);
  1.1311 +    if (sndBufferSize > 0) {
  1.1312 +        opt.option = PR_SockOpt_SendBufferSize;
  1.1313 +        opt.value.send_buffer_size = sndBufferSize;
  1.1314 +        PR_SetSocketOption(fd, &opt);
  1.1315 +    }
  1.1316 +
  1.1317 +    if (mQoSBits) {
  1.1318 +        opt.option = PR_SockOpt_IpTypeOfService;
  1.1319 +        opt.value.tos = mQoSBits;
  1.1320 +        PR_SetSocketOption(fd, &opt);
  1.1321 +    }
  1.1322 +
  1.1323 +    // inform socket transport about this newly created socket...
  1.1324 +    rv = mSocketTransportService->AttachSocket(fd, this);
  1.1325 +    if (NS_FAILED(rv)) {
  1.1326 +        PR_Close(fd);
  1.1327 +        return rv;
  1.1328 +    }
  1.1329 +    mAttached = true;
  1.1330 +
  1.1331 +    // assign mFD so that we can properly handle OnSocketDetached before we've
  1.1332 +    // established a connection.
  1.1333 +    {
  1.1334 +        MutexAutoLock lock(mLock);
  1.1335 +        mFD = fd;
  1.1336 +        mFDref = 1;
  1.1337 +        mFDconnected = false;
  1.1338 +    }
  1.1339 +
  1.1340 +    SOCKET_LOG(("  advancing to STATE_CONNECTING\n"));
  1.1341 +    mState = STATE_CONNECTING;
  1.1342 +    mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
  1.1343 +    SendStatus(NS_NET_STATUS_CONNECTING_TO);
  1.1344 +
  1.1345 +#if defined(PR_LOGGING)
  1.1346 +    if (SOCKET_LOG_ENABLED()) {
  1.1347 +        char buf[kNetAddrMaxCStrBufSize];
  1.1348 +        NetAddrToString(&mNetAddr, buf, sizeof(buf));
  1.1349 +        SOCKET_LOG(("  trying address: %s\n", buf));
  1.1350 +    }
  1.1351 +#endif
  1.1352 +
  1.1353 +    //
  1.1354 +    // Initiate the connect() to the host...
  1.1355 +    //
  1.1356 +    PRNetAddr prAddr;
  1.1357 +    NetAddrToPRNetAddr(&mNetAddr, &prAddr);
  1.1358 +
  1.1359 +    MOZ_EVENT_TRACER_EXEC(this, "net::tcp::connect");
  1.1360 +    status = PR_Connect(fd, &prAddr, NS_SOCKET_CONNECT_TIMEOUT);
  1.1361 +    if (status == PR_SUCCESS) {
  1.1362 +        // 
  1.1363 +        // we are connected!
  1.1364 +        //
  1.1365 +        OnSocketConnected();
  1.1366 +    }
  1.1367 +    else {
  1.1368 +        PRErrorCode code = PR_GetError();
  1.1369 +#if defined(TEST_CONNECT_ERRORS)
  1.1370 +        code = RandomizeConnectError(code);
  1.1371 +#endif
  1.1372 +        //
  1.1373 +        // If the PR_Connect(...) would block, then poll for a connection.
  1.1374 +        //
  1.1375 +        if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code))
  1.1376 +            mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
  1.1377 +        //
  1.1378 +        // If the socket is already connected, then return success...
  1.1379 +        //
  1.1380 +        else if (PR_IS_CONNECTED_ERROR == code) {
  1.1381 +            //
  1.1382 +            // we are connected!
  1.1383 +            //
  1.1384 +            OnSocketConnected();
  1.1385 +
  1.1386 +            if (mSecInfo && mProxyUse && proxyTransparent && usingSSL) {
  1.1387 +                // if the connection phase is finished, and the ssl layer has
  1.1388 +                // been pushed, and we were proxying (transparently; ie. nothing
  1.1389 +                // has to happen in the protocol layer above us), it's time for
  1.1390 +                // the ssl to start doing it's thing.
  1.1391 +                nsCOMPtr<nsISSLSocketControl> secCtrl =
  1.1392 +                    do_QueryInterface(mSecInfo);
  1.1393 +                if (secCtrl) {
  1.1394 +                    SOCKET_LOG(("  calling ProxyStartSSL()\n"));
  1.1395 +                    secCtrl->ProxyStartSSL();
  1.1396 +                }
  1.1397 +                // XXX what if we were forced to poll on the socket for a successful
  1.1398 +                // connection... wouldn't we need to call ProxyStartSSL after a call
  1.1399 +                // to PR_ConnectContinue indicates that we are connected?
  1.1400 +                //
  1.1401 +                // XXX this appears to be what the old socket transport did.  why
  1.1402 +                // isn't this broken?
  1.1403 +            }
  1.1404 +        }
  1.1405 +        //
  1.1406 +        // A SOCKS request was rejected; get the actual error code from
  1.1407 +        // the OS error
  1.1408 +        //
  1.1409 +        else if (PR_UNKNOWN_ERROR == code &&
  1.1410 +                 mProxyUse && mProxyTransparent) {
  1.1411 +            code = PR_GetOSError();
  1.1412 +            rv = ErrorAccordingToNSPR(code);
  1.1413 +        }
  1.1414 +        //
  1.1415 +        // The connection was refused...
  1.1416 +        //
  1.1417 +        else {
  1.1418 +            rv = ErrorAccordingToNSPR(code);
  1.1419 +            if (rv == NS_ERROR_CONNECTION_REFUSED && mProxyUse)
  1.1420 +                rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
  1.1421 +        }
  1.1422 +    }
  1.1423 +    return rv;
  1.1424 +}
  1.1425 +
  1.1426 +bool
  1.1427 +nsSocketTransport::RecoverFromError()
  1.1428 +{
  1.1429 +    NS_ASSERTION(NS_FAILED(mCondition), "there should be something wrong");
  1.1430 +
  1.1431 +    SOCKET_LOG(("nsSocketTransport::RecoverFromError [this=%p state=%x cond=%x]\n",
  1.1432 +        this, mState, mCondition));
  1.1433 +
  1.1434 +#if defined(XP_UNIX)
  1.1435 +    // Unix domain connections don't have multiple addresses to try,
  1.1436 +    // so the recovery techniques here don't apply.
  1.1437 +    if (mNetAddrIsSet && mNetAddr.raw.family == AF_LOCAL)
  1.1438 +        return false;
  1.1439 +#endif
  1.1440 +
  1.1441 +    // can only recover from errors in these states
  1.1442 +    if (mState != STATE_RESOLVING && mState != STATE_CONNECTING)
  1.1443 +        return false;
  1.1444 +
  1.1445 +    nsresult rv;
  1.1446 +
  1.1447 +    // OK to check this outside mLock
  1.1448 +    NS_ASSERTION(!mFDconnected, "socket should not be connected");
  1.1449 +
  1.1450 +    // all connection failures need to be reported to DNS so that the next
  1.1451 +    // time we will use a different address if available.
  1.1452 +    if (mState == STATE_CONNECTING && mDNSRecord) {
  1.1453 +        mDNSRecord->ReportUnusable(SocketPort());
  1.1454 +    }
  1.1455 +
  1.1456 +    // can only recover from these errors
  1.1457 +    if (mCondition != NS_ERROR_CONNECTION_REFUSED &&
  1.1458 +        mCondition != NS_ERROR_PROXY_CONNECTION_REFUSED &&
  1.1459 +        mCondition != NS_ERROR_NET_TIMEOUT &&
  1.1460 +        mCondition != NS_ERROR_UNKNOWN_HOST &&
  1.1461 +        mCondition != NS_ERROR_UNKNOWN_PROXY_HOST)
  1.1462 +        return false;
  1.1463 +
  1.1464 +    bool tryAgain = false;
  1.1465 +
  1.1466 +    if (mConnectionFlags & (DISABLE_IPV6 | DISABLE_IPV4) &&
  1.1467 +        mCondition == NS_ERROR_UNKNOWN_HOST &&
  1.1468 +        mState == STATE_RESOLVING &&
  1.1469 +        !mProxyTransparentResolvesHost) {
  1.1470 +        SOCKET_LOG(("  trying lookup again with both ipv4/ipv6 enabled\n"));
  1.1471 +        mConnectionFlags &= ~(DISABLE_IPV6 | DISABLE_IPV4);
  1.1472 +        tryAgain = true;
  1.1473 +    }
  1.1474 +
  1.1475 +    // try next ip address only if past the resolver stage...
  1.1476 +    if (mState == STATE_CONNECTING && mDNSRecord) {
  1.1477 +        nsresult rv = mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
  1.1478 +        if (NS_SUCCEEDED(rv)) {
  1.1479 +            SOCKET_LOG(("  trying again with next ip address\n"));
  1.1480 +            tryAgain = true;
  1.1481 +        }
  1.1482 +        else if (mConnectionFlags & (DISABLE_IPV6 | DISABLE_IPV4)) {
  1.1483 +            // Drop state to closed.  This will trigger new round of DNS
  1.1484 +            // resolving bellow.
  1.1485 +            // XXX Could be optimized to only switch the flags to save duplicate
  1.1486 +            // connection attempts.
  1.1487 +            SOCKET_LOG(("  failed to connect all ipv4-only or ipv6-only hosts,"
  1.1488 +                        " trying lookup/connect again with both ipv4/ipv6\n"));
  1.1489 +            mState = STATE_CLOSED;
  1.1490 +            mConnectionFlags &= ~(DISABLE_IPV6 | DISABLE_IPV4);
  1.1491 +            tryAgain = true;
  1.1492 +        }
  1.1493 +    }
  1.1494 +
  1.1495 +#if defined(XP_WIN)
  1.1496 +    // If not trying next address, try to make a connection using dialup. 
  1.1497 +    // Retry if that connection is made.
  1.1498 +    if (!tryAgain) {
  1.1499 +        bool autodialEnabled;
  1.1500 +        mSocketTransportService->GetAutodialEnabled(&autodialEnabled);
  1.1501 +        if (autodialEnabled) {
  1.1502 +          tryAgain = nsNativeConnectionHelper::OnConnectionFailed(
  1.1503 +                       NS_ConvertUTF8toUTF16(SocketHost()).get());
  1.1504 +	    }
  1.1505 +    }
  1.1506 +#endif
  1.1507 +
  1.1508 +    // prepare to try again.
  1.1509 +    if (tryAgain) {
  1.1510 +        uint32_t msg;
  1.1511 +
  1.1512 +        if (mState == STATE_CONNECTING) {
  1.1513 +            mState = STATE_RESOLVING;
  1.1514 +            msg = MSG_DNS_LOOKUP_COMPLETE;
  1.1515 +        }
  1.1516 +        else {
  1.1517 +            mState = STATE_CLOSED;
  1.1518 +            msg = MSG_ENSURE_CONNECT;
  1.1519 +        }
  1.1520 +
  1.1521 +        rv = PostEvent(msg, NS_OK);
  1.1522 +        if (NS_FAILED(rv))
  1.1523 +            tryAgain = false;
  1.1524 +    }
  1.1525 +
  1.1526 +    return tryAgain;
  1.1527 +}
  1.1528 +
  1.1529 +// called on the socket thread only
  1.1530 +void
  1.1531 +nsSocketTransport::OnMsgInputClosed(nsresult reason)
  1.1532 +{
  1.1533 +    SOCKET_LOG(("nsSocketTransport::OnMsgInputClosed [this=%p reason=%x]\n",
  1.1534 +        this, reason));
  1.1535 +
  1.1536 +    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1.1537 +
  1.1538 +    mInputClosed = true;
  1.1539 +    // check if event should affect entire transport
  1.1540 +    if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
  1.1541 +        mCondition = reason;                // XXX except if NS_FAILED(mCondition), right??
  1.1542 +    else if (mOutputClosed)
  1.1543 +        mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
  1.1544 +    else {
  1.1545 +        if (mState == STATE_TRANSFERRING)
  1.1546 +            mPollFlags &= ~PR_POLL_READ;
  1.1547 +        mInput.OnSocketReady(reason);
  1.1548 +    }
  1.1549 +}
  1.1550 +
  1.1551 +// called on the socket thread only
  1.1552 +void
  1.1553 +nsSocketTransport::OnMsgOutputClosed(nsresult reason)
  1.1554 +{
  1.1555 +    SOCKET_LOG(("nsSocketTransport::OnMsgOutputClosed [this=%p reason=%x]\n",
  1.1556 +        this, reason));
  1.1557 +
  1.1558 +    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1.1559 +
  1.1560 +    mOutputClosed = true;
  1.1561 +    // check if event should affect entire transport
  1.1562 +    if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
  1.1563 +        mCondition = reason;                // XXX except if NS_FAILED(mCondition), right??
  1.1564 +    else if (mInputClosed)
  1.1565 +        mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
  1.1566 +    else {
  1.1567 +        if (mState == STATE_TRANSFERRING)
  1.1568 +            mPollFlags &= ~PR_POLL_WRITE;
  1.1569 +        mOutput.OnSocketReady(reason);
  1.1570 +    }
  1.1571 +}
  1.1572 +
  1.1573 +void
  1.1574 +nsSocketTransport::OnSocketConnected()
  1.1575 +{
  1.1576 +    SOCKET_LOG(("  advancing to STATE_TRANSFERRING\n"));
  1.1577 +
  1.1578 +    mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
  1.1579 +    mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
  1.1580 +    mState = STATE_TRANSFERRING;
  1.1581 +
  1.1582 +    // Set the mNetAddrIsSet flag only when state has reached TRANSFERRING
  1.1583 +    // because we need to make sure its value does not change due to failover
  1.1584 +    mNetAddrIsSet = true;
  1.1585 +
  1.1586 +    // assign mFD (must do this within the transport lock), but take care not
  1.1587 +    // to trample over mFDref if mFD is already set.
  1.1588 +    {
  1.1589 +        MutexAutoLock lock(mLock);
  1.1590 +        NS_ASSERTION(mFD.IsInitialized(), "no socket");
  1.1591 +        NS_ASSERTION(mFDref == 1, "wrong socket ref count");
  1.1592 +        mFDconnected = true;
  1.1593 +    }
  1.1594 +
  1.1595 +    // Ensure keepalive is configured correctly if previously enabled.
  1.1596 +    if (mKeepaliveEnabled) {
  1.1597 +        nsresult rv = SetKeepaliveEnabledInternal(true);
  1.1598 +        if (NS_WARN_IF(NS_FAILED(rv))) {
  1.1599 +            SOCKET_LOG(("  SetKeepaliveEnabledInternal failed rv[0x%x]", rv));
  1.1600 +        }
  1.1601 +    }
  1.1602 +
  1.1603 +    MOZ_EVENT_TRACER_DONE(this, "net::tcp::connect");
  1.1604 +
  1.1605 +    SendStatus(NS_NET_STATUS_CONNECTED_TO);
  1.1606 +}
  1.1607 +
  1.1608 +PRFileDesc *
  1.1609 +nsSocketTransport::GetFD_Locked()
  1.1610 +{
  1.1611 +    mLock.AssertCurrentThreadOwns();
  1.1612 +
  1.1613 +    // mFD is not available to the streams while disconnected.
  1.1614 +    if (!mFDconnected)
  1.1615 +        return nullptr;
  1.1616 +
  1.1617 +    if (mFD.IsInitialized())
  1.1618 +        mFDref++;
  1.1619 +
  1.1620 +    return mFD;
  1.1621 +}
  1.1622 +
  1.1623 +class ThunkPRClose : public nsRunnable
  1.1624 +{
  1.1625 +public:
  1.1626 +  ThunkPRClose(PRFileDesc *fd) : mFD(fd) {}
  1.1627 +
  1.1628 +  NS_IMETHOD Run()
  1.1629 +  {
  1.1630 +    PR_Close(mFD);
  1.1631 +    return NS_OK;
  1.1632 +  }
  1.1633 +private:
  1.1634 +  PRFileDesc *mFD;
  1.1635 +};
  1.1636 +
  1.1637 +void
  1.1638 +STS_PRCloseOnSocketTransport(PRFileDesc *fd)
  1.1639 +{
  1.1640 +  if (gSocketTransportService) {
  1.1641 +    // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
  1.1642 +    // FIX - Should use RUN_ON_THREAD once it's generally available
  1.1643 +    // RUN_ON_THREAD(gSocketThread,WrapRunnableNM(&PR_Close, mFD);
  1.1644 +    gSocketTransportService->Dispatch(new ThunkPRClose(fd), NS_DISPATCH_NORMAL);
  1.1645 +  } else {
  1.1646 +    // something horrible has happened
  1.1647 +    NS_ASSERTION(gSocketTransportService, "No STS service");
  1.1648 +  }
  1.1649 +}
  1.1650 +
  1.1651 +void
  1.1652 +nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd)
  1.1653 +{
  1.1654 +    mLock.AssertCurrentThreadOwns();
  1.1655 +
  1.1656 +    NS_ASSERTION(mFD == fd, "wrong fd");
  1.1657 +    SOCKET_LOG(("JIMB: ReleaseFD_Locked: mFDref = %d\n", mFDref));
  1.1658 +
  1.1659 +    if (--mFDref == 0) {
  1.1660 +        if (PR_GetCurrentThread() == gSocketThread) {
  1.1661 +            SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%p]\n", this));
  1.1662 +            PR_Close(mFD);
  1.1663 +        } else {
  1.1664 +            // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
  1.1665 +            STS_PRCloseOnSocketTransport(mFD);
  1.1666 +        }
  1.1667 +        mFD = nullptr;
  1.1668 +    }
  1.1669 +}
  1.1670 +
  1.1671 +//-----------------------------------------------------------------------------
  1.1672 +// socket event handler impl
  1.1673 +
  1.1674 +void
  1.1675 +nsSocketTransport::OnSocketEvent(uint32_t type, nsresult status, nsISupports *param)
  1.1676 +{
  1.1677 +    SOCKET_LOG(("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%x param=%p]\n",
  1.1678 +        this, type, status, param));
  1.1679 +
  1.1680 +    if (NS_FAILED(mCondition)) {
  1.1681 +        // block event since we're apparently already dead.
  1.1682 +        SOCKET_LOG(("  blocking event [condition=%x]\n", mCondition));
  1.1683 +        //
  1.1684 +        // notify input/output streams in case either has a pending notify.
  1.1685 +        //
  1.1686 +        mInput.OnSocketReady(mCondition);
  1.1687 +        mOutput.OnSocketReady(mCondition);
  1.1688 +        return;
  1.1689 +    }
  1.1690 +
  1.1691 +    switch (type) {
  1.1692 +    case MSG_ENSURE_CONNECT:
  1.1693 +        SOCKET_LOG(("  MSG_ENSURE_CONNECT\n"));
  1.1694 +        //
  1.1695 +        // ensure that we have created a socket, attached it, and have a
  1.1696 +        // connection.
  1.1697 +        //
  1.1698 +        if (mState == STATE_CLOSED) {
  1.1699 +            // Unix domain sockets are ready to connect; mNetAddr is all we
  1.1700 +            // need. Internet address families require a DNS lookup (or possibly
  1.1701 +            // several) before we can connect.
  1.1702 +#if defined(XP_UNIX)
  1.1703 +            if (mNetAddrIsSet && mNetAddr.raw.family == AF_LOCAL)
  1.1704 +                mCondition = InitiateSocket();
  1.1705 +            else
  1.1706 +#endif
  1.1707 +                mCondition = ResolveHost();
  1.1708 +
  1.1709 +        } else {
  1.1710 +            SOCKET_LOG(("  ignoring redundant event\n"));
  1.1711 +        }
  1.1712 +        break;
  1.1713 +
  1.1714 +    case MSG_DNS_LOOKUP_COMPLETE:
  1.1715 +        if (mDNSRequest)  // only send this if we actually resolved anything
  1.1716 +            SendStatus(NS_NET_STATUS_RESOLVED_HOST);
  1.1717 +
  1.1718 +        SOCKET_LOG(("  MSG_DNS_LOOKUP_COMPLETE\n"));
  1.1719 +        mDNSRequest = 0;
  1.1720 +        if (param) {
  1.1721 +            mDNSRecord = static_cast<nsIDNSRecord *>(param);
  1.1722 +            mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
  1.1723 +        }
  1.1724 +        // status contains DNS lookup status
  1.1725 +        if (NS_FAILED(status)) {
  1.1726 +            // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP 
  1.1727 +            // proxy host is not found, so we fixup the error code.
  1.1728 +            // For SOCKS proxies (mProxyTransparent == true), the socket 
  1.1729 +            // transport resolves the real host here, so there's no fixup 
  1.1730 +            // (see bug 226943).
  1.1731 +            if (status == NS_ERROR_UNKNOWN_HOST && !mProxyTransparent &&
  1.1732 +                mProxyUse)
  1.1733 +                mCondition = NS_ERROR_UNKNOWN_PROXY_HOST;
  1.1734 +            else
  1.1735 +                mCondition = status;
  1.1736 +        }
  1.1737 +        else if (mState == STATE_RESOLVING)
  1.1738 +            mCondition = InitiateSocket();
  1.1739 +        break;
  1.1740 +
  1.1741 +    case MSG_RETRY_INIT_SOCKET:
  1.1742 +        mCondition = InitiateSocket();
  1.1743 +        break;
  1.1744 +
  1.1745 +    case MSG_INPUT_CLOSED:
  1.1746 +        SOCKET_LOG(("  MSG_INPUT_CLOSED\n"));
  1.1747 +        OnMsgInputClosed(status);
  1.1748 +        break;
  1.1749 +
  1.1750 +    case MSG_INPUT_PENDING:
  1.1751 +        SOCKET_LOG(("  MSG_INPUT_PENDING\n"));
  1.1752 +        OnMsgInputPending();
  1.1753 +        break;
  1.1754 +
  1.1755 +    case MSG_OUTPUT_CLOSED:
  1.1756 +        SOCKET_LOG(("  MSG_OUTPUT_CLOSED\n"));
  1.1757 +        OnMsgOutputClosed(status);
  1.1758 +        break;
  1.1759 +
  1.1760 +    case MSG_OUTPUT_PENDING:
  1.1761 +        SOCKET_LOG(("  MSG_OUTPUT_PENDING\n"));
  1.1762 +        OnMsgOutputPending();
  1.1763 +        break;
  1.1764 +    case MSG_TIMEOUT_CHANGED:
  1.1765 +        SOCKET_LOG(("  MSG_TIMEOUT_CHANGED\n"));
  1.1766 +        mPollTimeout = mTimeouts[(mState == STATE_TRANSFERRING)
  1.1767 +          ? TIMEOUT_READ_WRITE : TIMEOUT_CONNECT];
  1.1768 +        break;
  1.1769 +    default:
  1.1770 +        SOCKET_LOG(("  unhandled event!\n"));
  1.1771 +    }
  1.1772 +    
  1.1773 +    if (NS_FAILED(mCondition)) {
  1.1774 +        SOCKET_LOG(("  after event [this=%p cond=%x]\n", this, mCondition));
  1.1775 +        if (!mAttached) // need to process this error ourselves...
  1.1776 +            OnSocketDetached(nullptr);
  1.1777 +    }
  1.1778 +    else if (mPollFlags == PR_POLL_EXCEPT)
  1.1779 +        mPollFlags = 0; // make idle
  1.1780 +}
  1.1781 +
  1.1782 +//-----------------------------------------------------------------------------
  1.1783 +// socket handler impl
  1.1784 +
  1.1785 +void
  1.1786 +nsSocketTransport::OnSocketReady(PRFileDesc *fd, int16_t outFlags)
  1.1787 +{
  1.1788 +    SOCKET_LOG(("nsSocketTransport::OnSocketReady [this=%p outFlags=%hd]\n",
  1.1789 +        this, outFlags));
  1.1790 +
  1.1791 +    if (outFlags == -1) {
  1.1792 +        SOCKET_LOG(("socket timeout expired\n"));
  1.1793 +        mCondition = NS_ERROR_NET_TIMEOUT;
  1.1794 +        return;
  1.1795 +    }
  1.1796 +
  1.1797 +    if (mState == STATE_TRANSFERRING) {
  1.1798 +        // if waiting to write and socket is writable or hit an exception.
  1.1799 +        if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
  1.1800 +            // assume that we won't need to poll any longer (the stream will
  1.1801 +            // request that we poll again if it is still pending).
  1.1802 +            mPollFlags &= ~PR_POLL_WRITE;
  1.1803 +            mOutput.OnSocketReady(NS_OK);
  1.1804 +        }
  1.1805 +        // if waiting to read and socket is readable or hit an exception.
  1.1806 +        if ((mPollFlags & PR_POLL_READ) && (outFlags & ~PR_POLL_WRITE)) {
  1.1807 +            // assume that we won't need to poll any longer (the stream will
  1.1808 +            // request that we poll again if it is still pending).
  1.1809 +            mPollFlags &= ~PR_POLL_READ;
  1.1810 +            mInput.OnSocketReady(NS_OK);
  1.1811 +        }
  1.1812 +        // Update poll timeout in case it was changed
  1.1813 +        mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
  1.1814 +    }
  1.1815 +
  1.1816 +//STATE_SENDINGGET: handshake proceeded to state "sent connect"
  1.1817 +//one more poll to OnSocketReady will trigger the get request, and state STATE_SENTGET
  1.1818 +//STATE_SENTGET: continue and finish handshake
  1.1819 +    else if (mState == STATE_SENDINGGET) {
  1.1820 +        if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
  1.1821 +            mOutput.OnSocketReady(NS_OK);
  1.1822 +        }
  1.1823 +        mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
  1.1824 +        mState = STATE_SENTGET;
  1.1825 +    }
  1.1826 +
  1.1827 +    else if (mState == STATE_CONNECTING || mState == STATE_SENTGET) {
  1.1828 +        PRStatus status = PR_ConnectContinue(fd, outFlags);
  1.1829 +        if (status == PR_SUCCESS && mState == STATE_CONNECTING) {
  1.1830 +            OnSocketConnected();
  1.1831 +            mState = STATE_SENDINGGET;
  1.1832 +        }
  1.1833 +        else if (status == PR_SUCCESS && mState == STATE_SENTGET) {
  1.1834 +            //
  1.1835 +            // we are connected!
  1.1836 +            //
  1.1837 +            OnSocketConnected();
  1.1838 +        }
  1.1839 +        else {
  1.1840 +            PRErrorCode code = PR_GetError();
  1.1841 +#if defined(TEST_CONNECT_ERRORS)
  1.1842 +            code = RandomizeConnectError(code);
  1.1843 +#endif
  1.1844 +            //
  1.1845 +            // If the connect is still not ready, then continue polling...
  1.1846 +            //
  1.1847 +            if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
  1.1848 +                // Set up the select flags for connect...
  1.1849 +                mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
  1.1850 +                // Update poll timeout in case it was changed
  1.1851 +                mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
  1.1852 +            }
  1.1853 +            //
  1.1854 +            // The SOCKS proxy rejected our request. Find out why.
  1.1855 +            //
  1.1856 +            else if (PR_UNKNOWN_ERROR == code &&
  1.1857 +                     mProxyUse && mProxyTransparent) {
  1.1858 +                code = PR_GetOSError();
  1.1859 +                mCondition = ErrorAccordingToNSPR(code);
  1.1860 +            }
  1.1861 +            else {
  1.1862 +                //
  1.1863 +                // else, the connection failed...
  1.1864 +                //
  1.1865 +                mCondition = ErrorAccordingToNSPR(code);
  1.1866 +                if (mCondition == NS_ERROR_CONNECTION_REFUSED && mProxyUse)
  1.1867 +                    mCondition = NS_ERROR_PROXY_CONNECTION_REFUSED;
  1.1868 +                SOCKET_LOG(("  connection failed! [reason=%x]\n", mCondition));
  1.1869 +            }
  1.1870 +        }
  1.1871 +    }
  1.1872 +    else {
  1.1873 +        NS_ERROR("unexpected socket state");
  1.1874 +        mCondition = NS_ERROR_UNEXPECTED;
  1.1875 +    }
  1.1876 +
  1.1877 +    if (mPollFlags == PR_POLL_EXCEPT)
  1.1878 +        mPollFlags = 0; // make idle
  1.1879 +}
  1.1880 +
  1.1881 +// called on the socket thread only
  1.1882 +void
  1.1883 +nsSocketTransport::OnSocketDetached(PRFileDesc *fd)
  1.1884 +{
  1.1885 +    SOCKET_LOG(("nsSocketTransport::OnSocketDetached [this=%p cond=%x]\n",
  1.1886 +        this, mCondition));
  1.1887 +
  1.1888 +    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1.1889 +
  1.1890 +    // if we didn't initiate this detach, then be sure to pass an error
  1.1891 +    // condition up to our consumers.  (e.g., STS is shutting down.)
  1.1892 +    if (NS_SUCCEEDED(mCondition)) {
  1.1893 +        if (gIOService->IsOffline()) {
  1.1894 +          mCondition = NS_ERROR_OFFLINE;
  1.1895 +        }
  1.1896 +        else {
  1.1897 +          mCondition = NS_ERROR_ABORT;
  1.1898 +        }
  1.1899 +    }
  1.1900 +
  1.1901 +    if (RecoverFromError())
  1.1902 +        mCondition = NS_OK;
  1.1903 +    else {
  1.1904 +        mState = STATE_CLOSED;
  1.1905 +
  1.1906 +        // make sure there isn't any pending DNS request
  1.1907 +        if (mDNSRequest) {
  1.1908 +            mDNSRequest->Cancel(NS_ERROR_ABORT);
  1.1909 +            mDNSRequest = 0;
  1.1910 +        }
  1.1911 +
  1.1912 +        //
  1.1913 +        // notify input/output streams
  1.1914 +        //
  1.1915 +        mInput.OnSocketReady(mCondition);
  1.1916 +        mOutput.OnSocketReady(mCondition);
  1.1917 +    }
  1.1918 +
  1.1919 +    // break any potential reference cycle between the security info object
  1.1920 +    // and ourselves by resetting its notification callbacks object.  see
  1.1921 +    // bug 285991 for details.
  1.1922 +    nsCOMPtr<nsISSLSocketControl> secCtrl = do_QueryInterface(mSecInfo);
  1.1923 +    if (secCtrl)
  1.1924 +        secCtrl->SetNotificationCallbacks(nullptr);
  1.1925 +
  1.1926 +    // finally, release our reference to the socket (must do this within
  1.1927 +    // the transport lock) possibly closing the socket. Also release our
  1.1928 +    // listeners to break potential refcount cycles.
  1.1929 +
  1.1930 +    // We should be careful not to release mEventSink and mCallbacks while
  1.1931 +    // we're locked, because releasing it might require acquiring the lock
  1.1932 +    // again, so we just null out mEventSink and mCallbacks while we're
  1.1933 +    // holding the lock, and let the stack based objects' destuctors take
  1.1934 +    // care of destroying it if needed.
  1.1935 +    nsCOMPtr<nsIInterfaceRequestor> ourCallbacks;
  1.1936 +    nsCOMPtr<nsITransportEventSink> ourEventSink;
  1.1937 +    {
  1.1938 +        MutexAutoLock lock(mLock);
  1.1939 +        if (mFD.IsInitialized()) {
  1.1940 +            ReleaseFD_Locked(mFD);
  1.1941 +            // flag mFD as unusable; this prevents other consumers from 
  1.1942 +            // acquiring a reference to mFD.
  1.1943 +            mFDconnected = false;
  1.1944 +        }
  1.1945 +
  1.1946 +        // We must release mCallbacks and mEventSink to avoid memory leak
  1.1947 +        // but only when RecoverFromError() above failed. Otherwise we lose
  1.1948 +        // link with UI and security callbacks on next connection attempt 
  1.1949 +        // round. That would lead e.g. to a broken certificate exception page.
  1.1950 +        if (NS_FAILED(mCondition)) {
  1.1951 +            mCallbacks.swap(ourCallbacks);
  1.1952 +            mEventSink.swap(ourEventSink);
  1.1953 +        }
  1.1954 +    }
  1.1955 +}
  1.1956 +
  1.1957 +void
  1.1958 +nsSocketTransport::IsLocal(bool *aIsLocal)
  1.1959 +{
  1.1960 +    {
  1.1961 +        MutexAutoLock lock(mLock);
  1.1962 +
  1.1963 +#if defined(XP_UNIX)
  1.1964 +        // Unix-domain sockets are always local.
  1.1965 +        if (mNetAddr.raw.family == PR_AF_LOCAL)
  1.1966 +        {
  1.1967 +            *aIsLocal = true;
  1.1968 +            return;
  1.1969 +        }
  1.1970 +#endif
  1.1971 +
  1.1972 +        *aIsLocal = IsLoopBackAddress(&mNetAddr);
  1.1973 +    }
  1.1974 +}
  1.1975 +
  1.1976 +//-----------------------------------------------------------------------------
  1.1977 +// xpcom api
  1.1978 +
  1.1979 +NS_IMPL_ISUPPORTS(nsSocketTransport,
  1.1980 +                  nsISocketTransport,
  1.1981 +                  nsITransport,
  1.1982 +                  nsIDNSListener,
  1.1983 +                  nsIClassInfo)
  1.1984 +NS_IMPL_CI_INTERFACE_GETTER(nsSocketTransport,
  1.1985 +                            nsISocketTransport,
  1.1986 +                            nsITransport,
  1.1987 +                            nsIDNSListener)
  1.1988 +
  1.1989 +NS_IMETHODIMP
  1.1990 +nsSocketTransport::OpenInputStream(uint32_t flags,
  1.1991 +                                   uint32_t segsize,
  1.1992 +                                   uint32_t segcount,
  1.1993 +                                   nsIInputStream **result)
  1.1994 +{
  1.1995 +    SOCKET_LOG(("nsSocketTransport::OpenInputStream [this=%p flags=%x]\n",
  1.1996 +        this, flags));
  1.1997 +
  1.1998 +    NS_ENSURE_TRUE(!mInput.IsReferenced(), NS_ERROR_UNEXPECTED);
  1.1999 +
  1.2000 +    nsresult rv;
  1.2001 +    nsCOMPtr<nsIAsyncInputStream> pipeIn;
  1.2002 +
  1.2003 +    if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
  1.2004 +        // XXX if the caller wants blocking, then the caller also gets buffered!
  1.2005 +        //bool openBuffered = !(flags & OPEN_UNBUFFERED);
  1.2006 +        bool openBlocking =  (flags & OPEN_BLOCKING);
  1.2007 +
  1.2008 +        net_ResolveSegmentParams(segsize, segcount);
  1.2009 +
  1.2010 +        // create a pipe
  1.2011 +        nsCOMPtr<nsIAsyncOutputStream> pipeOut;
  1.2012 +        rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
  1.2013 +                         !openBlocking, true, segsize, segcount);
  1.2014 +        if (NS_FAILED(rv)) return rv;
  1.2015 +
  1.2016 +        // async copy from socket to pipe
  1.2017 +        rv = NS_AsyncCopy(&mInput, pipeOut, mSocketTransportService,
  1.2018 +                          NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
  1.2019 +        if (NS_FAILED(rv)) return rv;
  1.2020 +
  1.2021 +        *result = pipeIn;
  1.2022 +    }
  1.2023 +    else
  1.2024 +        *result = &mInput;
  1.2025 +
  1.2026 +    // flag input stream as open
  1.2027 +    mInputClosed = false;
  1.2028 +
  1.2029 +    rv = PostEvent(MSG_ENSURE_CONNECT);
  1.2030 +    if (NS_FAILED(rv)) return rv;
  1.2031 +
  1.2032 +    NS_ADDREF(*result);
  1.2033 +    return NS_OK;
  1.2034 +}
  1.2035 +
  1.2036 +NS_IMETHODIMP
  1.2037 +nsSocketTransport::OpenOutputStream(uint32_t flags,
  1.2038 +                                    uint32_t segsize,
  1.2039 +                                    uint32_t segcount,
  1.2040 +                                    nsIOutputStream **result)
  1.2041 +{
  1.2042 +    SOCKET_LOG(("nsSocketTransport::OpenOutputStream [this=%p flags=%x]\n",
  1.2043 +        this, flags));
  1.2044 +
  1.2045 +    NS_ENSURE_TRUE(!mOutput.IsReferenced(), NS_ERROR_UNEXPECTED);
  1.2046 +
  1.2047 +    nsresult rv;
  1.2048 +    nsCOMPtr<nsIAsyncOutputStream> pipeOut;
  1.2049 +    if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
  1.2050 +        // XXX if the caller wants blocking, then the caller also gets buffered!
  1.2051 +        //bool openBuffered = !(flags & OPEN_UNBUFFERED);
  1.2052 +        bool openBlocking =  (flags & OPEN_BLOCKING);
  1.2053 +
  1.2054 +        net_ResolveSegmentParams(segsize, segcount);
  1.2055 +
  1.2056 +        // create a pipe
  1.2057 +        nsCOMPtr<nsIAsyncInputStream> pipeIn;
  1.2058 +        rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
  1.2059 +                         true, !openBlocking, segsize, segcount);
  1.2060 +        if (NS_FAILED(rv)) return rv;
  1.2061 +
  1.2062 +        // async copy from socket to pipe
  1.2063 +        rv = NS_AsyncCopy(pipeIn, &mOutput, mSocketTransportService,
  1.2064 +                          NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
  1.2065 +        if (NS_FAILED(rv)) return rv;
  1.2066 +
  1.2067 +        *result = pipeOut;
  1.2068 +    }
  1.2069 +    else
  1.2070 +        *result = &mOutput;
  1.2071 +
  1.2072 +    // flag output stream as open
  1.2073 +    mOutputClosed = false;
  1.2074 +
  1.2075 +    rv = PostEvent(MSG_ENSURE_CONNECT);
  1.2076 +    if (NS_FAILED(rv)) return rv;
  1.2077 +
  1.2078 +    NS_ADDREF(*result);
  1.2079 +    return NS_OK;
  1.2080 +}
  1.2081 +
  1.2082 +NS_IMETHODIMP
  1.2083 +nsSocketTransport::Close(nsresult reason)
  1.2084 +{
  1.2085 +    if (NS_SUCCEEDED(reason))
  1.2086 +        reason = NS_BASE_STREAM_CLOSED;
  1.2087 +
  1.2088 +    mInput.CloseWithStatus(reason);
  1.2089 +    mOutput.CloseWithStatus(reason);
  1.2090 +    return NS_OK;
  1.2091 +}
  1.2092 +
  1.2093 +NS_IMETHODIMP
  1.2094 +nsSocketTransport::GetSecurityInfo(nsISupports **secinfo)
  1.2095 +{
  1.2096 +    MutexAutoLock lock(mLock);
  1.2097 +    NS_IF_ADDREF(*secinfo = mSecInfo);
  1.2098 +    return NS_OK;
  1.2099 +}
  1.2100 +
  1.2101 +NS_IMETHODIMP
  1.2102 +nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor **callbacks)
  1.2103 +{
  1.2104 +    MutexAutoLock lock(mLock);
  1.2105 +    NS_IF_ADDREF(*callbacks = mCallbacks);
  1.2106 +    return NS_OK;
  1.2107 +}
  1.2108 +
  1.2109 +NS_IMETHODIMP
  1.2110 +nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor *callbacks)
  1.2111 +{
  1.2112 +    nsCOMPtr<nsIInterfaceRequestor> threadsafeCallbacks;
  1.2113 +    NS_NewNotificationCallbacksAggregation(callbacks, nullptr,
  1.2114 +                                           NS_GetCurrentThread(),
  1.2115 +                                           getter_AddRefs(threadsafeCallbacks));
  1.2116 +
  1.2117 +    nsCOMPtr<nsISupports> secinfo;
  1.2118 +    {
  1.2119 +        MutexAutoLock lock(mLock);
  1.2120 +        mCallbacks = threadsafeCallbacks;
  1.2121 +        SOCKET_LOG(("Reset callbacks for secinfo=%p callbacks=%p\n",
  1.2122 +                    mSecInfo.get(), mCallbacks.get()));
  1.2123 +
  1.2124 +        secinfo = mSecInfo;
  1.2125 +    }
  1.2126 +
  1.2127 +    // don't call into PSM while holding mLock!!
  1.2128 +    nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
  1.2129 +    if (secCtrl)
  1.2130 +        secCtrl->SetNotificationCallbacks(threadsafeCallbacks);
  1.2131 +
  1.2132 +    return NS_OK;
  1.2133 +}
  1.2134 +
  1.2135 +NS_IMETHODIMP
  1.2136 +nsSocketTransport::SetEventSink(nsITransportEventSink *sink,
  1.2137 +                                nsIEventTarget *target)
  1.2138 +{
  1.2139 +    nsCOMPtr<nsITransportEventSink> temp;
  1.2140 +    if (target) {
  1.2141 +        nsresult rv = net_NewTransportEventSinkProxy(getter_AddRefs(temp),
  1.2142 +                                                     sink, target);
  1.2143 +        if (NS_FAILED(rv))
  1.2144 +            return rv;
  1.2145 +        sink = temp.get();
  1.2146 +    }
  1.2147 +
  1.2148 +    MutexAutoLock lock(mLock);
  1.2149 +    mEventSink = sink;
  1.2150 +    return NS_OK;
  1.2151 +}
  1.2152 +
  1.2153 +NS_IMETHODIMP
  1.2154 +nsSocketTransport::IsAlive(bool *result)
  1.2155 +{
  1.2156 +    *result = false;
  1.2157 +
  1.2158 +    nsresult conditionWhileLocked = NS_OK;
  1.2159 +    PRFileDescAutoLock fd(this, &conditionWhileLocked);
  1.2160 +    if (NS_FAILED(conditionWhileLocked) || !fd.IsInitialized()) {
  1.2161 +        return NS_OK;
  1.2162 +    }
  1.2163 +
  1.2164 +    // XXX do some idle-time based checks??
  1.2165 +
  1.2166 +    char c;
  1.2167 +    int32_t rval = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
  1.2168 +
  1.2169 +    if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR))
  1.2170 +        *result = true;
  1.2171 +
  1.2172 +    return NS_OK;
  1.2173 +}
  1.2174 +
  1.2175 +NS_IMETHODIMP
  1.2176 +nsSocketTransport::GetHost(nsACString &host)
  1.2177 +{
  1.2178 +    host = SocketHost();
  1.2179 +    return NS_OK;
  1.2180 +}
  1.2181 +
  1.2182 +NS_IMETHODIMP
  1.2183 +nsSocketTransport::GetPort(int32_t *port)
  1.2184 +{
  1.2185 +    *port = (int32_t) SocketPort();
  1.2186 +    return NS_OK;
  1.2187 +}
  1.2188 +
  1.2189 +const nsCString &
  1.2190 +nsSocketTransport::SocketHost()
  1.2191 +{
  1.2192 +  if (mProxyInfo && !mProxyTransparent) {
  1.2193 +    if (mProxyHostCache.IsEmpty()) {
  1.2194 +      mProxyInfo->GetHost(mProxyHostCache);
  1.2195 +    }
  1.2196 +    return mProxyHostCache;
  1.2197 +  }
  1.2198 +  else
  1.2199 +    return mHost;
  1.2200 +}
  1.2201 +
  1.2202 +uint16_t
  1.2203 +nsSocketTransport::SocketPort()
  1.2204 +{
  1.2205 +  if (mProxyInfo && !mProxyTransparent) {
  1.2206 +    int32_t result;
  1.2207 +    mProxyInfo->GetPort(&result);
  1.2208 +    return (uint16_t) result;
  1.2209 +  }
  1.2210 +  else
  1.2211 +    return mPort;
  1.2212 +}
  1.2213 +
  1.2214 +NS_IMETHODIMP
  1.2215 +nsSocketTransport::GetPeerAddr(NetAddr *addr)
  1.2216 +{
  1.2217 +    // once we are in the connected state, mNetAddr will not change.
  1.2218 +    // so if we can verify that we are in the connected state, then
  1.2219 +    // we can freely access mNetAddr from any thread without being
  1.2220 +    // inside a critical section.
  1.2221 +
  1.2222 +    if (!mNetAddrIsSet) {
  1.2223 +        SOCKET_LOG(("nsSocketTransport::GetPeerAddr [this=%p state=%d] "
  1.2224 +                    "NOT_AVAILABLE because not yet connected.", this, mState));
  1.2225 +        return NS_ERROR_NOT_AVAILABLE;
  1.2226 +    }
  1.2227 +
  1.2228 +    memcpy(addr, &mNetAddr, sizeof(NetAddr));
  1.2229 +    return NS_OK;
  1.2230 +}
  1.2231 +
  1.2232 +NS_IMETHODIMP
  1.2233 +nsSocketTransport::GetSelfAddr(NetAddr *addr)
  1.2234 +{
  1.2235 +    // we must not call any PR methods on our file descriptor
  1.2236 +    // while holding mLock since those methods might re-enter
  1.2237 +    // socket transport code.
  1.2238 +
  1.2239 +    PRFileDescAutoLock fd(this);
  1.2240 +    if (!fd.IsInitialized()) {
  1.2241 +        return NS_ERROR_NOT_CONNECTED;
  1.2242 +    }
  1.2243 +
  1.2244 +    PRNetAddr prAddr;
  1.2245 +
  1.2246 +    // NSPR doesn't tell us the socket address's length (as provided by
  1.2247 +    // the 'getsockname' system call), so we can't distinguish between
  1.2248 +    // named, unnamed, and abstract Unix domain socket names. (Server
  1.2249 +    // sockets are never unnamed, obviously, but client sockets can use
  1.2250 +    // any kind of address.) Clear prAddr first, so that the path for
  1.2251 +    // unnamed and abstract addresses will at least be reliably empty,
  1.2252 +    // and not garbage for unnamed sockets.
  1.2253 +    memset(&prAddr, 0, sizeof(prAddr));
  1.2254 +
  1.2255 +    nsresult rv =
  1.2256 +        (PR_GetSockName(fd, &prAddr) == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
  1.2257 +    PRNetAddrToNetAddr(&prAddr, addr);
  1.2258 +
  1.2259 +    return rv;
  1.2260 +}
  1.2261 +
  1.2262 +/* nsINetAddr getScriptablePeerAddr (); */
  1.2263 +NS_IMETHODIMP
  1.2264 +nsSocketTransport::GetScriptablePeerAddr(nsINetAddr * *addr)
  1.2265 +{
  1.2266 +    NetAddr rawAddr;
  1.2267 +
  1.2268 +    nsresult rv;
  1.2269 +    rv = GetPeerAddr(&rawAddr);
  1.2270 +    if (NS_FAILED(rv))
  1.2271 +        return rv;
  1.2272 +
  1.2273 +    NS_ADDREF(*addr = new nsNetAddr(&rawAddr));
  1.2274 +
  1.2275 +    return NS_OK;
  1.2276 +}
  1.2277 +
  1.2278 +/* nsINetAddr getScriptableSelfAddr (); */
  1.2279 +NS_IMETHODIMP
  1.2280 +nsSocketTransport::GetScriptableSelfAddr(nsINetAddr * *addr)
  1.2281 +{
  1.2282 +    NetAddr rawAddr;
  1.2283 +
  1.2284 +    nsresult rv;
  1.2285 +    rv = GetSelfAddr(&rawAddr);
  1.2286 +    if (NS_FAILED(rv))
  1.2287 +        return rv;
  1.2288 +
  1.2289 +    NS_ADDREF(*addr = new nsNetAddr(&rawAddr));
  1.2290 +
  1.2291 +    return NS_OK;
  1.2292 +}
  1.2293 +
  1.2294 +NS_IMETHODIMP
  1.2295 +nsSocketTransport::GetTimeout(uint32_t type, uint32_t *value)
  1.2296 +{
  1.2297 +    NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
  1.2298 +    *value = (uint32_t) mTimeouts[type];
  1.2299 +    return NS_OK;
  1.2300 +}
  1.2301 +
  1.2302 +NS_IMETHODIMP
  1.2303 +nsSocketTransport::SetTimeout(uint32_t type, uint32_t value)
  1.2304 +{
  1.2305 +    NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
  1.2306 +    // truncate overly large timeout values.
  1.2307 +    mTimeouts[type] = (uint16_t) std::min<uint32_t>(value, UINT16_MAX);
  1.2308 +    PostEvent(MSG_TIMEOUT_CHANGED);
  1.2309 +    return NS_OK;
  1.2310 +}
  1.2311 +
  1.2312 +NS_IMETHODIMP
  1.2313 +nsSocketTransport::SetQoSBits(uint8_t aQoSBits)
  1.2314 +{
  1.2315 +    // Don't do any checking here of bits.  Why?  Because as of RFC-4594
  1.2316 +    // several different Class Selector and Assured Forwarding values
  1.2317 +    // have been defined, but that isn't to say more won't be added later.
  1.2318 +    // In that case, any checking would be an impediment to interoperating
  1.2319 +    // with newer QoS definitions.
  1.2320 +
  1.2321 +    mQoSBits = aQoSBits;
  1.2322 +    return NS_OK;
  1.2323 +}
  1.2324 +
  1.2325 +NS_IMETHODIMP
  1.2326 +nsSocketTransport::GetQoSBits(uint8_t *aQoSBits)
  1.2327 +{
  1.2328 +    *aQoSBits = mQoSBits;
  1.2329 +    return NS_OK;
  1.2330 +}
  1.2331 +
  1.2332 +NS_IMETHODIMP
  1.2333 +nsSocketTransport::GetRecvBufferSize(uint32_t *aSize)
  1.2334 +{
  1.2335 +    PRFileDescAutoLock fd(this);
  1.2336 +    if (!fd.IsInitialized())
  1.2337 +        return NS_ERROR_NOT_CONNECTED;
  1.2338 +
  1.2339 +    nsresult rv = NS_OK;
  1.2340 +    PRSocketOptionData opt;
  1.2341 +    opt.option = PR_SockOpt_RecvBufferSize;
  1.2342 +    if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS)
  1.2343 +        *aSize = opt.value.recv_buffer_size;
  1.2344 +    else
  1.2345 +        rv = NS_ERROR_FAILURE;
  1.2346 +
  1.2347 +    return rv;
  1.2348 +}
  1.2349 +
  1.2350 +NS_IMETHODIMP
  1.2351 +nsSocketTransport::GetSendBufferSize(uint32_t *aSize)
  1.2352 +{
  1.2353 +    PRFileDescAutoLock fd(this);
  1.2354 +    if (!fd.IsInitialized())
  1.2355 +        return NS_ERROR_NOT_CONNECTED;
  1.2356 +
  1.2357 +    nsresult rv = NS_OK;
  1.2358 +    PRSocketOptionData opt;
  1.2359 +    opt.option = PR_SockOpt_SendBufferSize;
  1.2360 +    if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS)
  1.2361 +        *aSize = opt.value.send_buffer_size;
  1.2362 +    else
  1.2363 +        rv = NS_ERROR_FAILURE;
  1.2364 +
  1.2365 +    return rv;
  1.2366 +}
  1.2367 +
  1.2368 +NS_IMETHODIMP
  1.2369 +nsSocketTransport::SetRecvBufferSize(uint32_t aSize)
  1.2370 +{
  1.2371 +    PRFileDescAutoLock fd(this);
  1.2372 +    if (!fd.IsInitialized())
  1.2373 +        return NS_ERROR_NOT_CONNECTED;
  1.2374 +
  1.2375 +    nsresult rv = NS_OK;
  1.2376 +    PRSocketOptionData opt;
  1.2377 +    opt.option = PR_SockOpt_RecvBufferSize;
  1.2378 +    opt.value.recv_buffer_size = aSize;
  1.2379 +    if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS)
  1.2380 +        rv = NS_ERROR_FAILURE;
  1.2381 +
  1.2382 +    return rv;
  1.2383 +}
  1.2384 +
  1.2385 +NS_IMETHODIMP
  1.2386 +nsSocketTransport::SetSendBufferSize(uint32_t aSize)
  1.2387 +{
  1.2388 +    PRFileDescAutoLock fd(this);
  1.2389 +    if (!fd.IsInitialized())
  1.2390 +        return NS_ERROR_NOT_CONNECTED;
  1.2391 +
  1.2392 +    nsresult rv = NS_OK;
  1.2393 +    PRSocketOptionData opt;
  1.2394 +    opt.option = PR_SockOpt_SendBufferSize;
  1.2395 +    opt.value.send_buffer_size = aSize;
  1.2396 +    if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS)
  1.2397 +        rv = NS_ERROR_FAILURE;
  1.2398 +
  1.2399 +    return rv;
  1.2400 +}
  1.2401 +
  1.2402 +NS_IMETHODIMP
  1.2403 +nsSocketTransport::OnLookupComplete(nsICancelable *request,
  1.2404 +                                    nsIDNSRecord  *rec,
  1.2405 +                                    nsresult       status)
  1.2406 +{
  1.2407 +    // flag host lookup complete for the benefit of the ResolveHost method.
  1.2408 +    mResolving = false;
  1.2409 +
  1.2410 +    MOZ_EVENT_TRACER_WAIT(this, "net::tcp::connect");
  1.2411 +    nsresult rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, status, rec);
  1.2412 +
  1.2413 +    // if posting a message fails, then we should assume that the socket
  1.2414 +    // transport has been shutdown.  this should never happen!  if it does
  1.2415 +    // it means that the socket transport service was shutdown before the
  1.2416 +    // DNS service.
  1.2417 +    if (NS_FAILED(rv))
  1.2418 +        NS_WARNING("unable to post DNS lookup complete message");
  1.2419 +
  1.2420 +    return NS_OK;
  1.2421 +}
  1.2422 +
  1.2423 +NS_IMETHODIMP
  1.2424 +nsSocketTransport::GetInterfaces(uint32_t *count, nsIID * **array)
  1.2425 +{
  1.2426 +    return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(count, array);
  1.2427 +}
  1.2428 +
  1.2429 +NS_IMETHODIMP
  1.2430 +nsSocketTransport::GetHelperForLanguage(uint32_t language, nsISupports **_retval)
  1.2431 +{
  1.2432 +    *_retval = nullptr;
  1.2433 +    return NS_OK;
  1.2434 +}
  1.2435 +
  1.2436 +NS_IMETHODIMP
  1.2437 +nsSocketTransport::GetContractID(char * *aContractID)
  1.2438 +{
  1.2439 +    *aContractID = nullptr;
  1.2440 +    return NS_OK;
  1.2441 +}
  1.2442 +
  1.2443 +NS_IMETHODIMP
  1.2444 +nsSocketTransport::GetClassDescription(char * *aClassDescription)
  1.2445 +{
  1.2446 +    *aClassDescription = nullptr;
  1.2447 +    return NS_OK;
  1.2448 +}
  1.2449 +
  1.2450 +NS_IMETHODIMP
  1.2451 +nsSocketTransport::GetClassID(nsCID * *aClassID)
  1.2452 +{
  1.2453 +    *aClassID = nullptr;
  1.2454 +    return NS_OK;
  1.2455 +}
  1.2456 +
  1.2457 +NS_IMETHODIMP
  1.2458 +nsSocketTransport::GetImplementationLanguage(uint32_t *aImplementationLanguage)
  1.2459 +{
  1.2460 +    *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
  1.2461 +    return NS_OK;
  1.2462 +}
  1.2463 +
  1.2464 +NS_IMETHODIMP
  1.2465 +nsSocketTransport::GetFlags(uint32_t *aFlags)
  1.2466 +{
  1.2467 +    *aFlags = nsIClassInfo::THREADSAFE;
  1.2468 +    return NS_OK;
  1.2469 +}
  1.2470 +
  1.2471 +NS_IMETHODIMP
  1.2472 +nsSocketTransport::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
  1.2473 +{
  1.2474 +    return NS_ERROR_NOT_AVAILABLE;
  1.2475 +}
  1.2476 +
  1.2477 +
  1.2478 +NS_IMETHODIMP
  1.2479 +nsSocketTransport::GetConnectionFlags(uint32_t *value)
  1.2480 +{
  1.2481 +    *value = mConnectionFlags;
  1.2482 +    return NS_OK;
  1.2483 +}
  1.2484 +
  1.2485 +NS_IMETHODIMP
  1.2486 +nsSocketTransport::SetConnectionFlags(uint32_t value)
  1.2487 +{
  1.2488 +    mConnectionFlags = value;
  1.2489 +    mIsPrivate = value & nsISocketTransport::NO_PERMANENT_STORAGE;
  1.2490 +    return NS_OK;
  1.2491 +}
  1.2492 +
  1.2493 +void
  1.2494 +nsSocketTransport::OnKeepaliveEnabledPrefChange(bool aEnabled)
  1.2495 +{
  1.2496 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1.2497 +
  1.2498 +    // The global pref toggles keepalive as a system feature; it only affects
  1.2499 +    // an individual socket if keepalive has been specifically enabled for it.
  1.2500 +    // So, ensure keepalive is configured correctly if previously enabled.
  1.2501 +    if (mKeepaliveEnabled) {
  1.2502 +        nsresult rv = SetKeepaliveEnabledInternal(aEnabled);
  1.2503 +        if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2504 +            SOCKET_LOG(("  SetKeepaliveEnabledInternal [%s] failed rv[0x%x]",
  1.2505 +                        aEnabled ? "enable" : "disable", rv));
  1.2506 +        }
  1.2507 +    }
  1.2508 +}
  1.2509 +
  1.2510 +nsresult
  1.2511 +nsSocketTransport::SetKeepaliveEnabledInternal(bool aEnable)
  1.2512 +{
  1.2513 +    MOZ_ASSERT(mKeepaliveIdleTimeS > 0 &&
  1.2514 +               mKeepaliveIdleTimeS <= kMaxTCPKeepIdle);
  1.2515 +    MOZ_ASSERT(mKeepaliveRetryIntervalS > 0 &&
  1.2516 +               mKeepaliveRetryIntervalS <= kMaxTCPKeepIntvl);
  1.2517 +    MOZ_ASSERT(mKeepaliveProbeCount > 0 &&
  1.2518 +               mKeepaliveProbeCount <= kMaxTCPKeepCount);
  1.2519 +
  1.2520 +    PRFileDescAutoLock fd(this);
  1.2521 +    if (NS_WARN_IF(!fd.IsInitialized())) {
  1.2522 +        return NS_ERROR_NOT_INITIALIZED;
  1.2523 +    }
  1.2524 +
  1.2525 +    // Only enable if keepalives are globally enabled, but ensure other
  1.2526 +    // options are set correctly on the fd.
  1.2527 +    bool enable = aEnable && mSocketTransportService->IsKeepaliveEnabled();
  1.2528 +    nsresult rv = fd.SetKeepaliveVals(enable,
  1.2529 +                                      mKeepaliveIdleTimeS,
  1.2530 +                                      mKeepaliveRetryIntervalS,
  1.2531 +                                      mKeepaliveProbeCount);
  1.2532 +    if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2533 +        SOCKET_LOG(("  SetKeepaliveVals failed rv[0x%x]", rv));
  1.2534 +        return rv;
  1.2535 +    }
  1.2536 +    rv = fd.SetKeepaliveEnabled(enable);
  1.2537 +    if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2538 +        SOCKET_LOG(("  SetKeepaliveEnabled failed rv[0x%x]", rv));
  1.2539 +        return rv;
  1.2540 +    }
  1.2541 +    return NS_OK;
  1.2542 +}
  1.2543 +
  1.2544 +NS_IMETHODIMP
  1.2545 +nsSocketTransport::GetKeepaliveEnabled(bool *aResult)
  1.2546 +{
  1.2547 +    MOZ_ASSERT(aResult);
  1.2548 +
  1.2549 +    *aResult = mKeepaliveEnabled;
  1.2550 +    return NS_OK;
  1.2551 +}
  1.2552 +
  1.2553 +nsresult
  1.2554 +nsSocketTransport::EnsureKeepaliveValsAreInitialized()
  1.2555 +{
  1.2556 +    nsresult rv = NS_OK;
  1.2557 +    int32_t val = -1;
  1.2558 +    if (mKeepaliveIdleTimeS == -1) {
  1.2559 +        rv = mSocketTransportService->GetKeepaliveIdleTime(&val);
  1.2560 +        if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2561 +            return rv;
  1.2562 +        }
  1.2563 +        mKeepaliveIdleTimeS = val;
  1.2564 +    }
  1.2565 +    if (mKeepaliveRetryIntervalS == -1) {
  1.2566 +        rv = mSocketTransportService->GetKeepaliveRetryInterval(&val);
  1.2567 +        if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2568 +            return rv;
  1.2569 +        }
  1.2570 +        mKeepaliveRetryIntervalS = val;
  1.2571 +    }
  1.2572 +    if (mKeepaliveProbeCount == -1) {
  1.2573 +        rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
  1.2574 +        if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2575 +            return rv;
  1.2576 +        }
  1.2577 +        mKeepaliveProbeCount = val;
  1.2578 +    }
  1.2579 +    return NS_OK;
  1.2580 +}
  1.2581 +
  1.2582 +NS_IMETHODIMP
  1.2583 +nsSocketTransport::SetKeepaliveEnabled(bool aEnable)
  1.2584 +{
  1.2585 +#if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
  1.2586 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1.2587 +
  1.2588 +    if (aEnable == mKeepaliveEnabled) {
  1.2589 +        SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] already %s.",
  1.2590 +                    this, aEnable ? "enabled" : "disabled"));
  1.2591 +        return NS_OK;
  1.2592 +    }
  1.2593 +
  1.2594 +    nsresult rv = NS_OK;
  1.2595 +    if (aEnable) {
  1.2596 +        rv = EnsureKeepaliveValsAreInitialized();
  1.2597 +        if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2598 +            SOCKET_LOG(("  SetKeepaliveEnabled [%p] "
  1.2599 +                        "error [0x%x] initializing keepalive vals",
  1.2600 +                        this, rv));
  1.2601 +            return rv;
  1.2602 +        }
  1.2603 +    }
  1.2604 +    SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] "
  1.2605 +                "%s, idle time[%ds] retry interval[%ds] packet count[%d]: "
  1.2606 +                "globally %s.",
  1.2607 +                this, aEnable ? "enabled" : "disabled",
  1.2608 +                mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS,
  1.2609 +                mKeepaliveProbeCount,
  1.2610 +                mSocketTransportService->IsKeepaliveEnabled() ?
  1.2611 +                "enabled" : "disabled"));
  1.2612 +
  1.2613 +    // Set mKeepaliveEnabled here so that state is maintained; it is possible
  1.2614 +    // that we're in between fds, e.g. the 1st IP address failed, so we're about
  1.2615 +    // to retry on a 2nd from the DNS record.
  1.2616 +    mKeepaliveEnabled = aEnable;
  1.2617 +
  1.2618 +    rv = SetKeepaliveEnabledInternal(aEnable);
  1.2619 +    if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2620 +        SOCKET_LOG(("  SetKeepaliveEnabledInternal failed rv[0x%x]", rv));
  1.2621 +        return rv;
  1.2622 +    }
  1.2623 +
  1.2624 +    return NS_OK;
  1.2625 +#else /* !(defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)) */
  1.2626 +    SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled unsupported platform"));
  1.2627 +    return NS_ERROR_NOT_IMPLEMENTED;
  1.2628 +#endif
  1.2629 +}
  1.2630 +
  1.2631 +NS_IMETHODIMP
  1.2632 +nsSocketTransport::SetKeepaliveVals(int32_t aIdleTime,
  1.2633 +                                    int32_t aRetryInterval)
  1.2634 +{
  1.2635 +#if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
  1.2636 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1.2637 +    if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
  1.2638 +        return NS_ERROR_INVALID_ARG;
  1.2639 +    }
  1.2640 +    if (NS_WARN_IF(aRetryInterval <= 0 ||
  1.2641 +                   kMaxTCPKeepIntvl < aRetryInterval)) {
  1.2642 +        return NS_ERROR_INVALID_ARG;
  1.2643 +    }
  1.2644 +
  1.2645 +    if (aIdleTime == mKeepaliveIdleTimeS &&
  1.2646 +        aRetryInterval == mKeepaliveRetryIntervalS) {
  1.2647 +        SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals [%p] idle time "
  1.2648 +                    "already %ds and retry interval already %ds.",
  1.2649 +                    this, mKeepaliveIdleTimeS,
  1.2650 +                    mKeepaliveRetryIntervalS));
  1.2651 +        return NS_OK;
  1.2652 +    }
  1.2653 +    mKeepaliveIdleTimeS = aIdleTime;
  1.2654 +    mKeepaliveRetryIntervalS = aRetryInterval;
  1.2655 +
  1.2656 +    nsresult rv = NS_OK;
  1.2657 +    if (mKeepaliveProbeCount == -1) {
  1.2658 +        int32_t val = -1;
  1.2659 +        nsresult rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
  1.2660 +        if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2661 +            return rv;
  1.2662 +        }
  1.2663 +        mKeepaliveProbeCount = val;
  1.2664 +    }
  1.2665 +
  1.2666 +    SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals [%p] "
  1.2667 +                "keepalive %s, idle time[%ds] retry interval[%ds] "
  1.2668 +                "packet count[%d]",
  1.2669 +                this, mKeepaliveEnabled ? "enabled" : "disabled",
  1.2670 +                mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS,
  1.2671 +                mKeepaliveProbeCount));
  1.2672 +
  1.2673 +    PRFileDescAutoLock fd(this);
  1.2674 +    if (NS_WARN_IF(!fd.IsInitialized())) {
  1.2675 +        return NS_ERROR_NULL_POINTER;
  1.2676 +    }
  1.2677 +
  1.2678 +    rv = fd.SetKeepaliveVals(mKeepaliveEnabled,
  1.2679 +                             mKeepaliveIdleTimeS,
  1.2680 +                             mKeepaliveRetryIntervalS,
  1.2681 +                             mKeepaliveProbeCount);
  1.2682 +    if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2683 +        return rv;
  1.2684 +    }
  1.2685 +    return NS_OK;
  1.2686 +#else
  1.2687 +    SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals unsupported platform"));
  1.2688 +    return NS_ERROR_NOT_IMPLEMENTED;
  1.2689 +#endif
  1.2690 +}
  1.2691 +
  1.2692 +#ifdef ENABLE_SOCKET_TRACING
  1.2693 +
  1.2694 +#include <stdio.h>
  1.2695 +#include <ctype.h>
  1.2696 +#include "prenv.h"
  1.2697 +
  1.2698 +static void
  1.2699 +DumpBytesToFile(const char *path, const char *header, const char *buf, int32_t n)
  1.2700 +{
  1.2701 +    FILE *fp = fopen(path, "a");
  1.2702 +
  1.2703 +    fprintf(fp, "\n%s [%d bytes]\n", header, n);
  1.2704 +
  1.2705 +    const unsigned char *p;
  1.2706 +    while (n) {
  1.2707 +        p = (const unsigned char *) buf;
  1.2708 +
  1.2709 +        int32_t i, row_max = std::min(16, n);
  1.2710 +
  1.2711 +        for (i = 0; i < row_max; ++i)
  1.2712 +            fprintf(fp, "%02x  ", *p++);
  1.2713 +        for (i = row_max; i < 16; ++i)
  1.2714 +            fprintf(fp, "    ");
  1.2715 +
  1.2716 +        p = (const unsigned char *) buf;
  1.2717 +        for (i = 0; i < row_max; ++i, ++p) {
  1.2718 +            if (isprint(*p))
  1.2719 +                fprintf(fp, "%c", *p);
  1.2720 +            else
  1.2721 +                fprintf(fp, ".");
  1.2722 +        }
  1.2723 +
  1.2724 +        fprintf(fp, "\n");
  1.2725 +        buf += row_max;
  1.2726 +        n -= row_max;
  1.2727 +    }
  1.2728 +
  1.2729 +    fprintf(fp, "\n");
  1.2730 +    fclose(fp);
  1.2731 +}
  1.2732 +
  1.2733 +void
  1.2734 +nsSocketTransport::TraceInBuf(const char *buf, int32_t n)
  1.2735 +{
  1.2736 +    char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
  1.2737 +    if (!val || !*val)
  1.2738 +        return;
  1.2739 +
  1.2740 +    nsAutoCString header;
  1.2741 +    header.Assign(NS_LITERAL_CSTRING("Reading from: ") + mHost);
  1.2742 +    header.Append(':');
  1.2743 +    header.AppendInt(mPort);
  1.2744 +
  1.2745 +    DumpBytesToFile(val, header.get(), buf, n);
  1.2746 +}
  1.2747 +
  1.2748 +void
  1.2749 +nsSocketTransport::TraceOutBuf(const char *buf, int32_t n)
  1.2750 +{
  1.2751 +    char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
  1.2752 +    if (!val || !*val)
  1.2753 +        return;
  1.2754 +
  1.2755 +    nsAutoCString header;
  1.2756 +    header.Assign(NS_LITERAL_CSTRING("Writing to: ") + mHost);
  1.2757 +    header.Append(':');
  1.2758 +    header.AppendInt(mPort);
  1.2759 +
  1.2760 +    DumpBytesToFile(val, header.get(), buf, n);
  1.2761 +}
  1.2762 +
  1.2763 +#endif
  1.2764 +
  1.2765 +static void LogNSPRError(const char* aPrefix, const void *aObjPtr)
  1.2766 +{
  1.2767 +#if defined(PR_LOGGING) && defined(DEBUG)
  1.2768 +    PRErrorCode errCode = PR_GetError();
  1.2769 +    int errLen = PR_GetErrorTextLength();
  1.2770 +    nsAutoCString errStr;
  1.2771 +    if (errLen > 0) {
  1.2772 +        errStr.SetLength(errLen);
  1.2773 +        PR_GetErrorText(errStr.BeginWriting());
  1.2774 +    }
  1.2775 +    NS_WARNING(nsPrintfCString(
  1.2776 +               "%s [%p] NSPR error[0x%x] %s.",
  1.2777 +               aPrefix ? aPrefix : "nsSocketTransport", aObjPtr, errCode,
  1.2778 +               errLen > 0 ? errStr.BeginReading() : "<no error text>").get());
  1.2779 +#endif
  1.2780 +}
  1.2781 +
  1.2782 +nsresult
  1.2783 +nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled(bool aEnable)
  1.2784 +{
  1.2785 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1.2786 +    MOZ_ASSERT(!(aEnable && !gSocketTransportService->IsKeepaliveEnabled()),
  1.2787 +               "Cannot enable keepalive if global pref is disabled!");
  1.2788 +    if (aEnable && !gSocketTransportService->IsKeepaliveEnabled()) {
  1.2789 +        return NS_ERROR_ILLEGAL_VALUE;
  1.2790 +    }
  1.2791 +
  1.2792 +    PRSocketOptionData opt;
  1.2793 +
  1.2794 +    opt.option = PR_SockOpt_Keepalive;
  1.2795 +    opt.value.keep_alive = aEnable;
  1.2796 +    PRStatus status = PR_SetSocketOption(mFd, &opt);
  1.2797 +    if (NS_WARN_IF(status != PR_SUCCESS)) {
  1.2798 +        LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled",
  1.2799 +                     mSocketTransport);
  1.2800 +        return ErrorAccordingToNSPR(PR_GetError());
  1.2801 +    }
  1.2802 +    return NS_OK;
  1.2803 +}
  1.2804 +
  1.2805 +static void LogOSError(const char *aPrefix, const void *aObjPtr)
  1.2806 +{
  1.2807 +#if defined(PR_LOGGING) && defined(DEBUG)
  1.2808 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1.2809 +
  1.2810 +#ifdef XP_WIN
  1.2811 +    DWORD errCode = WSAGetLastError();
  1.2812 +    LPVOID errMessage;
  1.2813 +    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  1.2814 +                  FORMAT_MESSAGE_FROM_SYSTEM |
  1.2815 +                  FORMAT_MESSAGE_IGNORE_INSERTS,
  1.2816 +                  NULL,
  1.2817 +                  errCode,
  1.2818 +                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1.2819 +                  (LPTSTR) &errMessage,
  1.2820 +                  0, NULL);
  1.2821 +#else
  1.2822 +    int errCode = errno;
  1.2823 +    char *errMessage = strerror(errno);
  1.2824 +#endif
  1.2825 +    NS_WARNING(nsPrintfCString(
  1.2826 +               "%s [%p] OS error[0x%x] %s",
  1.2827 +               aPrefix ? aPrefix : "nsSocketTransport", aObjPtr, errCode,
  1.2828 +               errMessage ? errMessage : "<no error text>").get());
  1.2829 +#ifdef XP_WIN
  1.2830 +    LocalFree(errMessage);
  1.2831 +#endif
  1.2832 +#endif
  1.2833 +}
  1.2834 +
  1.2835 +/* XXX PR_SetSockOpt does not support setting keepalive values, so native
  1.2836 + * handles and platform specific apis (setsockopt, WSAIOCtl) are used in this
  1.2837 + * file. Requires inclusion of NSPR private/pprio.h, and platform headers.
  1.2838 + */
  1.2839 +
  1.2840 +nsresult
  1.2841 +nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(bool aEnabled,
  1.2842 +                                                        int aIdleTime,
  1.2843 +                                                        int aRetryInterval,
  1.2844 +                                                        int aProbeCount)
  1.2845 +{
  1.2846 +#if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
  1.2847 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1.2848 +    if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
  1.2849 +        return NS_ERROR_INVALID_ARG;
  1.2850 +    }
  1.2851 +    if (NS_WARN_IF(aRetryInterval <= 0 ||
  1.2852 +                   kMaxTCPKeepIntvl < aRetryInterval)) {
  1.2853 +        return NS_ERROR_INVALID_ARG;
  1.2854 +    }
  1.2855 +    if (NS_WARN_IF(aProbeCount <= 0 || kMaxTCPKeepCount < aProbeCount)) {
  1.2856 +        return NS_ERROR_INVALID_ARG;
  1.2857 +    }
  1.2858 +
  1.2859 +    PROsfd sock = PR_FileDesc2NativeHandle(mFd);
  1.2860 +    if (NS_WARN_IF(sock == -1)) {
  1.2861 +        LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals",
  1.2862 +                     mSocketTransport);
  1.2863 +        return ErrorAccordingToNSPR(PR_GetError());
  1.2864 +    }
  1.2865 +#endif
  1.2866 +
  1.2867 +#if defined(XP_WIN)
  1.2868 +    // Windows allows idle time and retry interval to be set; NOT ping count.
  1.2869 +    struct tcp_keepalive keepalive_vals = {
  1.2870 +        (int)aEnabled,
  1.2871 +        // Windows uses msec.
  1.2872 +        aIdleTime * 1000,
  1.2873 +        aRetryInterval * 1000
  1.2874 +    };
  1.2875 +    DWORD bytes_returned;
  1.2876 +    int err = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &keepalive_vals,
  1.2877 +                       sizeof(keepalive_vals), NULL, 0, &bytes_returned, NULL,
  1.2878 +                       NULL);
  1.2879 +    if (NS_WARN_IF(err)) {
  1.2880 +        LogOSError("nsSocketTransport WSAIoctl failed", mSocketTransport);
  1.2881 +        return NS_ERROR_UNEXPECTED;
  1.2882 +    }
  1.2883 +    return NS_OK;
  1.2884 +
  1.2885 +#elif defined(XP_MACOSX)
  1.2886 +    // OS X uses sec; only supports idle time being set.
  1.2887 +    int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE,
  1.2888 +                         &aIdleTime, sizeof(aIdleTime));
  1.2889 +    if (NS_WARN_IF(err)) {
  1.2890 +        LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
  1.2891 +                   mSocketTransport);
  1.2892 +        return NS_ERROR_UNEXPECTED;
  1.2893 +    }
  1.2894 +    return NS_OK;
  1.2895 +
  1.2896 +#elif defined(XP_UNIX)
  1.2897 +    // Not all *nix OSes support the following setsockopt() options
  1.2898 +    // ... but we assume they are supported in the Android kernel;
  1.2899 +    // build errors will tell us if they are not.
  1.2900 +#if defined(ANDROID) || defined(TCP_KEEPIDLE)
  1.2901 +    // Idle time until first keepalive probe; interval between ack'd probes; seconds.
  1.2902 +    int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE,
  1.2903 +                         &aIdleTime, sizeof(aIdleTime));
  1.2904 +    if (NS_WARN_IF(err)) {
  1.2905 +        LogOSError("nsSocketTransport Failed setting TCP_KEEPIDLE",
  1.2906 +                   mSocketTransport);
  1.2907 +        return NS_ERROR_UNEXPECTED;
  1.2908 +    }
  1.2909 +
  1.2910 +#endif
  1.2911 +#if defined(ANDROID) || defined(TCP_KEEPINTVL)
  1.2912 +    // Interval between unack'd keepalive probes; seconds.
  1.2913 +    err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL,
  1.2914 +                        &aRetryInterval, sizeof(aRetryInterval));
  1.2915 +    if (NS_WARN_IF(err)) {
  1.2916 +        LogOSError("nsSocketTransport Failed setting TCP_KEEPINTVL",
  1.2917 +                   mSocketTransport);
  1.2918 +        return NS_ERROR_UNEXPECTED;
  1.2919 +    }
  1.2920 +
  1.2921 +#endif
  1.2922 +#if defined(ANDROID) || defined(TCP_KEEPCNT)
  1.2923 +    // Number of unack'd keepalive probes before connection times out.
  1.2924 +    err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT,
  1.2925 +                     &aProbeCount, sizeof(aProbeCount));
  1.2926 +    if (NS_WARN_IF(err)) {
  1.2927 +        LogOSError("nsSocketTransport Failed setting TCP_KEEPCNT",
  1.2928 +                   mSocketTransport);
  1.2929 +        return NS_ERROR_UNEXPECTED;
  1.2930 +    }
  1.2931 +
  1.2932 +#endif
  1.2933 +    return NS_OK;
  1.2934 +#else
  1.2935 +    MOZ_ASSERT(false, "nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals "
  1.2936 +               "called on unsupported platform!");
  1.2937 +    return NS_ERROR_UNEXPECTED;
  1.2938 +#endif
  1.2939 +}

mercurial