Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/network/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 34 kB image not shown  

Quelle  TCPSocket.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "TCPServerSocket.h"
#include "TCPSocket.h"
#include "TCPSocketChild.h"
#include "TCPSocketParent.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/TCPSocketBinding.h"
#include "mozilla/dom/TCPSocketErrorEvent.h"
#include "mozilla/dom/TCPSocketErrorEventBinding.h"
#include "mozilla/dom/TCPSocketEvent.h"
#include "mozilla/dom/TCPSocketEventBinding.h"
#include "mozilla/dom/ToJSValue.h"
#include "nsComponentManagerUtils.h"
#include "nsContentUtils.h"
#include "nsIArrayBufferInputStream.h"
#include "nsIAsyncInputStream.h"
#include "nsIAsyncStreamCopier.h"
#include "nsIBinaryInputStream.h"
#include "nsICancelable.h"
#include "nsIChannel.h"
#include "nsIInputStream.h"
#include "nsIInputStreamPump.h"
#include "nsIMultiplexInputStream.h"
#include "nsINSSErrorsService.h"
#include "nsIObserverService.h"
#include "nsIOutputStream.h"
#include "nsIProtocolProxyService.h"
#include "nsIScriptableInputStream.h"
#include "nsISocketTransport.h"
#include "nsISocketTransportService.h"
#include "nsISupportsPrimitives.h"
#include "nsITLSSocketControl.h"
#include "nsITransport.h"
#include "nsIURIMutator.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsStringStream.h"
#include "secerr.h"
#include "sslerr.h"

using namespace mozilla::dom;

NS_IMPL_CYCLE_COLLECTION(LegacyMozTCPSocket, mGlobal)

NS_IMPL_CYCLE_COLLECTING_ADDREF(LegacyMozTCPSocket)
NS_IMPL_CYCLE_COLLECTING_RELEASE(LegacyMozTCPSocket)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LegacyMozTCPSocket)
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

LegacyMozTCPSocket::LegacyMozTCPSocket(nsPIDOMWindowInner* aWindow)
    : mGlobal(do_QueryInterface(aWindow)) {}

LegacyMozTCPSocket::~LegacyMozTCPSocket() = default;

already_AddRefed<TCPSocket> LegacyMozTCPSocket::Open(
    const nsAString& aHost, uint16_t aPort, const SocketOptions& aOptions,
    mozilla::ErrorResult& aRv) {
  AutoJSAPI api;
  if (NS_WARN_IF(!api.Init(mGlobal))) {
    aRv.Throw(NS_ERROR_FAILURE);
    return nullptr;
  }
  GlobalObject globalObj(api.cx(), mGlobal->GetGlobalJSObject());
  return TCPSocket::Constructor(globalObj, aHost, aPort, aOptions, aRv);
}

already_AddRefed<TCPServerSocket> LegacyMozTCPSocket::Listen(
    uint16_t aPort, const ServerSocketOptions& aOptions, uint16_t aBacklog,
    mozilla::ErrorResult& aRv) {
  AutoJSAPI api;
  if (NS_WARN_IF(!api.Init(mGlobal))) {
    return nullptr;
  }
  GlobalObject globalObj(api.cx(), mGlobal->GetGlobalJSObject());
  return TCPServerSocket::Constructor(globalObj, aPort, aOptions, aBacklog,
                                      aRv);
}

bool LegacyMozTCPSocket::WrapObject(JSContext* aCx,
                                    JS::Handle<JSObject*> aGivenProto,
                                    JS::MutableHandle<JSObject*> aReflector) {
  return LegacyMozTCPSocket_Binding::Wrap(aCx, this, aGivenProto, aReflector);
}

NS_IMPL_CYCLE_COLLECTION_CLASS(TCPSocket)

NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(TCPSocket, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRACE_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TCPSocket,
                                                  DOMEventTargetHelper)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransport)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketInputStream)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketOutputStream)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamPump)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamScriptable)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamBinary)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingDataAfterStartTLS)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketBridgeChild)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketBridgeParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TCPSocket, DOMEventTargetHelper)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransport)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketInputStream)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketOutputStream)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamPump)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamScriptable)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamBinary)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingDataAfterStartTLS)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketBridgeChild)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketBridgeParent)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_ADDREF_INHERITED(TCPSocket, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(TCPSocket, DOMEventTargetHelper)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocket)
  NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
  NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  NS_INTERFACE_MAP_ENTRY(nsITCPSocketCallback)
  NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)

TCPSocket::TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost,
                     uint16_t aPort, bool aSsl, bool aUseArrayBuffers)
    : DOMEventTargetHelper(aGlobal),
      mReadyState(TCPReadyState::Closed),
      mUseArrayBuffers(aUseArrayBuffers),
      mHost(aHost),
      mPort(aPort),
      mSsl(aSsl),
      mAsyncCopierActive(false),
      mWaitingForDrain(false),
      mInnerWindowID(0),
      mBufferedAmount(0),
      mSuspendCount(0),
      mTrackingNumber(0),
      mWaitingForStartTLS(false),
      mObserversActive(false) {
  if (aGlobal) {
    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
    if (window) {
      mInnerWindowID = window->WindowID();
    }
  }
}

