netwerk/base/src/nsSocketTransport2.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial