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 +}