TCPSocket::~TCPSocket() {
  if (mObserversActive) {
    nsCOMPtr<nsIObserverService> obs =
        do_GetService("@mozilla.org/observer-service;1");
    if (obs) {
      obs->RemoveObserver(this"inner-window-destroyed");
      obs->RemoveObserver(this"profile-change-net-teardown");
    }
  }
}

nsresult TCPSocket::CreateStream() {
  nsresult rv =
      mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(mSocketInputStream));
  NS_ENSURE_SUCCESS(rv, rv);
  rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
                                    getter_AddRefs(mSocketOutputStream));
  NS_ENSURE_SUCCESS(rv, rv);

  // If the other side is not listening, we will
  // get an onInputStreamReady callback where available
  // raises to indicate the connection was refused.
  nsCOMPtr<nsIAsyncInputStream> asyncStream =
      do_QueryInterface(mSocketInputStream);
  NS_ENSURE_TRUE(asyncStream, NS_ERROR_NOT_AVAILABLE);

  nsCOMPtr<nsISerialEventTarget> mainTarget = GetMainThreadSerialEventTarget();
  rv = asyncStream->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0,
                              mainTarget);
  NS_ENSURE_SUCCESS(rv, rv);

  if (mUseArrayBuffers) {
    mInputStreamBinary =
        do_CreateInstance("@mozilla.org/binaryinputstream;1", &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    rv = mInputStreamBinary->SetInputStream(mSocketInputStream);
    NS_ENSURE_SUCCESS(rv, rv);
  } else {
    mInputStreamScriptable =
        do_CreateInstance("@mozilla.org/scriptableinputstream;1", &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    rv = mInputStreamScriptable->Init(mSocketInputStream);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  return NS_OK;
}

nsresult TCPSocket::InitWithUnconnectedTransport(
    nsISocketTransport* aTransport) {
  mReadyState = TCPReadyState::Connecting;
  mTransport = aTransport;

  MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content);

  nsCOMPtr<nsISerialEventTarget> mainTarget = GetMainThreadSerialEventTarget();
  mTransport->SetEventSink(this, mainTarget);

  nsresult rv = CreateStream();
  NS_ENSURE_SUCCESS(rv, rv);

  return NS_OK;
}

nsresult TCPSocket::Init(nsIProxyInfo* aProxyInfo) {
  nsCOMPtr<nsIObserverService> obs =
      do_GetService("@mozilla.org/observer-service;1");
  if (obs) {
    mObserversActive = true;
    obs->AddObserver(this"inner-window-destroyed"true);  // weak reference
    obs->AddObserver(this"profile-change-net-teardown"true);  // weak ref
  }

  if (XRE_IsContentProcess()) {
    mReadyState = TCPReadyState::Connecting;

    nsCOMPtr<nsISerialEventTarget> target;
    if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
      target = global->SerialEventTarget();
    }
    mSocketBridgeChild = new TCPSocketChild(mHost, mPort, target);
    mSocketBridgeChild->SendOpen(this, mSsl, mUseArrayBuffers);
    return NS_OK;
  }

  nsCOMPtr<nsISocketTransportService> sts =
      do_GetService("@mozilla.org/network/socket-transport-service;1");

  AutoTArray<nsCString, 1> socketTypes;
  if (mSsl) {
    socketTypes.AppendElement("ssl"_ns);
  } else {
    socketTypes.AppendElement("starttls"_ns);
  }
  nsCOMPtr<nsISocketTransport> transport;
  nsresult rv =
      sts->CreateTransport(socketTypes, NS_ConvertUTF16toUTF8(mHost), mPort,
                           aProxyInfo, nullptr, getter_AddRefs(transport));
  NS_ENSURE_SUCCESS(rv, rv);

  return InitWithUnconnectedTransport(transport);
}

void TCPSocket::InitWithSocketChild(TCPSocketChild* aSocketBridge) {
  mSocketBridgeChild = aSocketBridge;
  mReadyState = TCPReadyState::Open;
  mSocketBridgeChild->SetSocket(this);
  mSocketBridgeChild->GetHost(mHost);
  mSocketBridgeChild->GetPort(&mPort);
}

nsresult TCPSocket::InitWithTransport(nsISocketTransport* aTransport) {
  mTransport = aTransport;
  nsresult rv = CreateStream();
  NS_ENSURE_SUCCESS(rv, rv);

  mReadyState = TCPReadyState::Open;
  rv = CreateInputStreamPump();
  NS_ENSURE_SUCCESS(rv, rv);

  nsAutoCString host;
  mTransport->GetHost(host);
  CopyUTF8toUTF16(host, mHost);
  int32_t port;
  mTransport->GetPort(&port);
  mPort = port;

  return NS_OK;
}

