TCPSocket.cpp (35655B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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 file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "TCPSocket.h" 8 9 #include "TCPServerSocket.h" 10 #include "TCPSocketChild.h" 11 #include "TCPSocketParent.h" 12 #include "mozilla/BasePrincipal.h" 13 #include "mozilla/ErrorResult.h" 14 #include "mozilla/SyncRunnable.h" 15 #include "mozilla/dom/RootedDictionary.h" 16 #include "mozilla/dom/ScriptSettings.h" 17 #include "mozilla/dom/TCPSocketBinding.h" 18 #include "mozilla/dom/TCPSocketErrorEvent.h" 19 #include "mozilla/dom/TCPSocketErrorEventBinding.h" 20 #include "mozilla/dom/TCPSocketEvent.h" 21 #include "mozilla/dom/TCPSocketEventBinding.h" 22 #include "mozilla/dom/ToJSValue.h" 23 #include "nsComponentManagerUtils.h" 24 #include "nsContentUtils.h" 25 #include "nsIArrayBufferInputStream.h" 26 #include "nsIAsyncInputStream.h" 27 #include "nsIAsyncStreamCopier.h" 28 #include "nsIBinaryInputStream.h" 29 #include "nsICancelable.h" 30 #include "nsIChannel.h" 31 #include "nsIInputStream.h" 32 #include "nsIInputStreamPump.h" 33 #include "nsIMultiplexInputStream.h" 34 #include "nsINSSErrorsService.h" 35 #include "nsIObserverService.h" 36 #include "nsIOutputStream.h" 37 #include "nsIProtocolProxyService.h" 38 #include "nsIScriptableInputStream.h" 39 #include "nsISocketTransport.h" 40 #include "nsISocketTransportService.h" 41 #include "nsISupportsPrimitives.h" 42 #include "nsITLSSocketControl.h" 43 #include "nsITransport.h" 44 #include "nsIURIMutator.h" 45 #include "nsNetCID.h" 46 #include "nsNetUtil.h" 47 #include "nsServiceManagerUtils.h" 48 #include "nsString.h" 49 #include "nsStringStream.h" 50 #include "secerr.h" 51 #include "sslerr.h" 52 53 using namespace mozilla::dom; 54 55 NS_IMPL_CYCLE_COLLECTION(LegacyMozTCPSocket, mGlobal) 56 57 NS_IMPL_CYCLE_COLLECTING_ADDREF(LegacyMozTCPSocket) 58 NS_IMPL_CYCLE_COLLECTING_RELEASE(LegacyMozTCPSocket) 59 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LegacyMozTCPSocket) 60 NS_INTERFACE_MAP_ENTRY(nsISupports) 61 NS_INTERFACE_MAP_END 62 63 LegacyMozTCPSocket::LegacyMozTCPSocket(nsPIDOMWindowInner* aWindow) 64 : mGlobal(do_QueryInterface(aWindow)) {} 65 66 LegacyMozTCPSocket::~LegacyMozTCPSocket() = default; 67 68 already_AddRefed<TCPSocket> LegacyMozTCPSocket::Open( 69 const nsAString& aHost, uint16_t aPort, const SocketOptions& aOptions, 70 mozilla::ErrorResult& aRv) { 71 AutoJSAPI api; 72 if (NS_WARN_IF(!api.Init(mGlobal))) { 73 aRv.Throw(NS_ERROR_FAILURE); 74 return nullptr; 75 } 76 GlobalObject globalObj(api.cx(), mGlobal->GetGlobalJSObject()); 77 return TCPSocket::Constructor(globalObj, aHost, aPort, aOptions, aRv); 78 } 79 80 already_AddRefed<TCPServerSocket> LegacyMozTCPSocket::Listen( 81 uint16_t aPort, const ServerSocketOptions& aOptions, uint16_t aBacklog, 82 mozilla::ErrorResult& aRv) { 83 AutoJSAPI api; 84 if (NS_WARN_IF(!api.Init(mGlobal))) { 85 return nullptr; 86 } 87 GlobalObject globalObj(api.cx(), mGlobal->GetGlobalJSObject()); 88 return TCPServerSocket::Constructor(globalObj, aPort, aOptions, aBacklog, 89 aRv); 90 } 91 92 bool LegacyMozTCPSocket::WrapObject(JSContext* aCx, 93 JS::Handle<JSObject*> aGivenProto, 94 JS::MutableHandle<JSObject*> aReflector) { 95 return LegacyMozTCPSocket_Binding::Wrap(aCx, this, aGivenProto, aReflector); 96 } 97 98 NS_IMPL_CYCLE_COLLECTION_CLASS(TCPSocket) 99 100 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(TCPSocket, DOMEventTargetHelper) 101 NS_IMPL_CYCLE_COLLECTION_TRACE_END 102 103 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TCPSocket, 104 DOMEventTargetHelper) 105 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransport) 106 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketInputStream) 107 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketOutputStream) 108 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamPump) 109 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamScriptable) 110 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamBinary) 111 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingDataAfterStartTLS) 112 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketBridgeChild) 113 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketBridgeParent) 114 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 115 116 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TCPSocket, DOMEventTargetHelper) 117 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransport) 118 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketInputStream) 119 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketOutputStream) 120 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamPump) 121 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamScriptable) 122 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamBinary) 123 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingDataAfterStartTLS) 124 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketBridgeChild) 125 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketBridgeParent) 126 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE 127 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 128 129 NS_IMPL_ADDREF_INHERITED(TCPSocket, DOMEventTargetHelper) 130 NS_IMPL_RELEASE_INHERITED(TCPSocket, DOMEventTargetHelper) 131 132 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocket) 133 NS_INTERFACE_MAP_ENTRY(nsITransportEventSink) 134 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) 135 NS_INTERFACE_MAP_ENTRY(nsIStreamListener) 136 NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback) 137 NS_INTERFACE_MAP_ENTRY(nsIObserver) 138 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 139 NS_INTERFACE_MAP_ENTRY(nsITCPSocketCallback) 140 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback) 141 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 142 143 TCPSocket::TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, 144 uint16_t aPort, bool aSsl, bool aUseArrayBuffers) 145 : DOMEventTargetHelper(aGlobal), 146 mReadyState(TCPReadyState::Closed), 147 mUseArrayBuffers(aUseArrayBuffers), 148 mHost(aHost), 149 mPort(aPort), 150 mSsl(aSsl), 151 mAsyncCopierActive(false), 152 mWaitingForDrain(false), 153 mInnerWindowID(0), 154 mBufferedAmount(0), 155 mSuspendCount(0), 156 mTrackingNumber(0), 157 mWaitingForStartTLS(false), 158 mObserversActive(false) { 159 if (aGlobal) { 160 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal); 161 if (window) { 162 mInnerWindowID = window->WindowID(); 163 } 164 } 165 } 166 167 TCPSocket::~TCPSocket() { 168 if (mObserversActive) { 169 nsCOMPtr<nsIObserverService> obs = 170 do_GetService("@mozilla.org/observer-service;1"); 171 if (obs) { 172 obs->RemoveObserver(this, "inner-window-destroyed"); 173 obs->RemoveObserver(this, "profile-change-net-teardown"); 174 } 175 } 176 } 177 178 nsresult TCPSocket::CreateStream() { 179 nsresult rv = 180 mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(mSocketInputStream)); 181 NS_ENSURE_SUCCESS(rv, rv); 182 rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, 183 getter_AddRefs(mSocketOutputStream)); 184 NS_ENSURE_SUCCESS(rv, rv); 185 186 // If the other side is not listening, we will 187 // get an onInputStreamReady callback where available 188 // raises to indicate the connection was refused. 189 nsCOMPtr<nsIAsyncInputStream> asyncStream = 190 do_QueryInterface(mSocketInputStream); 191 NS_ENSURE_TRUE(asyncStream, NS_ERROR_NOT_AVAILABLE); 192 193 nsCOMPtr<nsISerialEventTarget> mainTarget = GetMainThreadSerialEventTarget(); 194 rv = asyncStream->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, 195 mainTarget); 196 NS_ENSURE_SUCCESS(rv, rv); 197 198 if (mUseArrayBuffers) { 199 mInputStreamBinary = 200 do_CreateInstance("@mozilla.org/binaryinputstream;1", &rv); 201 NS_ENSURE_SUCCESS(rv, rv); 202 rv = mInputStreamBinary->SetInputStream(mSocketInputStream); 203 NS_ENSURE_SUCCESS(rv, rv); 204 } else { 205 mInputStreamScriptable = 206 do_CreateInstance("@mozilla.org/scriptableinputstream;1", &rv); 207 NS_ENSURE_SUCCESS(rv, rv); 208 rv = mInputStreamScriptable->Init(mSocketInputStream); 209 NS_ENSURE_SUCCESS(rv, rv); 210 } 211 212 return NS_OK; 213 } 214 215 nsresult TCPSocket::InitWithUnconnectedTransport( 216 nsISocketTransport* aTransport) { 217 mReadyState = TCPReadyState::Connecting; 218 mTransport = aTransport; 219 220 MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content); 221 222 nsCOMPtr<nsISerialEventTarget> mainTarget = GetMainThreadSerialEventTarget(); 223 mTransport->SetEventSink(this, mainTarget); 224 225 nsresult rv = CreateStream(); 226 NS_ENSURE_SUCCESS(rv, rv); 227 228 return NS_OK; 229 } 230 231 nsresult TCPSocket::Init(nsIProxyInfo* aProxyInfo) { 232 nsCOMPtr<nsIObserverService> obs = 233 do_GetService("@mozilla.org/observer-service;1"); 234 if (obs) { 235 mObserversActive = true; 236 obs->AddObserver(this, "inner-window-destroyed", true); // weak reference 237 obs->AddObserver(this, "profile-change-net-teardown", true); // weak ref 238 } 239 240 if (XRE_IsContentProcess()) { 241 mReadyState = TCPReadyState::Connecting; 242 243 nsCOMPtr<nsISerialEventTarget> target; 244 if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) { 245 target = global->SerialEventTarget(); 246 } 247 mSocketBridgeChild = new TCPSocketChild(mHost, mPort, target); 248 mSocketBridgeChild->SendOpen(this, mSsl, mUseArrayBuffers); 249 return NS_OK; 250 } 251 252 nsCOMPtr<nsISocketTransportService> sts = 253 do_GetService("@mozilla.org/network/socket-transport-service;1"); 254 255 AutoTArray<nsCString, 1> socketTypes; 256 if (mSsl) { 257 socketTypes.AppendElement("ssl"_ns); 258 } else { 259 socketTypes.AppendElement("starttls"_ns); 260 } 261 nsCOMPtr<nsISocketTransport> transport; 262 nsresult rv = 263 sts->CreateTransport(socketTypes, NS_ConvertUTF16toUTF8(mHost), mPort, 264 aProxyInfo, nullptr, getter_AddRefs(transport)); 265 NS_ENSURE_SUCCESS(rv, rv); 266 267 return InitWithUnconnectedTransport(transport); 268 } 269 270 void TCPSocket::InitWithSocketChild(TCPSocketChild* aSocketBridge) { 271 mSocketBridgeChild = aSocketBridge; 272 mReadyState = TCPReadyState::Open; 273 mSocketBridgeChild->SetSocket(this); 274 mSocketBridgeChild->GetHost(mHost); 275 mSocketBridgeChild->GetPort(&mPort); 276 } 277 278 nsresult TCPSocket::InitWithTransport(nsISocketTransport* aTransport) { 279 mTransport = aTransport; 280 nsresult rv = CreateStream(); 281 NS_ENSURE_SUCCESS(rv, rv); 282 283 mReadyState = TCPReadyState::Open; 284 rv = CreateInputStreamPump(); 285 NS_ENSURE_SUCCESS(rv, rv); 286 287 nsAutoCString host; 288 mTransport->GetHost(host); 289 CopyUTF8toUTF16(host, mHost); 290 int32_t port; 291 mTransport->GetPort(&port); 292 mPort = port; 293 294 return NS_OK; 295 } 296 297 void TCPSocket::UpgradeToSecure(mozilla::ErrorResult& aRv) { 298 if (mReadyState != TCPReadyState::Open) { 299 aRv.Throw(NS_ERROR_FAILURE); 300 return; 301 } 302 303 if (mSsl) { 304 return; 305 } 306 307 mSsl = true; 308 309 if (mSocketBridgeChild) { 310 mSocketBridgeChild->SendStartTLS(); 311 return; 312 } 313 314 if (!mAsyncCopierActive) { 315 ActivateTLS(); 316 } else { 317 mWaitingForStartTLS = true; 318 } 319 } 320 321 namespace { 322 class CopierCallbacks final : public nsIRequestObserver { 323 RefPtr<TCPSocket> mOwner; 324 325 public: 326 explicit CopierCallbacks(TCPSocket* aSocket) : mOwner(aSocket) {} 327 328 NS_DECL_ISUPPORTS 329 NS_DECL_NSIREQUESTOBSERVER 330 private: 331 ~CopierCallbacks() = default; 332 }; 333 334 NS_IMPL_ISUPPORTS(CopierCallbacks, nsIRequestObserver) 335 336 NS_IMETHODIMP 337 CopierCallbacks::OnStartRequest(nsIRequest* aRequest) { return NS_OK; } 338 339 NS_IMETHODIMP 340 CopierCallbacks::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) { 341 mOwner->NotifyCopyComplete(aStatus); 342 mOwner = nullptr; 343 return NS_OK; 344 } 345 } // unnamed namespace 346 347 void TCPSocket::CalculateBufferedAmount() { 348 // Let's update the buffered amount of data. 349 uint64_t bufferedAmount = 0; 350 for (uint32_t i = 0, len = mPendingData.Length(); i < len; ++i) { 351 nsCOMPtr<nsIInputStream> stream = mPendingData[i]; 352 uint64_t available = 0; 353 if (NS_SUCCEEDED(stream->Available(&available))) { 354 bufferedAmount += available; 355 } 356 } 357 mBufferedAmount = bufferedAmount; 358 } 359 360 nsresult TCPSocket::EnsureCopying() { 361 if (mAsyncCopierActive) { 362 return NS_OK; 363 } 364 365 mAsyncCopierActive = true; 366 367 nsresult rv; 368 369 nsCOMPtr<nsIMultiplexInputStream> multiplexStream = 370 do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv); 371 NS_ENSURE_SUCCESS(rv, rv); 372 373 nsCOMPtr<nsIInputStream> stream = do_QueryInterface(multiplexStream); 374 375 while (!mPendingData.IsEmpty()) { 376 nsCOMPtr<nsIInputStream> stream = mPendingData[0]; 377 multiplexStream->AppendStream(stream); 378 mPendingData.RemoveElementAt(0); 379 } 380 381 nsCOMPtr<nsIAsyncStreamCopier> copier = 382 do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv); 383 NS_ENSURE_SUCCESS(rv, rv); 384 385 nsCOMPtr<nsISocketTransportService> sts = 386 do_GetService("@mozilla.org/network/socket-transport-service;1"); 387 388 nsCOMPtr<nsISerialEventTarget> target = do_QueryInterface(sts); 389 rv = copier->Init(stream, mSocketOutputStream, target, 390 true, /* source buffered */ 391 false, /* sink buffered */ 392 BUFFER_SIZE, false, /* close source */ 393 false); /* close sink */ 394 NS_ENSURE_SUCCESS(rv, rv); 395 396 RefPtr<CopierCallbacks> callbacks = new CopierCallbacks(this); 397 rv = copier->AsyncCopy(callbacks, nullptr); 398 NS_ENSURE_SUCCESS(rv, rv); 399 400 return NS_OK; 401 } 402 403 void TCPSocket::NotifyCopyComplete(nsresult aStatus) { 404 mAsyncCopierActive = false; 405 CalculateBufferedAmount(); 406 407 if (mSocketBridgeParent && mSocketBridgeParent->IPCOpen()) { 408 (void)mSocketBridgeParent->SendUpdateBufferedAmount(BufferedAmount(), 409 mTrackingNumber); 410 } 411 412 if (NS_FAILED(aStatus)) { 413 MaybeReportErrorAndCloseIfOpen(aStatus); 414 return; 415 } 416 417 if (BufferedAmount() != 0) { 418 EnsureCopying(); 419 return; 420 } 421 422 // Maybe we have some empty stream. We want to have an empty queue now. 423 mPendingData.Clear(); 424 425 // If we are waiting for initiating starttls, we can begin to 426 // activate tls now. 427 if (mWaitingForStartTLS && mReadyState == TCPReadyState::Open) { 428 ActivateTLS(); 429 mWaitingForStartTLS = false; 430 // If we have pending data, we should send them, or fire 431 // a drain event if we are waiting for it. 432 if (!mPendingDataAfterStartTLS.IsEmpty()) { 433 mPendingData = std::move(mPendingDataAfterStartTLS); 434 EnsureCopying(); 435 return; 436 } 437 } 438 439 // If we have a connected child, we let the child decide whether 440 // ondrain should be dispatched. 441 if (mWaitingForDrain && !mSocketBridgeParent) { 442 mWaitingForDrain = false; 443 FireEvent(u"drain"_ns); 444 } 445 446 if (mReadyState == TCPReadyState::Closing) { 447 if (mSocketOutputStream) { 448 mSocketOutputStream->Close(); 449 mSocketOutputStream = nullptr; 450 } 451 mReadyState = TCPReadyState::Closed; 452 FireEvent(u"close"_ns); 453 } 454 } 455 456 void TCPSocket::ActivateTLS() { 457 nsresult rv; 458 nsCOMPtr<nsIEventTarget> socketThread = 459 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); 460 if (NS_FAILED(rv)) { 461 return; 462 } 463 464 bool alreadyOnSTST = false; 465 if (NS_FAILED(socketThread->IsOnCurrentThread(&alreadyOnSTST))) { 466 return; 467 } 468 469 if (alreadyOnSTST) { 470 ActivateTLSHelper(); 471 return; 472 } 473 474 // Since we use mozilla::SyncRunnable::DispatchToThread, it is unnecessary to 475 // AddRef/Release TCPSocket in CallActivateTLS. 476 auto CallActivateTLS = [&self = *this]() mutable { 477 self.ActivateTLSHelper(); 478 }; 479 mozilla::SyncRunnable::DispatchToThread( 480 socketThread, 481 NS_NewRunnableFunction("TCPSocket::UpgradeToSecure->ActivateTLSHelper", 482 CallActivateTLS)); 483 } 484 485 void TCPSocket::ActivateTLSHelper() { 486 nsCOMPtr<nsITLSSocketControl> tlsSocketControl; 487 mTransport->GetTlsSocketControl(getter_AddRefs(tlsSocketControl)); 488 if (tlsSocketControl) { 489 tlsSocketControl->StartTLS(); 490 } 491 } 492 493 NS_IMETHODIMP 494 TCPSocket::FireErrorEvent(const nsAString& aName, const nsAString& aType, 495 nsresult aErrorCode) { 496 if (mSocketBridgeParent) { 497 mSocketBridgeParent->FireErrorEvent(aName, aType, aErrorCode, mReadyState); 498 return NS_OK; 499 } 500 501 TCPSocketErrorEventInit init; 502 init.mBubbles = false; 503 init.mCancelable = false; 504 init.mName = aName; 505 init.mMessage = aType; 506 static_assert(std::is_same_v<std::underlying_type_t<nsresult>, uint32_t>); 507 init.mErrorCode = uint32_t(aErrorCode); 508 509 RefPtr<TCPSocketErrorEvent> event = 510 TCPSocketErrorEvent::Constructor(this, u"error"_ns, init); 511 MOZ_ASSERT(event); 512 event->SetTrusted(true); 513 DispatchEvent(*event); 514 return NS_OK; 515 } 516 517 NS_IMETHODIMP 518 TCPSocket::FireEvent(const nsAString& aType) { 519 if (mSocketBridgeParent) { 520 mSocketBridgeParent->FireEvent(aType, mReadyState); 521 return NS_OK; 522 } 523 524 AutoJSAPI api; 525 if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) { 526 return NS_ERROR_FAILURE; 527 } 528 JS::Rooted<JS::Value> val(api.cx()); 529 return FireDataEvent(api.cx(), aType, val); 530 } 531 532 NS_IMETHODIMP 533 TCPSocket::FireDataArrayEvent(const nsAString& aType, 534 const nsTArray<uint8_t>& buffer) { 535 AutoJSAPI api; 536 if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) { 537 return NS_ERROR_FAILURE; 538 } 539 JSContext* cx = api.cx(); 540 JS::Rooted<JS::Value> val(cx); 541 542 bool ok = IPC::DeserializeArrayBuffer(cx, buffer, &val); 543 if (ok) { 544 return FireDataEvent(cx, aType, val); 545 } 546 return NS_ERROR_FAILURE; 547 } 548 549 NS_IMETHODIMP 550 TCPSocket::FireDataStringEvent(const nsAString& aType, 551 const nsACString& aString) { 552 AutoJSAPI api; 553 if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) { 554 return NS_ERROR_FAILURE; 555 } 556 JSContext* cx = api.cx(); 557 JS::Rooted<JS::Value> val(cx); 558 559 bool ok = ToJSValue(cx, NS_ConvertASCIItoUTF16(aString), &val); 560 if (ok) { 561 return FireDataEvent(cx, aType, val); 562 } 563 return NS_ERROR_FAILURE; 564 } 565 566 nsresult TCPSocket::FireDataEvent(JSContext* aCx, const nsAString& aType, 567 JS::Handle<JS::Value> aData) { 568 MOZ_ASSERT(!mSocketBridgeParent); 569 570 RootedDictionary<TCPSocketEventInit> init(aCx); 571 init.mBubbles = false; 572 init.mCancelable = false; 573 init.mData = aData; 574 575 RefPtr<TCPSocketEvent> event = TCPSocketEvent::Constructor(this, aType, init); 576 event->SetTrusted(true); 577 DispatchEvent(*event); 578 return NS_OK; 579 } 580 581 JSObject* TCPSocket::WrapObject(JSContext* aCx, 582 JS::Handle<JSObject*> aGivenProto) { 583 return TCPSocket_Binding::Wrap(aCx, this, aGivenProto); 584 } 585 586 void TCPSocket::GetHost(nsAString& aHost) { aHost.Assign(mHost); } 587 588 uint32_t TCPSocket::Port() const { return mPort; } 589 590 bool TCPSocket::Ssl() const { return mSsl; } 591 592 void TCPSocket::Suspend() { 593 if (mSocketBridgeChild) { 594 mSocketBridgeChild->SendSuspend(); 595 return; 596 } 597 if (mInputStreamPump) { 598 mInputStreamPump->Suspend(); 599 } 600 mSuspendCount++; 601 } 602 603 void TCPSocket::Resume(mozilla::ErrorResult& aRv) { 604 if (mSocketBridgeChild) { 605 mSocketBridgeChild->SendResume(); 606 return; 607 } 608 if (!mSuspendCount) { 609 aRv.Throw(NS_ERROR_FAILURE); 610 return; 611 } 612 613 if (mInputStreamPump) { 614 mInputStreamPump->Resume(); 615 } 616 mSuspendCount--; 617 } 618 619 nsresult TCPSocket::MaybeReportErrorAndCloseIfOpen(nsresult status) { 620 // If we're closed, we've already reported the error or just don't need to 621 // report the error. 622 if (mReadyState == TCPReadyState::Closed) { 623 return NS_OK; 624 } 625 626 // go through ::Closing state and then mark ::Closed 627 Close(); 628 mReadyState = TCPReadyState::Closed; 629 630 if (NS_FAILED(status)) { 631 // Convert the status code to an appropriate error message. 632 633 nsString errorType, errName; 634 635 // security module? (and this is an error) 636 if ((static_cast<uint32_t>(status) & 0xFF0000) == 0x5a0000) { 637 nsCOMPtr<nsINSSErrorsService> errSvc = 638 do_GetService("@mozilla.org/nss_errors_service;1"); 639 // getErrorClass will throw a generic NS_ERROR_FAILURE if the error code 640 // is somehow not in the set of covered errors. 641 uint32_t errorClass; 642 nsresult rv = errSvc->GetErrorClass(status, &errorClass); 643 if (NS_FAILED(rv)) { 644 errorType.AssignLiteral("SecurityProtocol"); 645 } else { 646 switch (errorClass) { 647 case nsINSSErrorsService::ERROR_CLASS_BAD_CERT: 648 errorType.AssignLiteral("SecurityCertificate"); 649 break; 650 default: 651 errorType.AssignLiteral("SecurityProtocol"); 652 break; 653 } 654 } 655 656 // NSS_SEC errors (happen below the base value because of negative vals) 657 if ((static_cast<int32_t>(status) & 0xFFFF) < 658 abs(nsINSSErrorsService::NSS_SEC_ERROR_BASE)) { 659 switch (static_cast<SECErrorCodes>(status)) { 660 case SEC_ERROR_EXPIRED_CERTIFICATE: 661 errName.AssignLiteral("SecurityExpiredCertificateError"); 662 break; 663 case SEC_ERROR_REVOKED_CERTIFICATE: 664 errName.AssignLiteral("SecurityRevokedCertificateError"); 665 break; 666 // per bsmith, we will be unable to tell these errors apart very 667 // soon, so it makes sense to just folder them all together already. 668 case SEC_ERROR_UNKNOWN_ISSUER: 669 case SEC_ERROR_UNTRUSTED_ISSUER: 670 case SEC_ERROR_UNTRUSTED_CERT: 671 case SEC_ERROR_CA_CERT_INVALID: 672 errName.AssignLiteral("SecurityUntrustedCertificateIssuerError"); 673 break; 674 case SEC_ERROR_INADEQUATE_KEY_USAGE: 675 errName.AssignLiteral("SecurityInadequateKeyUsageError"); 676 break; 677 case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: 678 errName.AssignLiteral( 679 "SecurityCertificateSignatureAlgorithmDisabledError"); 680 break; 681 default: 682 errName.AssignLiteral("SecurityError"); 683 break; 684 } 685 } else { 686 // NSS_SSL errors 687 switch (static_cast<SSLErrorCodes>(status)) { 688 case SSL_ERROR_NO_CERTIFICATE: 689 errName.AssignLiteral("SecurityNoCertificateError"); 690 break; 691 case SSL_ERROR_BAD_CERTIFICATE: 692 errName.AssignLiteral("SecurityBadCertificateError"); 693 break; 694 case SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE: 695 errName.AssignLiteral("SecurityUnsupportedCertificateTypeError"); 696 break; 697 case SSL_ERROR_UNSUPPORTED_VERSION: 698 errName.AssignLiteral("SecurityUnsupportedTLSVersionError"); 699 break; 700 case SSL_ERROR_BAD_CERT_DOMAIN: 701 errName.AssignLiteral("SecurityCertificateDomainMismatchError"); 702 break; 703 default: 704 errName.AssignLiteral("SecurityError"); 705 break; 706 } 707 } 708 } else { 709 // must be network 710 errorType.AssignLiteral("Network"); 711 712 switch (status) { 713 // connect to host:port failed 714 case NS_ERROR_CONNECTION_REFUSED: 715 errName.AssignLiteral("ConnectionRefusedError"); 716 break; 717 // network timeout error 718 case NS_ERROR_NET_TIMEOUT: 719 errName.AssignLiteral("NetworkTimeoutError"); 720 break; 721 // hostname lookup failed 722 case NS_ERROR_UNKNOWN_HOST: 723 errName.AssignLiteral("DomainNotFoundError"); 724 break; 725 case NS_ERROR_NET_INTERRUPT: 726 errName.AssignLiteral("NetworkInterruptError"); 727 break; 728 default: 729 errName.AssignLiteral("NetworkError"); 730 break; 731 } 732 } 733 734 (void)NS_WARN_IF(NS_FAILED(FireErrorEvent(errName, errorType, status))); 735 } 736 737 return FireEvent(u"close"_ns); 738 } 739 740 void TCPSocket::Close() { CloseHelper(true); } 741 742 void TCPSocket::CloseImmediately() { CloseHelper(false); } 743 744 void TCPSocket::CloseHelper(bool waitForUnsentData) { 745 if (mReadyState == TCPReadyState::Closed || 746 mReadyState == TCPReadyState::Closing) { 747 return; 748 } 749 750 mReadyState = TCPReadyState::Closing; 751 752 if (mProxyRequest) { 753 mProxyRequest->Cancel(NS_BINDING_ABORTED); 754 mProxyRequest = nullptr; 755 } 756 757 if (mSocketBridgeChild) { 758 mSocketBridgeChild->SendClose(); 759 return; 760 } 761 762 if (!mAsyncCopierActive || !waitForUnsentData) { 763 mPendingData.Clear(); 764 mPendingDataAfterStartTLS.Clear(); 765 766 if (mSocketOutputStream) { 767 mSocketOutputStream->Close(); 768 mSocketOutputStream = nullptr; 769 } 770 } 771 772 if (mSocketInputStream) { 773 mSocketInputStream->Close(); 774 mSocketInputStream = nullptr; 775 } 776 } 777 778 bool TCPSocket::Send(const nsACString& aData, mozilla::ErrorResult& aRv) { 779 if (mReadyState != TCPReadyState::Open) { 780 aRv.Throw(NS_ERROR_FAILURE); 781 return false; 782 } 783 784 uint64_t byteLength; 785 nsCOMPtr<nsIInputStream> stream; 786 if (mSocketBridgeChild) { 787 mSocketBridgeChild->SendSend(aData); 788 byteLength = aData.Length(); 789 } else { 790 nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), aData); 791 if (NS_FAILED(rv)) { 792 aRv.Throw(rv); 793 return false; 794 } 795 rv = stream->Available(&byteLength); 796 if (NS_FAILED(rv)) { 797 aRv.Throw(rv); 798 return false; 799 } 800 } 801 return Send(stream, byteLength); 802 } 803 804 bool TCPSocket::Send(const ArrayBuffer& aData, uint32_t aByteOffset, 805 const Optional<uint32_t>& aByteLength, 806 mozilla::ErrorResult& aRv) { 807 if (mReadyState != TCPReadyState::Open) { 808 aRv.Throw(NS_ERROR_FAILURE); 809 return false; 810 } 811 812 nsCOMPtr<nsIArrayBufferInputStream> stream; 813 814 uint32_t nbytes; 815 auto calculateOffsetAndCount = [&](uint32_t aLength) { 816 uint32_t offset = std::min(aLength, aByteOffset); 817 nbytes = std::min(aLength - aByteOffset, 818 aByteLength.WasPassed() ? aByteLength.Value() : aLength); 819 return std::pair(offset, nbytes); 820 }; 821 822 if (mSocketBridgeChild) { 823 nsTArray<uint8_t> arrayBuffer; 824 if (!aData.AppendDataTo(arrayBuffer, calculateOffsetAndCount)) { 825 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 826 return false; 827 } 828 829 mSocketBridgeChild->SendSend(std::move(arrayBuffer)); 830 } else { 831 mozilla::Maybe<mozilla::UniquePtr<uint8_t[]>> arrayBuffer = 832 aData.CreateFromData<mozilla::UniquePtr<uint8_t[]>>( 833 calculateOffsetAndCount); 834 if (arrayBuffer.isNothing()) { 835 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 836 return false; 837 } 838 839 stream = do_CreateInstance("@mozilla.org/io/arraybuffer-input-stream;1"); 840 nsresult rv = stream->SetData(arrayBuffer.extract(), nbytes); 841 if (NS_WARN_IF(NS_FAILED(rv))) { 842 aRv.Throw(rv); 843 return false; 844 } 845 } 846 return Send(stream, nbytes); 847 } 848 849 bool TCPSocket::Send(nsIInputStream* aStream, uint32_t aByteLength) { 850 uint64_t newBufferedAmount = BufferedAmount() + aByteLength; 851 bool bufferFull = newBufferedAmount > BUFFER_SIZE; 852 if (bufferFull) { 853 // If we buffered more than some arbitrary amount of data, 854 // (65535 right now) we should tell the caller so they can 855 // wait until ondrain is called if they so desire. Once all the 856 // buffered data has been written to the socket, ondrain is 857 // called. 858 mWaitingForDrain = true; 859 } 860 861 if (mSocketBridgeChild) { 862 // In the child, we just add the buffer length to our bufferedAmount and let 863 // the parent update our bufferedAmount when the data have been sent. 864 mBufferedAmount = newBufferedAmount; 865 return !bufferFull; 866 } 867 868 // This is used to track how many packets we have been told to send. Signaling 869 // this back to the user of this API allows the user to know how many packets 870 // are currently in flight over IPC. 871 ++mTrackingNumber; 872 if (mWaitingForStartTLS) { 873 // When we are waiting for starttls, newStream is added to pendingData 874 // and will be appended to multiplexStream after tls had been set up. 875 mPendingDataAfterStartTLS.AppendElement(aStream); 876 } else { 877 mPendingData.AppendElement(aStream); 878 } 879 880 CalculateBufferedAmount(); 881 EnsureCopying(); 882 883 return !bufferFull; 884 } 885 886 TCPReadyState TCPSocket::ReadyState() { return mReadyState; } 887 888 TCPSocketBinaryType TCPSocket::BinaryType() const { 889 if (mUseArrayBuffers) { 890 return TCPSocketBinaryType::Arraybuffer; 891 } 892 return TCPSocketBinaryType::String; 893 } 894 895 already_AddRefed<TCPSocket> TCPSocket::CreateAcceptedSocket( 896 nsIGlobalObject* aGlobal, nsISocketTransport* aTransport, 897 bool aUseArrayBuffers) { 898 RefPtr<TCPSocket> socket = 899 new TCPSocket(aGlobal, u""_ns, 0, false, aUseArrayBuffers); 900 nsresult rv = socket->InitWithTransport(aTransport); 901 NS_ENSURE_SUCCESS(rv, nullptr); 902 return socket.forget(); 903 } 904 905 already_AddRefed<TCPSocket> TCPSocket::CreateAcceptedSocket( 906 nsIGlobalObject* aGlobal, TCPSocketChild* aBridge, bool aUseArrayBuffers) { 907 RefPtr<TCPSocket> socket = 908 new TCPSocket(aGlobal, u""_ns, 0, false, aUseArrayBuffers); 909 socket->InitWithSocketChild(aBridge); 910 return socket.forget(); 911 } 912 913 already_AddRefed<TCPSocket> TCPSocket::Constructor( 914 const GlobalObject& aGlobal, const nsAString& aHost, uint16_t aPort, 915 const SocketOptions& aOptions, mozilla::ErrorResult& aRv) { 916 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 917 RefPtr<TCPSocket> socket = 918 new TCPSocket(global, aHost, aPort, aOptions.mUseSecureTransport, 919 aOptions.mBinaryType == TCPSocketBinaryType::Arraybuffer); 920 socket->ResolveProxy(); 921 922 return socket.forget(); 923 } 924 925 nsresult TCPSocket::ResolveProxy() { 926 nsresult rv; 927 nsCOMPtr<nsIProtocolProxyService> pps = 928 do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv); 929 if (NS_WARN_IF(NS_FAILED(rv))) { 930 return rv; 931 } 932 933 nsCOMPtr<nsIURI> uri; 934 nsCString spec = mSsl ? "https://"_ns : "http://"_ns; 935 bool maybeIPv6 = mHost.FindChar(':') != -1; 936 if (maybeIPv6) spec.Append('['); 937 if (!AppendUTF16toUTF8(mHost, spec, fallible)) { 938 return NS_ERROR_OUT_OF_MEMORY; 939 } 940 if (maybeIPv6) spec.Append(']'); 941 rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) 942 .SetSpec(spec) 943 .SetPort(mPort) 944 .Finalize(uri); 945 if (NS_WARN_IF(NS_FAILED(rv))) { 946 return rv; 947 } 948 949 nsCOMPtr<nsIChannel> channel; 950 rv = NS_NewChannel(getter_AddRefs(channel), uri, 951 nsContentUtils::GetSystemPrincipal(), 952 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 953 nsIContentPolicy::TYPE_OTHER); 954 if (NS_WARN_IF(NS_FAILED(rv))) { 955 return rv; 956 } 957 958 rv = pps->AsyncResolve(channel, 959 nsIProtocolProxyService::RESOLVE_PREFER_SOCKS_PROXY, 960 this, nullptr, getter_AddRefs(mProxyRequest)); 961 if (NS_WARN_IF(NS_FAILED(rv))) { 962 return rv; 963 } 964 965 return NS_OK; 966 } 967 968 NS_IMETHODIMP 969 TCPSocket::OnProxyAvailable(nsICancelable* aRequest, nsIChannel* aChannel, 970 nsIProxyInfo* aProxyInfo, nsresult aResult) { 971 mProxyRequest = nullptr; 972 if (NS_SUCCEEDED(aResult) && aProxyInfo) { 973 nsCString proxyType; 974 nsresult rv = aProxyInfo->GetType(proxyType); 975 if (NS_WARN_IF(NS_FAILED(rv))) { 976 Close(); 977 return rv; 978 } 979 // Only supports SOCKS proxy for now. 980 if (proxyType == "socks" || proxyType == "socks4") { 981 return Init(aProxyInfo); 982 } 983 } 984 return Init(nullptr); 985 } 986 987 nsresult TCPSocket::CreateInputStreamPump() { 988 if (!mSocketInputStream) { 989 return NS_ERROR_NOT_AVAILABLE; 990 } 991 nsresult rv; 992 mInputStreamPump = 993 do_CreateInstance("@mozilla.org/network/input-stream-pump;1", &rv); 994 NS_ENSURE_SUCCESS(rv, rv); 995 996 rv = mInputStreamPump->Init(mSocketInputStream, 0, 0, false, nullptr); 997 NS_ENSURE_SUCCESS(rv, rv); 998 999 uint64_t suspendCount = mSuspendCount; 1000 while (suspendCount--) { 1001 mInputStreamPump->Suspend(); 1002 } 1003 1004 rv = mInputStreamPump->AsyncRead(this); 1005 NS_ENSURE_SUCCESS(rv, rv); 1006 return NS_OK; 1007 } 1008 1009 NS_IMETHODIMP 1010 TCPSocket::OnTransportStatus(nsITransport* aTransport, nsresult aStatus, 1011 int64_t aProgress, int64_t aProgressMax) { 1012 if (static_cast<uint32_t>(aStatus) != 1013 static_cast<uint32_t>(nsISocketTransport::STATUS_CONNECTED_TO)) { 1014 return NS_OK; 1015 } 1016 1017 mReadyState = TCPReadyState::Open; 1018 nsresult rv = CreateInputStreamPump(); 1019 NS_ENSURE_SUCCESS(rv, rv); 1020 FireEvent(u"open"_ns); 1021 1022 return NS_OK; 1023 } 1024 1025 NS_IMETHODIMP 1026 TCPSocket::OnInputStreamReady(nsIAsyncInputStream* aStream) { 1027 // Only used for detecting if the connection was refused. 1028 1029 uint64_t dummy; 1030 nsresult rv = aStream->Available(&dummy); 1031 if (NS_FAILED(rv)) { 1032 MaybeReportErrorAndCloseIfOpen(NS_ERROR_CONNECTION_REFUSED); 1033 } 1034 return NS_OK; 1035 } 1036 1037 NS_IMETHODIMP 1038 TCPSocket::OnStartRequest(nsIRequest* aRequest) { return NS_OK; } 1039 1040 NS_IMETHODIMP 1041 TCPSocket::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aStream, 1042 uint64_t aOffset, uint32_t aCount) { 1043 if (mUseArrayBuffers) { 1044 nsTArray<uint8_t> buffer; 1045 buffer.SetCapacity(aCount); 1046 uint32_t actual; 1047 nsresult rv = aStream->Read(reinterpret_cast<char*>(buffer.Elements()), 1048 aCount, &actual); 1049 NS_ENSURE_SUCCESS(rv, rv); 1050 MOZ_ASSERT(actual == aCount); 1051 buffer.SetLength(actual); 1052 1053 if (mSocketBridgeParent) { 1054 mSocketBridgeParent->FireArrayBufferDataEvent(buffer, mReadyState); 1055 return NS_OK; 1056 } 1057 1058 AutoJSAPI api; 1059 if (!api.Init(GetOwnerGlobal())) { 1060 return NS_ERROR_FAILURE; 1061 } 1062 JSContext* cx = api.cx(); 1063 1064 JS::Rooted<JS::Value> value(cx); 1065 if (!ToJSValue(cx, TypedArrayCreator<ArrayBuffer>(buffer), &value)) { 1066 return NS_ERROR_FAILURE; 1067 } 1068 FireDataEvent(cx, u"data"_ns, value); 1069 return NS_OK; 1070 } 1071 1072 nsCString data; 1073 nsresult rv = mInputStreamScriptable->ReadBytes(aCount, data); 1074 NS_ENSURE_SUCCESS(rv, rv); 1075 1076 if (mSocketBridgeParent) { 1077 mSocketBridgeParent->FireStringDataEvent(data, mReadyState); 1078 return NS_OK; 1079 } 1080 1081 AutoJSAPI api; 1082 if (!api.Init(GetOwnerGlobal())) { 1083 return NS_ERROR_FAILURE; 1084 } 1085 JSContext* cx = api.cx(); 1086 1087 JS::Rooted<JS::Value> value(cx); 1088 if (!ToJSValue(cx, NS_ConvertASCIItoUTF16(data), &value)) { 1089 return NS_ERROR_FAILURE; 1090 } 1091 FireDataEvent(cx, u"data"_ns, value); 1092 1093 return NS_OK; 1094 } 1095 1096 NS_IMETHODIMP 1097 TCPSocket::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) { 1098 mInputStreamPump = nullptr; 1099 1100 if (mAsyncCopierActive && NS_SUCCEEDED(aStatus)) { 1101 // If we have some buffered output still, and status is not an 1102 // error, the other side has done a half-close, but we don't 1103 // want to be in the close state until we are done sending 1104 // everything that was buffered. We also don't want to call onclose 1105 // yet. 1106 return NS_OK; 1107 } 1108 1109 // We call this even if there is no error. 1110 MaybeReportErrorAndCloseIfOpen(aStatus); 1111 return NS_OK; 1112 } 1113 1114 void TCPSocket::SetSocketBridgeParent(TCPSocketParent* aBridgeParent) { 1115 MOZ_ASSERT(NS_IsMainThread()); 1116 1117 mSocketBridgeParent = aBridgeParent; 1118 } 1119 1120 NS_IMETHODIMP 1121 TCPSocket::UpdateReadyState(uint32_t aReadyState) { 1122 MOZ_ASSERT(mSocketBridgeChild); 1123 mReadyState = static_cast<TCPReadyState>(aReadyState); 1124 return NS_OK; 1125 } 1126 1127 NS_IMETHODIMP 1128 TCPSocket::UpdateBufferedAmount(uint32_t aBufferedAmount, 1129 uint32_t aTrackingNumber) { 1130 if (aTrackingNumber != mTrackingNumber) { 1131 return NS_OK; 1132 } 1133 mBufferedAmount = aBufferedAmount; 1134 if (!mBufferedAmount) { 1135 if (mWaitingForDrain) { 1136 mWaitingForDrain = false; 1137 return FireEvent(u"drain"_ns); 1138 } 1139 } 1140 return NS_OK; 1141 } 1142 1143 NS_IMETHODIMP 1144 TCPSocket::Observe(nsISupports* aSubject, const char* aTopic, 1145 const char16_t* aData) { 1146 if (!strcmp(aTopic, "inner-window-destroyed")) { 1147 nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject); 1148 NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE); 1149 uint64_t innerID; 1150 nsresult rv = wrapper->GetData(&innerID); 1151 if (NS_WARN_IF(NS_FAILED(rv))) { 1152 return rv; 1153 } 1154 1155 if (innerID == mInnerWindowID) { 1156 Close(); 1157 } 1158 } else if (!strcmp(aTopic, "profile-change-net-teardown")) { 1159 Close(); 1160 } 1161 1162 return NS_OK; 1163 } 1164 1165 /* static */ 1166 bool TCPSocket::ShouldTCPSocketExist(JSContext* aCx, JSObject* aGlobal) { 1167 JS::Rooted<JSObject*> global(aCx, aGlobal); 1168 return nsContentUtils::ObjectPrincipal(global)->IsSystemPrincipal(); 1169 }