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