void TCPSocket::UpgradeToSecure(mozilla::ErrorResult& aRv) {
  if (mReadyState != TCPReadyState::Open) {
    aRv.Throw(NS_ERROR_FAILURE);
    return;
  }

  if (mSsl) {
    return;
  }

  mSsl = true;

  if (mSocketBridgeChild) {
    mSocketBridgeChild->SendStartTLS();
    return;
  }

  if (!mAsyncCopierActive) {
    ActivateTLS();
  } else {
    mWaitingForStartTLS = true;
  }
}

namespace {
class CopierCallbacks final : public nsIRequestObserver {
  RefPtr<TCPSocket> mOwner;

 public:
  explicit CopierCallbacks(TCPSocket* aSocket) : mOwner(aSocket) {}

  NS_DECL_ISUPPORTS
  NS_DECL_NSIREQUESTOBSERVER
 private:
  ~CopierCallbacks() = default;
};

NS_IMPL_ISUPPORTS(CopierCallbacks, nsIRequestObserver)

NS_IMETHODIMP
CopierCallbacks::OnStartRequest(nsIRequest* aRequest) { return NS_OK; }

NS_IMETHODIMP
CopierCallbacks::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
  mOwner->NotifyCopyComplete(aStatus);
  mOwner = nullptr;
  return NS_OK;
}
}  // unnamed namespace

void TCPSocket::CalculateBufferedAmount() {
  // Let's update the buffered amount of data.
  uint64_t bufferedAmount = 0;
  for (uint32_t i = 0, len = mPendingData.Length(); i < len; ++i) {
    nsCOMPtr<nsIInputStream> stream = mPendingData[i];
    uint64_t available = 0;
    if (NS_SUCCEEDED(stream->Available(&available))) {
      bufferedAmount += available;
    }
  }
  mBufferedAmount = bufferedAmount;
}

nsresult TCPSocket::EnsureCopying() {
  if (mAsyncCopierActive) {
    return NS_OK;
  }

  mAsyncCopierActive = true;

  nsresult rv;

  nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
      do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIInputStream> stream = do_QueryInterface(multiplexStream);

  while (!mPendingData.IsEmpty()) {
    nsCOMPtr<nsIInputStream> stream = mPendingData[0];
    multiplexStream->AppendStream(stream);
    mPendingData.RemoveElementAt(0);
  }

  nsCOMPtr<nsIAsyncStreamCopier> copier =
      do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsISocketTransportService> sts =
      do_GetService("@mozilla.org/network/socket-transport-service;1");

  nsCOMPtr<nsISerialEventTarget> target = do_QueryInterface(sts);
  rv = copier->Init(stream, mSocketOutputStream, target,
                    true,               /* source buffered */
                    false,              /* sink buffered */
                    BUFFER_SIZE, false/* close source */
                    false);             /* close sink */
  NS_ENSURE_SUCCESS(rv, rv);

  RefPtr<CopierCallbacks> callbacks = new CopierCallbacks(this);
  rv = copier->AsyncCopy(callbacks, nullptr);
  NS_ENSURE_SUCCESS(rv, rv);

  return NS_OK;
}

void TCPSocket::NotifyCopyComplete(nsresult aStatus) {
  mAsyncCopierActive = false;
  CalculateBufferedAmount();

  if (mSocketBridgeParent && mSocketBridgeParent->IPCOpen()) {
    mozilla::Unused << mSocketBridgeParent->SendUpdateBufferedAmount(
        BufferedAmount(), mTrackingNumber);
  }

  if (NS_FAILED(aStatus)) {
    MaybeReportErrorAndCloseIfOpen(aStatus);
    return;
  }

  if (BufferedAmount() != 0) {
    EnsureCopying();
    return;
  }

  // Maybe we have some empty stream. We want to have an empty queue now.
  mPendingData.Clear();

  // If we are waiting for initiating starttls, we can begin to
  // activate tls now.
  if (mWaitingForStartTLS && mReadyState == TCPReadyState::Open) {
    ActivateTLS();
    mWaitingForStartTLS = false;
    // If we have pending data, we should send them, or fire
    // a drain event if we are waiting for it.
    if (!mPendingDataAfterStartTLS.IsEmpty()) {
      mPendingData = std::move(mPendingDataAfterStartTLS);
      EnsureCopying();
      return;
    }
  }

  // If we have a connected child, we let the child decide whether
  // ondrain should be dispatched.
  if (mWaitingForDrain && !mSocketBridgeParent) {
    mWaitingForDrain = false;
    FireEvent(u"drain"_ns);
  }

  if (mReadyState == TCPReadyState::Closing) {
    if (mSocketOutputStream) {
      mSocketOutputStream->Close();
      mSocketOutputStream = nullptr;
    }
    mReadyState = TCPReadyState::Closed;
    FireEvent(u"close"_ns);
  }
}

void TCPSocket::ActivateTLS() {
  nsresult rv;
  nsCOMPtr<nsIEventTarget> socketThread =
      do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
  if (NS_FAILED(rv)) {
    return;
  }

  bool alreadyOnSTST = false;
  if (NS_FAILED(socketThread->IsOnCurrentThread(&alreadyOnSTST))) {
    return;
  }

  if (alreadyOnSTST) {
    ActivateTLSHelper();
    return;
  }

  auto CallActivateTLS = [sock = RefPtr{this}]() mutable {
    sock->ActivateTLSHelper();
  };
  mozilla::SyncRunnable::DispatchToThread(
      socketThread,
      NS_NewRunnableFunction("TCPSocket::UpgradeToSecure->ActivateTLSHelper",
                             CallActivateTLS));
}

void TCPSocket::ActivateTLSHelper() {
  nsCOMPtr<nsITLSSocketControl> tlsSocketControl;
  mTransport->GetTlsSocketControl(getter_AddRefs(tlsSocketControl));
  if (tlsSocketControl) {
    tlsSocketControl->StartTLS();
  }
}

NS_IMETHODIMP
TCPSocket::FireErrorEvent(const nsAString& aName, const nsAString& aType,
                          nsresult aErrorCode) {
  if (mSocketBridgeParent) {
    mSocketBridgeParent->FireErrorEvent(aName, aType, aErrorCode, mReadyState);
    return NS_OK;
  }

  TCPSocketErrorEventInit init;
  init.mBubbles = false;
  init.mCancelable = false;
  init.mName = aName;
  init.mMessage = aType;
  static_assert(std::is_same_v<std::underlying_type_t<nsresult>, uint32_t>);
  init.mErrorCode = uint32_t(aErrorCode);

  RefPtr<TCPSocketErrorEvent> event =
      TCPSocketErrorEvent::Constructor(this, u"error"_ns, init);
  MOZ_ASSERT(event);
  event->SetTrusted(true);
  DispatchEvent(*event);
  return NS_OK;
}

NS_IMETHODIMP
TCPSocket::FireEvent(const nsAString& aType) {
  if (mSocketBridgeParent) {
    mSocketBridgeParent->FireEvent(aType, mReadyState);
    return NS_OK;
  }

  AutoJSAPI api;
  if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) {
    return NS_ERROR_FAILURE;
  }
  JS::Rooted<JS::Value> val(api.cx());
  return FireDataEvent(api.cx(), aType, val);
}

NS_IMETHODIMP
TCPSocket::FireDataArrayEvent(const nsAString& aType,
                              const nsTArray<uint8_t>& buffer) {
  AutoJSAPI api;
  if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) {
    return NS_ERROR_FAILURE;
  }
  JSContext* cx = api.cx();
  JS::Rooted<JS::Value> val(cx);

  bool ok = IPC::DeserializeArrayBuffer(cx, buffer, &val);
  if (ok) {
    return FireDataEvent(cx, aType, val);
  }
  return NS_ERROR_FAILURE;
}

NS_IMETHODIMP
TCPSocket::FireDataStringEvent(const nsAString& aType,
                               const nsACString& aString) {
  AutoJSAPI api;
  if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) {
    return NS_ERROR_FAILURE;
  }
  JSContext* cx = api.cx();
  JS::Rooted<JS::Value> val(cx);

  bool ok = ToJSValue(cx, NS_ConvertASCIItoUTF16(aString), &val);
  if (ok) {
    return FireDataEvent(cx, aType, val);
  }
  return NS_ERROR_FAILURE;
}

nsresult TCPSocket::FireDataEvent(JSContext* aCx, const nsAString& aType,
                                  JS::Handle<JS::Value> aData) {
  MOZ_ASSERT(!mSocketBridgeParent);

  RootedDictionary<TCPSocketEventInit> init(aCx);
  init.mBubbles = false;
  init.mCancelable = false;
  init.mData = aData;

  RefPtr<TCPSocketEvent> event = TCPSocketEvent::Constructor(this, aType, init);
  event->SetTrusted(true);
  DispatchEvent(*event);
  return NS_OK;
}

JSObject* TCPSocket::WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) {
  return TCPSocket_Binding::Wrap(aCx, this, aGivenProto);
}

void TCPSocket::GetHost(nsAString& aHost) { aHost.Assign(mHost); }

uint32_t TCPSocket::Port() const { return mPort; }

bool TCPSocket::Ssl() const { return mSsl; }

void TCPSocket::Suspend() {
  if (mSocketBridgeChild) {
    mSocketBridgeChild->SendSuspend();
    return;
  }
  if (mInputStreamPump) {
    mInputStreamPump->Suspend();
  }
  mSuspendCount++;
}

void TCPSocket::Resume(mozilla::ErrorResult& aRv) {
  if (mSocketBridgeChild) {
    mSocketBridgeChild->SendResume();
    return;
  }
  if (!mSuspendCount) {
    aRv.Throw(NS_ERROR_FAILURE);
    return;
  }

  if (mInputStreamPump) {
    mInputStreamPump->Resume();
  }
  mSuspendCount--;
}

nsresult TCPSocket::MaybeReportErrorAndCloseIfOpen(nsresult status) {
  // If we're closed, we've already reported the error or just don't need to
  // report the error.
  if (mReadyState == TCPReadyState::Closed) {
    return NS_OK;
  }

  // go through ::Closing state and then mark ::Closed
  Close();
  mReadyState = TCPReadyState::Closed;

  if (NS_FAILED(status)) {
    // Convert the status code to an appropriate error message.

    nsString errorType, errName;

    // security module? (and this is an error)
    if ((static_cast<uint32_t>(status) & 0xFF0000) == 0x5a0000) {
      nsCOMPtr<nsINSSErrorsService> errSvc =
          do_GetService("@mozilla.org/nss_errors_service;1");
      // getErrorClass will throw a generic NS_ERROR_FAILURE if the error code
      // is somehow not in the set of covered errors.
      uint32_t errorClass;
      nsresult rv = errSvc->GetErrorClass(status, &errorClass);
      if (NS_FAILED(rv)) {
        errorType.AssignLiteral("SecurityProtocol");
      } else {
        switch (errorClass) {
          case nsINSSErrorsService::ERROR_CLASS_BAD_CERT:
            errorType.AssignLiteral("SecurityCertificate");
            break;
          default:
            errorType.AssignLiteral("SecurityProtocol");
            break;
        }
      }

      // NSS_SEC errors (happen below the base value because of negative vals)
      if ((static_cast<int32_t>(status) & 0xFFFF) <
          abs(nsINSSErrorsService::NSS_SEC_ERROR_BASE)) {
        switch (static_cast<SECErrorCodes>(status)) {
          case SEC_ERROR_EXPIRED_CERTIFICATE:
            errName.AssignLiteral("SecurityExpiredCertificateError");
            break;
          case SEC_ERROR_REVOKED_CERTIFICATE:
            errName.AssignLiteral("SecurityRevokedCertificateError");
            break;
            // per bsmith, we will be unable to tell these errors apart very
            // soon, so it makes sense to just folder them all together already.
          case SEC_ERROR_UNKNOWN_ISSUER:
          case SEC_ERROR_UNTRUSTED_ISSUER:
          case SEC_ERROR_UNTRUSTED_CERT:
          case SEC_ERROR_CA_CERT_INVALID:
            errName.AssignLiteral("SecurityUntrustedCertificateIssuerError");
            break;
          case SEC_ERROR_INADEQUATE_KEY_USAGE:
            errName.AssignLiteral("SecurityInadequateKeyUsageError");
            break;
          case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
            errName.AssignLiteral(
                "SecurityCertificateSignatureAlgorithmDisabledError");
            break;
          default:
            errName.AssignLiteral("SecurityError");
            break;
        }
      } else {
        // NSS_SSL errors
        switch (static_cast<SSLErrorCodes>(status)) {
          case SSL_ERROR_NO_CERTIFICATE:
            errName.AssignLiteral("SecurityNoCertificateError");
            break;
          case SSL_ERROR_BAD_CERTIFICATE:
            errName.AssignLiteral("SecurityBadCertificateError");
            break;
          case SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE:
            errName.AssignLiteral("SecurityUnsupportedCertificateTypeError");
            break;
          case SSL_ERROR_UNSUPPORTED_VERSION:
            errName.AssignLiteral("SecurityUnsupportedTLSVersionError");
            break;
          case SSL_ERROR_BAD_CERT_DOMAIN:
            errName.AssignLiteral("SecurityCertificateDomainMismatchError");
            break;
          default:
            errName.AssignLiteral("SecurityError");
            break;
        }
      }
    } else {
      // must be network
      errorType.AssignLiteral("Network");

      switch (status) {
        // connect to host:port failed
        case NS_ERROR_CONNECTION_REFUSED:
          errName.AssignLiteral("ConnectionRefusedError");
          break;
          // network timeout error
        case NS_ERROR_NET_TIMEOUT:
          errName.AssignLiteral("NetworkTimeoutError");
          break;
          // hostname lookup failed
        case NS_ERROR_UNKNOWN_HOST:
          errName.AssignLiteral("DomainNotFoundError");
          break;
        case NS_ERROR_NET_INTERRUPT:
          errName.AssignLiteral("NetworkInterruptError");
          break;
        default:
          errName.AssignLiteral("NetworkError");
          break;
      }
    }

    Unused << NS_WARN_IF(NS_FAILED(FireErrorEvent(errName, errorType, status)));
  }

  return FireEvent(u"close"_ns);
}

void TCPSocket::Close() { CloseHelper(true); }

void TCPSocket::CloseImmediately() { CloseHelper(false); }

void TCPSocket::CloseHelper(bool waitForUnsentData) {
  if (mReadyState == TCPReadyState::Closed ||
      mReadyState == TCPReadyState::Closing) {
    return;
  }

  mReadyState = TCPReadyState::Closing;

  if (mProxyRequest) {
    mProxyRequest->Cancel(NS_BINDING_ABORTED);
    mProxyRequest = nullptr;
  }

  if (mSocketBridgeChild) {
    mSocketBridgeChild->SendClose();
    return;
  }

  if (!mAsyncCopierActive || !waitForUnsentData) {
    mPendingData.Clear();
    mPendingDataAfterStartTLS.Clear();

    if (mSocketOutputStream) {
      mSocketOutputStream->Close();
      mSocketOutputStream = nullptr;
    }
  }

  if (mSocketInputStream) {
    mSocketInputStream->Close();
    mSocketInputStream = nullptr;
  }
}

bool TCPSocket::Send(const nsACString& aData, mozilla::ErrorResult& aRv) {
  if (mReadyState != TCPReadyState::Open) {
    aRv.Throw(NS_ERROR_FAILURE);
    return false;
  }

  uint64_t byteLength;
  nsCOMPtr<nsIInputStream> stream;
  if (mSocketBridgeChild) {
    mSocketBridgeChild->SendSend(aData);
    byteLength = aData.Length();
  } else {
    nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), aData);
    if (NS_FAILED(rv)) {
      aRv.Throw(rv);
      return false;
    }
    rv = stream->Available(&byteLength);
    if (NS_FAILED(rv)) {
      aRv.Throw(rv);
      return false;
    }
  }
  return Send(stream, byteLength);
}

bool TCPSocket::Send(const ArrayBuffer& aData, uint32_t aByteOffset,
                     const Optional<uint32_t>& aByteLength,
                     mozilla::ErrorResult& aRv) {
  if (mReadyState != TCPReadyState::Open) {
    aRv.Throw(NS_ERROR_FAILURE);
    return false;
  }

  nsCOMPtr<nsIArrayBufferInputStream> stream;

  uint32_t nbytes;
  auto calculateOffsetAndCount = [&](uint32_t aLength) {
    uint32_t offset = std::min(aLength, aByteOffset);
    nbytes = std::min(aLength - aByteOffset,
                      aByteLength.WasPassed() ? aByteLength.Value() : aLength);
    return std::pair(offset, nbytes);
  };

  if (mSocketBridgeChild) {
    nsTArray<uint8_t> arrayBuffer;
    if (!aData.AppendDataTo(arrayBuffer, calculateOffsetAndCount)) {
      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
      return false;
    }

    mSocketBridgeChild->SendSend(std::move(arrayBuffer));
  } else {
    mozilla::Maybe<mozilla::UniquePtr<uint8_t[]>> arrayBuffer =
        aData.CreateFromData<mozilla::UniquePtr<uint8_t[]>>(
            calculateOffsetAndCount);
    if (arrayBuffer.isNothing()) {
      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
      return false;
    }

    stream = do_CreateInstance("@mozilla.org/io/arraybuffer-input-stream;1");
    nsresult rv = stream->SetData(arrayBuffer.extract(), nbytes);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      aRv.Throw(rv);
      return false;
    }
  }
  return Send(stream, nbytes);
}

bool TCPSocket::Send(nsIInputStream* aStream, uint32_t aByteLength) {
  uint64_t newBufferedAmount = BufferedAmount() + aByteLength;
  bool bufferFull = newBufferedAmount > BUFFER_SIZE;
  if (bufferFull) {
    // If we buffered more than some arbitrary amount of data,
    // (65535 right now) we should tell the caller so they can
    // wait until ondrain is called if they so desire. Once all the
    // buffered data has been written to the socket, ondrain is
    // called.
    mWaitingForDrain = true;
  }

  if (mSocketBridgeChild) {
    // In the child, we just add the buffer length to our bufferedAmount and let
    // the parent update our bufferedAmount when the data have been sent.
    mBufferedAmount = newBufferedAmount;
    return !bufferFull;
  }

  // This is used to track how many packets we have been told to send. Signaling
  // this back to the user of this API allows the user to know how many packets
  // are currently in flight over IPC.
  ++mTrackingNumber;
  if (mWaitingForStartTLS) {
    // When we are waiting for starttls, newStream is added to pendingData
    // and will be appended to multiplexStream after tls had been set up.
    mPendingDataAfterStartTLS.AppendElement(aStream);
  } else {
    mPendingData.AppendElement(aStream);
  }

  CalculateBufferedAmount();
  EnsureCopying();

  return !bufferFull;
}

TCPReadyState TCPSocket::ReadyState() { return mReadyState; }

TCPSocketBinaryType TCPSocket::BinaryType() const {
  if (mUseArrayBuffers) {
    return TCPSocketBinaryType::Arraybuffer;
  }
  return TCPSocketBinaryType::String;
}

already_AddRefed<TCPSocket> TCPSocket::CreateAcceptedSocket(
    nsIGlobalObject* aGlobal, nsISocketTransport* aTransport,
    bool aUseArrayBuffers) {
  RefPtr<TCPSocket> socket =
      new TCPSocket(aGlobal, u""_ns, 0, false, aUseArrayBuffers);
  nsresult rv = socket->InitWithTransport(aTransport);
  NS_ENSURE_SUCCESS(rv, nullptr);
  return socket.forget();
}

already_AddRefed<TCPSocket> TCPSocket::CreateAcceptedSocket(
    nsIGlobalObject* aGlobal, TCPSocketChild* aBridge, bool aUseArrayBuffers) {
  RefPtr<TCPSocket> socket =
      new TCPSocket(aGlobal, u""_ns, 0, false, aUseArrayBuffers);
  socket->InitWithSocketChild(aBridge);
  return socket.forget();
}

already_AddRefed<TCPSocket> TCPSocket::Constructor(
    const GlobalObject& aGlobal, const nsAString& aHost, uint16_t aPort,
    const SocketOptions& aOptions, mozilla::ErrorResult& aRv) {
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
  RefPtr<TCPSocket> socket =
      new TCPSocket(global, aHost, aPort, aOptions.mUseSecureTransport,
                    aOptions.mBinaryType == TCPSocketBinaryType::Arraybuffer);
  socket->ResolveProxy();

  return socket.forget();
}

nsresult TCPSocket::ResolveProxy() {
  nsresult rv;
  nsCOMPtr<nsIProtocolProxyService> pps =
      do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  nsCOMPtr<nsIURI> uri;
  nsCString spec = mSsl ? "https://"_ns : "http://"_ns;
  bool maybeIPv6 = mHost.FindChar(':') != -1;
  if (maybeIPv6) spec.Append('[');
  if (!AppendUTF16toUTF8(mHost, spec, fallible)) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  if (maybeIPv6) spec.Append(']');
  rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
           .SetSpec(spec)
           .SetPort(mPort)
           .Finalize(uri);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  nsCOMPtr<nsIChannel> channel;
  rv = NS_NewChannel(getter_AddRefs(channel), uri,
                     nsContentUtils::GetSystemPrincipal(),
                     nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
                     nsIContentPolicy::TYPE_OTHER);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  rv = pps->AsyncResolve(channel,
                         nsIProtocolProxyService::RESOLVE_PREFER_SOCKS_PROXY,
                         this, nullptr, getter_AddRefs(mProxyRequest));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  return NS_OK;
}

NS_IMETHODIMP
TCPSocket::OnProxyAvailable(nsICancelable* aRequest, nsIChannel* aChannel,
                            nsIProxyInfo* aProxyInfo, nsresult aResult) {
  mProxyRequest = nullptr;
  if (NS_SUCCEEDED(aResult) && aProxyInfo) {
    nsCString proxyType;
    nsresult rv = aProxyInfo->GetType(proxyType);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      Close();
      return rv;
    }
    // Only supports SOCKS proxy for now.
    if (proxyType == "socks" || proxyType == "socks4") {
      return Init(aProxyInfo);
    }
  }
  return Init(nullptr);
}

nsresult TCPSocket::CreateInputStreamPump() {
  if (!mSocketInputStream) {
    return NS_ERROR_NOT_AVAILABLE;
  }
  nsresult rv;
  mInputStreamPump =
      do_CreateInstance("@mozilla.org/network/input-stream-pump;1", &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = mInputStreamPump->Init(mSocketInputStream, 0, 0, false, nullptr);
  NS_ENSURE_SUCCESS(rv, rv);

  uint64_t suspendCount = mSuspendCount;
  while (suspendCount--) {
    mInputStreamPump->Suspend();
  }

  rv = mInputStreamPump->AsyncRead(this);
  NS_ENSURE_SUCCESS(rv, rv);
  return NS_OK;
}

NS_IMETHODIMP
TCPSocket::OnTransportStatus(nsITransport* aTransport, nsresult aStatus,
                             int64_t aProgress, int64_t aProgressMax) {
  if (static_cast<uint32_t>(aStatus) !=
      static_cast<uint32_t>(nsISocketTransport::STATUS_CONNECTED_TO)) {
    return NS_OK;
  }

  mReadyState = TCPReadyState::Open;
  nsresult rv = CreateInputStreamPump();
  NS_ENSURE_SUCCESS(rv, rv);
  FireEvent(u"open"_ns);

  return NS_OK;
}

NS_IMETHODIMP
TCPSocket::OnInputStreamReady(nsIAsyncInputStream* aStream) {
  // Only used for detecting if the connection was refused.

  uint64_t dummy;
  nsresult rv = aStream->Available(&dummy);
  if (NS_FAILED(rv)) {
    MaybeReportErrorAndCloseIfOpen(NS_ERROR_CONNECTION_REFUSED);
  }
  return NS_OK;
}

NS_IMETHODIMP
TCPSocket::OnStartRequest(nsIRequest* aRequest) { return NS_OK; }

NS_IMETHODIMP
TCPSocket::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aStream,
                           uint64_t aOffset, uint32_t aCount) {
  if (mUseArrayBuffers) {
    nsTArray<uint8_t> buffer;
    buffer.SetCapacity(aCount);
    uint32_t actual;
    nsresult rv = aStream->Read(reinterpret_cast<char*>(buffer.Elements()),
                                aCount, &actual);
    NS_ENSURE_SUCCESS(rv, rv);
    MOZ_ASSERT(actual == aCount);
    buffer.SetLength(actual);

    if (mSocketBridgeParent) {
      mSocketBridgeParent->FireArrayBufferDataEvent(buffer, mReadyState);
      return NS_OK;
    }

    AutoJSAPI api;
    if (!api.Init(GetOwnerGlobal())) {
      return NS_ERROR_FAILURE;
    }
    JSContext* cx = api.cx();

    JS::Rooted<JS::Value> value(cx);
    if (!ToJSValue(cx, TypedArrayCreator<ArrayBuffer>(buffer), &value)) {
      return NS_ERROR_FAILURE;
    }
    FireDataEvent(cx, u"data"_ns, value);
    return NS_OK;
  }

  nsCString data;
  nsresult rv = mInputStreamScriptable->ReadBytes(aCount, data);
  NS_ENSURE_SUCCESS(rv, rv);

  if (mSocketBridgeParent) {
    mSocketBridgeParent->FireStringDataEvent(data, mReadyState);
    return NS_OK;
  }

  AutoJSAPI api;
  if (!api.Init(GetOwnerGlobal())) {
    return NS_ERROR_FAILURE;
  }
  JSContext* cx = api.cx();

  JS::Rooted<JS::Value> value(cx);
  if (!ToJSValue(cx, NS_ConvertASCIItoUTF16(data), &value)) {
    return NS_ERROR_FAILURE;
  }
  FireDataEvent(cx, u"data"_ns, value);

  return NS_OK;
}

NS_IMETHODIMP
TCPSocket::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
  mInputStreamPump = nullptr;

  if (mAsyncCopierActive && NS_SUCCEEDED(aStatus)) {
    // If we have some buffered output still, and status is not an
    // error, the other side has done a half-close, but we don't
    // want to be in the close state until we are done sending
    // everything that was buffered. We also don't want to call onclose
    // yet.
    return NS_OK;
  }

  // We call this even if there is no error.
  MaybeReportErrorAndCloseIfOpen(aStatus);
  return NS_OK;
}

void TCPSocket::SetSocketBridgeParent(TCPSocketParent* aBridgeParent) {
  MOZ_ASSERT(NS_IsMainThread());

  mSocketBridgeParent = aBridgeParent;
}

NS_IMETHODIMP
TCPSocket::UpdateReadyState(uint32_t aReadyState) {
  MOZ_ASSERT(mSocketBridgeChild);
  mReadyState = static_cast<TCPReadyState>(aReadyState);
  return NS_OK;
}

NS_IMETHODIMP
TCPSocket::UpdateBufferedAmount(uint32_t aBufferedAmount,
                                uint32_t aTrackingNumber) {
  if (aTrackingNumber != mTrackingNumber) {
    return NS_OK;
  }
  mBufferedAmount = aBufferedAmount;
  if (!mBufferedAmount) {
    if (mWaitingForDrain) {
      mWaitingForDrain = false;
      return FireEvent(u"drain"_ns);
    }
  }
  return NS_OK;
}

NS_IMETHODIMP
TCPSocket::Observe(nsISupports* aSubject, const char* aTopic,
                   const char16_t* aData) {
  if (!strcmp(aTopic, "inner-window-destroyed")) {
    nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
    NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
    uint64_t innerID;
    nsresult rv = wrapper->GetData(&innerID);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    if (innerID == mInnerWindowID) {
      Close();
    }
  } else if (!strcmp(aTopic, "profile-change-net-teardown")) {
    Close();
  }

  return NS_OK;
}

/* static */
bool TCPSocket::ShouldTCPSocketExist(JSContext* aCx, JSObject* aGlobal) {
  JS::Rooted<JSObject*> global(aCx, aGlobal);
  return nsContentUtils::ObjectPrincipal(global)->IsSystemPrincipal();
}

99%


¤ Dauer der Verarbeitung: 0.19 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.