Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  MediaTransportHandler.cpp

  Sprache: C
 

/* 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 "MediaTransportHandler.h"

#include "MediaTransportHandlerIPC.h"
#include "transport/nricemediastream.h"
#include "transport/nriceresolver.h"
#include "transport/sigslot.h"
#include "transport/transportflow.h"
#include "transport/transportlayerdtls.h"
#include "transport/transportlayerice.h"
#include "transport/transportlayersrtp.h"

// Config stuff
#include "mozilla/IceServerParser.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/dom/RTCConfigurationBinding.h"

// Logging stuff
#include "common/browser_logging/CSFLog.h"

// For fetching ICE logging
#include "transport/rlogconnector.h"

// DTLS
#include <map>
#include <string>
#include <vector>

#include "mozilla/ProfilerMarkers.h"
#include "mozilla/PublicSSL.h"  // For psm::InitializeCipherSuite
#include "mozilla/dom/RTCStatsReportBinding.h"
#include "nsDNSService2.h"
#include "nsFmtString.h"
#include "nsISocketTransportService.h"
#include "nss.h"  // For NSS_NoDB_Init
#include "sdp/SdpAttribute.h"
#include "transport/runnable_utils.h"
#define MEDIA_TRANSPORT_HANDLER_PACKET_RECEIVED(aPacket)              \
  PROFILER_MARKER_TEXT("WebRTC Packet Received", MEDIA_RT, {},        \
                       ProfilerString8View::WrapNullTerminatedString( \
                           MediaPacket::EnumValueToString((aPacket).type())));

namespace mozilla {

static const char* mthLogTag = "MediaTransportHandler";
#ifdef LOGTAG
#  undef LOGTAG
#endif
#define LOGTAG mthLogTag

class MediaTransportHandlerSTS : public MediaTransportHandler,
                                 public sigslot::has_slots<> {
 public:
  explicit MediaTransportHandlerSTS();

  RefPtr<IceLogPromise> GetIceLog(const nsCString& aPattern) override;
  void ClearIceLog() override;
  void EnterPrivateMode() override;
  void ExitPrivateMode() override;

  void CreateIceCtx(const std::string& aName) override;

  nsresult SetIceConfig(const nsTArray<dom::RTCIceServer>& aIceServers,
                        dom::RTCIceTransportPolicy aIcePolicy) override;

  // We will probably be able to move the proxy lookup stuff into
  // this class once we move mtransport to its own process.
  void SetProxyConfig(NrSocketProxyConfig&& aProxyConfig) override;

  void EnsureProvisionalTransport(const std::string& aTransportId,
                                  const std::string& aUfrag,
                                  const std::string& aPwd,
                                  int aComponentCount) override;

  void SetTargetForDefaultLocalAddressLookup(const std::string& aTargetIp,
                                             uint16_t aTargetPort) override;

  // We set default-route-only as late as possible because it depends on what
  // capture permissions have been granted on the window, which could easily
  // change between Init (ie; when the PC is created) and StartIceGathering
  // (ie; when we set the local description).
  void StartIceGathering(bool aDefaultRouteOnly, bool aObfuscateHostAddresses,
                         // This will go away once mtransport moves to its
                         // own process, because we won't need to get this
                         // via IPC anymore
                         const nsTArray<NrIceStunAddr>& aStunAddrs) override;

  void ActivateTransport(
      const std::string& aTransportId, const std::string& aLocalUfrag,
      const std::string& aLocalPwd, size_t aComponentCount,
      const std::string& aUfrag, const std::string& aPassword,
      const nsTArray<uint8_t>& aKeyDer, const nsTArray<uint8_t>& aCertDer,
      SSLKEAType aAuthType, bool aDtlsClient, const DtlsDigestList& aDigests,
      bool aPrivacyRequested) override;

  void RemoveTransportsExcept(
      const std::set<std::string>& aTransportIds) override;

  void StartIceChecks(bool aIsControlling,
                      const std::vector<std::string>& aIceOptions) override;

  void AddIceCandidate(const std::string& aTransportId,
                       const std::string& aCandidate, const std::string& aUfrag,
                       const std::string& aObfuscatedAddress) override;

  void UpdateNetworkState(bool aOnline) override;

  void SendPacket(const std::string& aTransportId,
                  MediaPacket&& aPacket) override;

  RefPtr<dom::RTCStatsPromise> GetIceStats(const std::string& aTransportId,
                                           DOMHighResTimeStamp aNow) override;

  void Shutdown();

 private:
  void Destroy() override;
  void DestroyFinal();
  void Shutdown_s();
  RefPtr<TransportFlow> CreateTransportFlow(
      const std::string& aTransportId, bool aIsRtcp,
      const RefPtr<DtlsIdentity>& aDtlsIdentity, bool aDtlsClient,
      const DtlsDigestList& aDigests, bool aPrivacyRequested);

  struct Transport {
    RefPtr<TransportFlow> mFlow;
    RefPtr<TransportFlow> mRtcpFlow;
  };

  using MediaTransportHandler::OnAlpnNegotiated;
  using MediaTransportHandler::OnCandidate;
  using MediaTransportHandler::OnCandidateError;
  using MediaTransportHandler::OnConnectionStateChange;
  using MediaTransportHandler::OnEncryptedSending;
  using MediaTransportHandler::OnGatheringStateChange;
  using MediaTransportHandler::OnPacketReceived;
  using MediaTransportHandler::OnRtcpStateChange;
  using MediaTransportHandler::OnStateChange;

  void OnGatheringStateChange(const std::string& aTransportId,
                              NrIceMediaStream::GatheringState aState);
  void OnConnectionStateChange(NrIceMediaStream* aIceStream,
                               NrIceCtx::ConnectionState aState);
  void OnCandidateFound(NrIceMediaStream* aStream,
                        const std::string& aCandidate,
                        const std::string& aUfrag, const std::string& aMDNSAddr,
                        const std::string& aActualAddr);
  void OnCandidateError(NrIceMediaStream* aStream, const std::string& aAddress,
                        uint16_t aPort, const std::string& aUrl,
                        uint16_t aErrorCode, const std::string& aErrorText);
  void OnStateChange(TransportLayer* aLayer, TransportLayer::State);
  void OnRtcpStateChange(TransportLayer* aLayer, TransportLayer::State);
  void PacketReceived(TransportLayer* aLayer, MediaPacket& aPacket);
  void EncryptedPacketSending(TransportLayer* aLayer, MediaPacket& aPacket);
  RefPtr<TransportFlow> GetTransportFlow(const std::string& aTransportId,
                                         bool aIsRtcp) const;
  void GetIceStats(const NrIceMediaStream& aStream, DOMHighResTimeStamp aNow,
                   dom::RTCStatsCollection* aStats,
                   dom::RTCTransportStats& aTransport) const;

  virtual ~MediaTransportHandlerSTS() = default;
  nsCOMPtr<nsISerialEventTarget> mStsThread;
  RefPtr<NrIceCtx> mIceCtx;
  RefPtr<NrIceResolver> mDNSResolver;
  std::map<std::string, Transport> mTransports;
  bool mObfuscateHostAddresses = false;
  bool mTurnDisabled = false;
  uint32_t mMinDtlsVersion = 0;
  uint32_t mMaxDtlsVersion = 0;
  bool mForceNoHost = false;
  bool mAllowLoopback = false;
  bool mAllowLinkLocal = false;
  Maybe<NrIceCtx::NatSimulatorConfig> mNatConfig;

  std::set<std::string> mSignaledAddresses;

  // Init can only be done on main, but we want this to be usable on any thread
  using InitPromise = MozPromise<bool, std::string, false>;
  RefPtr<InitPromise> mInitPromise;
};

/* static */
already_AddRefed<MediaTransportHandler> MediaTransportHandler::Create() {
  RefPtr<MediaTransportHandler> result;
  if (XRE_IsContentProcess() &&
      Preferences::GetBool("media.peerconnection.mtransport_process") &&
      StaticPrefs::network_process_enabled()) {
    result = MakeRefPtr<MediaTransportHandlerIPC>();
  } else {
    result = MakeRefPtr<MediaTransportHandlerSTS>();
  }
  result->Initialize();
  return result.forget();
}

class STSShutdownHandler : public nsISTSShutdownObserver {
 public:
  NS_DECL_ISUPPORTS

  // Lazy singleton
  static RefPtr<STSShutdownHandler>& Instance() {
    MOZ_ASSERT(NS_IsMainThread());
    static RefPtr<STSShutdownHandler> sHandler =
        MakeRefPtr<STSShutdownHandler>();
    return sHandler;
  }

  void Shutdown() {
    MOZ_ASSERT(NS_IsMainThread());
    for (const auto& handler : mHandlers) {
      handler->Shutdown();
    }
    mHandlers.clear();
  }

  STSShutdownHandler() {
    CSFLogDebug(LOGTAG, "%s", __func__);
    nsresult res;
    nsCOMPtr<nsISocketTransportService> sts =
        do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res);
    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(res));
    MOZ_RELEASE_ASSERT(sts);
    sts->AddShutdownObserver(this);
  }

  NS_IMETHOD Observe() override {
    CSFLogDebug(LOGTAG, "%s", __func__);
    Shutdown();
    nsresult res;
    nsCOMPtr<nsISocketTransportService> sts =
        do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res);
    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(res));
    MOZ_RELEASE_ASSERT(sts);
    sts->RemoveShutdownObserver(this);
    Instance() = nullptr;
    return NS_OK;
  }

  void Register(MediaTransportHandlerSTS* aHandler) {
    MOZ_ASSERT(NS_IsMainThread());
    mHandlers.insert(aHandler);
  }

  void Deregister(MediaTransportHandlerSTS* aHandler) {
    MOZ_ASSERT(NS_IsMainThread());
    mHandlers.erase(aHandler);
  }

 private:
  virtual ~STSShutdownHandler() = default;

  // Raw ptrs, registered on init, deregistered on destruction, all on main
  std::set<MediaTransportHandlerSTS*> mHandlers;
};

NS_IMPL_ISUPPORTS(STSShutdownHandler, nsISTSShutdownObserver);

MediaTransportHandlerSTS::MediaTransportHandlerSTS() {
  nsresult rv;
  mStsThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
  if (!mStsThread) {
    MOZ_CRASH();
  }

  RLogConnector::CreateInstance();

  CSFLogDebug(LOGTAG, "%s done %p", __func__, this);

  // We do not set up mDNSService here, because we are not running on main (we
  // use PBackground), and the DNS service asserts.
}

static NrIceCtx::Policy toNrIcePolicy(dom::RTCIceTransportPolicy aPolicy) {
  switch (aPolicy) {
    case dom::RTCIceTransportPolicy::Relay:
      return NrIceCtx::ICE_POLICY_RELAY;
    case dom::RTCIceTransportPolicy::All:
      return NrIceCtx::ICE_POLICY_ALL;
    default:
      MOZ_CRASH();
  }
  return NrIceCtx::ICE_POLICY_ALL;
}

static NrIceCtx::GlobalConfig GetGlobalConfig() {
  NrIceCtx::GlobalConfig config;
  config.mTcpEnabled =
      Preferences::GetBool("media.peerconnection.ice.tcp"false);
  config.mStunClientMaxTransmits = Preferences::GetInt(
      "media.peerconnection.ice.stun_client_maximum_transmits",
      config.mStunClientMaxTransmits);
  config.mTrickleIceGracePeriod =
      Preferences::GetInt("media.peerconnection.ice.trickle_grace_period",
                          config.mTrickleIceGracePeriod);
  config.mIceTcpSoSockCount = Preferences::GetInt(
      "media.peerconnection.ice.tcp_so_sock_count", config.mIceTcpSoSockCount);
  config.mIceTcpListenBacklog =
      Preferences::GetInt("media.peerconnection.ice.tcp_listen_backlog",
                          config.mIceTcpListenBacklog);
  (void)Preferences::GetCString("media.peerconnection.ice.force_interface",
                                config.mForceNetInterface);
  return config;
}

static Maybe<NrIceCtx::NatSimulatorConfig> GetNatConfig() {
  bool block_tcp = Preferences::GetBool(
      "media.peerconnection.nat_simulator.block_tcp"false);
  bool block_udp = Preferences::GetBool(
      "media.peerconnection.nat_simulator.block_udp"false);
  bool block_tls = Preferences::GetBool(
      "media.peerconnection.nat_simulator.block_tls"false);
  int error_code_for_drop = Preferences::GetInt(
      "media.peerconnection.nat_simulator.error_code_for_drop"0);
  nsAutoCString mapping_type;
  (void)Preferences::GetCString(
      "media.peerconnection.nat_simulator.mapping_type", mapping_type);
  nsAutoCString filtering_type;
  (void)Preferences::GetCString(
      "media.peerconnection.nat_simulator.filtering_type", filtering_type);
  nsAutoCString redirect_address;
  (void)Preferences::GetCString(
      "media.peerconnection.nat_simulator.redirect_address", redirect_address);
  nsAutoCString redirect_targets;
  (void)Preferences::GetCString(
      "media.peerconnection.nat_simulator.redirect_targets", redirect_targets);
  int network_delay_ms = Preferences::GetInt(
      "media.peerconnection.nat_simulator.network_delay_ms"0);

  if (block_udp || block_tcp || block_tls || !mapping_type.IsEmpty() ||
      !filtering_type.IsEmpty() || !redirect_address.IsEmpty()) {
    CSFLogDebug(LOGTAG, "NAT filtering type: %s", filtering_type.get());
    CSFLogDebug(LOGTAG, "NAT mapping type: %s", mapping_type.get());
    CSFLogDebug(LOGTAG, "NAT network delay: %d", network_delay_ms);
    NrIceCtx::NatSimulatorConfig natConfig;
    natConfig.mBlockUdp = block_udp;
    natConfig.mBlockTcp = block_tcp;
    natConfig.mBlockTls = block_tls;
    natConfig.mErrorCodeForDrop = error_code_for_drop;
    natConfig.mFilteringType = std::move(filtering_type);
    natConfig.mMappingType = std::move(mapping_type);
    natConfig.mNetworkDelayMs = network_delay_ms;
    if (redirect_address.Length()) {
      CSFLogDebug(LOGTAG, "Redirect address: %s", redirect_address.get());
      CSFLogDebug(LOGTAG, "Redirect targets: %s", redirect_targets.get());
      natConfig.mRedirectAddress = std::move(redirect_address);
      std::stringstream str(redirect_targets.get());
      std::string target;
      while (getline(str, target, ',')) {
        CSFLogDebug(LOGTAG, "Adding target: %s", target.c_str());
        natConfig.mRedirectTargets.AppendElement(std::move(target));
      }
    }
    return Some(std::move(natConfig));
  }
  return Nothing();
}

void MediaTransportHandlerSTS::CreateIceCtx(const std::string& aName) {
  mInitPromise = InvokeAsync(
      GetMainThreadSerialEventTarget(), __func__,
      [=, this, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
        CSFLogDebug(LOGTAG, "%s starting", __func__);
        if (!NSS_IsInitialized()) {
          if (NSS_NoDB_Init(nullptr) != SECSuccess) {
            MOZ_CRASH();
            return InitPromise::CreateAndReject("NSS_NoDB_Init failed",
                                                __func__);
          }

          if (NS_FAILED(mozilla::psm::InitializeCipherSuite())) {
            MOZ_CRASH();
            return InitPromise::CreateAndReject("InitializeCipherSuite failed",
                                                __func__);
          }

          mozilla::psm::DisableMD5();
        }

        static bool globalInitDone = false;
        if (!globalInitDone) {
          // Ensure the DNS service is initted for the first time on main
          DebugOnly<RefPtr<nsIDNSService>> dnsService =
              RefPtr<nsIDNSService>(nsDNSService::GetXPCOMSingleton());
          MOZ_ASSERT(dnsService.value);
          mStsThread->Dispatch(
              WrapRunnableNM(&NrIceCtx::InitializeGlobals, GetGlobalConfig()),
              NS_DISPATCH_NORMAL);
          globalInitDone = true;
        }

        // Give us a way to globally turn off TURN support
        mTurnDisabled =
            Preferences::GetBool("media.peerconnection.turn.disable"false);
        // We are reading these here, because when we setup the DTLS transport
        // we are on the wrong thread to read prefs
        mMinDtlsVersion =
            Preferences::GetUint("media.peerconnection.dtls.version.min");
        mMaxDtlsVersion =
            Preferences::GetUint("media.peerconnection.dtls.version.max");
        mForceNoHost =
            Preferences::GetBool("media.peerconnection.ice.no_host"false);
        mNatConfig = GetNatConfig();
        mAllowLoopback =
            Preferences::GetBool("media.peerconnection.ice.loopback"false);
        mAllowLinkLocal =
            Preferences::GetBool("media.peerconnection.ice.link_local"false);

        MOZ_RELEASE_ASSERT(STSShutdownHandler::Instance());
        STSShutdownHandler::Instance()->Register(this);

        return InvokeAsync(
            mStsThread, __func__,
            [=, this, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
              mIceCtx = NrIceCtx::Create(aName);
              if (!mIceCtx) {
                return InitPromise::CreateAndReject("NrIceCtx::Create failed",
                                                    __func__);
              }

              mIceCtx->SignalConnectionStateChange.connect(
                  this, &MediaTransportHandlerSTS::OnConnectionStateChange);

              mDNSResolver = MakeRefPtr<NrIceResolver>();
              nsresult rv;
              if (NS_FAILED(rv = mDNSResolver->Init())) {
                CSFLogError(LOGTAG, "%s: Failed to initialize dns resolver",
                            __FUNCTION__);
                return InitPromise::CreateAndReject(
                    "Failed to initialize dns resolver", __func__);
              }
              if (NS_FAILED(rv = mIceCtx->SetResolver(
                                mDNSResolver->AllocateResolver()))) {
                CSFLogError(LOGTAG, "%s: Failed to get dns resolver",
                            __FUNCTION__);
                return InitPromise::CreateAndReject(
                    "Failed to get dns resolver", __func__);
              }

              CSFLogDebug(LOGTAG, "%s done", __func__);
              return InitPromise::CreateAndResolve(true, __func__);
            });
      });
}

using ParsedIceServer = IceServerParser::ParsedIceServer;

nsresult MediaTransportHandlerSTS::SetIceConfig(
    const nsTArray<dom::RTCIceServer>& aIceServers,
    dom::RTCIceTransportPolicy aIcePolicy) {
  auto result = IceServerParser::Parse(aIceServers);
  if (result.isErr()) {
    // Discard the detailed ErrorResult; callers at this level use nsresult.
    result.unwrapErr().SuppressException();
    return NS_ERROR_FAILURE;
  }

  nsTArray<ParsedIceServer> entries = result.unwrap();

  MOZ_RELEASE_ASSERT(mInitPromise);

  mInitPromise->Then(
      mStsThread, __func__,
      [this, aIcePolicy, entries = std::move(entries),
       self = RefPtr<MediaTransportHandlerSTS>(this)]() {
        if (!mIceCtx) {
          CSFLogError(LOGTAG, "%s: mIceCtx is null", __FUNCTION__);
          return;
        }
        NrIceCtx::Config config;
        config.mPolicy = toNrIcePolicy(aIcePolicy);
        if (config.mPolicy == NrIceCtx::ICE_POLICY_ALL && mForceNoHost) {
          config.mPolicy = NrIceCtx::ICE_POLICY_NO_HOST;
        }

        config.mAllowLoopback = mAllowLoopback;
        config.mAllowLinkLocal = mAllowLinkLocal;
        config.mNatSimulatorConfig = mNatConfig;

        nsresult rv;

        if (NS_FAILED(rv = mIceCtx->SetIceServers(entries, mTurnDisabled))) {
          CSFLogError(LOGTAG, "%s: Failed to set ICE servers", __FUNCTION__);
          return;
        }
        if (NS_FAILED(rv = mIceCtx->SetIceConfig(config))) {
          CSFLogError(LOGTAG, "%s: Failed to set config", __FUNCTION__);
        }
      });

  return NS_OK;
}

void MediaTransportHandlerSTS::Shutdown() {
  CSFLogDebug(LOGTAG, "%s", __func__);
  MOZ_ASSERT(NS_IsMainThread());
  mStsThread->Dispatch(NewNonOwningRunnableMethod(
      __func__, this, &MediaTransportHandlerSTS::Shutdown_s));
}

void MediaTransportHandlerSTS::Shutdown_s() {
  CSFLogDebug(LOGTAG, "%s", __func__);
  // Clear the transports before destroying the ice ctx so that
  // the close_notify alerts have a chance to be sent as the
  // TransportFlow destructors execute.
  mTransports.clear();
  if (mIceCtx) {
    NrIceStats stats = mIceCtx->Destroy();
    CSFLogDebug(LOGTAG,
                "Ice Telemetry: stun (retransmits: %d)"
                "   turn (401s: %d   403s: %d   438s: %d)",
                stats.stun_retransmits, stats.turn_401s, stats.turn_403s,
                stats.turn_438s);
  }
  mIceCtx = nullptr;
  mDNSResolver = nullptr;
}

void MediaTransportHandlerSTS::Destroy() {
  CSFLogDebug(LOGTAG, "%s %p", __func__, this);
  // Our "destruction tour" starts on main, because we need to deregister.
  if (!NS_IsMainThread()) {
    GetMainThreadSerialEventTarget()->Dispatch(
        NewNonOwningRunnableMethod("MediaTransportHandlerSTS::Destroy", this,
                                   &MediaTransportHandlerSTS::Destroy));
    return;
  }

  MOZ_ASSERT(NS_IsMainThread());
  if (STSShutdownHandler::Instance()) {
    STSShutdownHandler::Instance()->Deregister(this);
    Shutdown();
  }

  // mIceCtx still has a reference to us via sigslot! We must dispach to STS,
  // and clean up there.
  nsresult rv = mStsThread->Dispatch(
      NewNonOwningRunnableMethod("MediaTransportHandlerSTS::DestroyFinal", this,
                                 &MediaTransportHandlerSTS::DestroyFinal));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    CSFLogError(LOGTAG,
                "Unable to dispatch to STS: why has the XPCOM shutdown handler "
                "not been invoked?");
    delete this;
  }
}

void MediaTransportHandlerSTS::DestroyFinal() { delete this; }

void MediaTransportHandlerSTS::SetProxyConfig(
    NrSocketProxyConfig&& aProxyConfig) {
  MOZ_RELEASE_ASSERT(mInitPromise);

  mInitPromise->Then(
      mStsThread, __func__,
      [this, self = RefPtr<MediaTransportHandlerSTS>(this),
       aProxyConfig = std::move(aProxyConfig)]() mutable {
        if (!mIceCtx) {
          return;  // Probably due to XPCOM shutdown
        }

        mIceCtx->SetProxyConfig(std::move(aProxyConfig));
      },
      [](const std::string& aError) {});
}

void MediaTransportHandlerSTS::EnsureProvisionalTransport(
    const std::string& aTransportId, const std::string& aUfrag,
    const std::string& aPwd, int aComponentCount) {
  MOZ_RELEASE_ASSERT(mInitPromise);

  mInitPromise->Then(
      mStsThread, __func__,
      [=, this, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
        if (!mIceCtx) {
          return;  // Probably due to XPCOM shutdown
        }

        RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId));
        if (!stream) {
          CSFLogDebug(LOGTAG, "%s: Creating ICE media stream=%s components=%d",
                      mIceCtx->name().c_str(), aTransportId.c_str(),
                      aComponentCount);

          std::ostringstream os;
          os << mIceCtx->name() << " transport-id=" << aTransportId;
          stream =
              mIceCtx->CreateStream(aTransportId, os.str(), aComponentCount);

          if (!stream) {
            CSFLogError(LOGTAG, "Failed to create ICE stream.");
            return;
          }

          stream->SignalCandidate.connect(
              this, &MediaTransportHandlerSTS::OnCandidateFound);
          stream->SignalCandidateError.connect(
              this, &MediaTransportHandlerSTS::OnCandidateError);
          stream->SignalGatheringStateChange.connect(
              this, &MediaTransportHandlerSTS::OnGatheringStateChange);
        }

        // Begins an ICE restart if this stream has a different ufrag/pwd
        stream->SetIceCredentials(aUfrag, aPwd);

        // Make sure there's an entry in mTransports
        mTransports[aTransportId];
      },
      [](const std::string& aError) {});
}

void MediaTransportHandlerSTS::ActivateTransport(
    const std::string& aTransportId, const std::string& aLocalUfrag,
    const std::string& aLocalPwd, size_t aComponentCount,
    const std::string& aUfrag, const std::string& aPassword,
    const nsTArray<uint8_t>& aKeyDer, const nsTArray<uint8_t>& aCertDer,
    SSLKEAType aAuthType, bool aDtlsClient, const DtlsDigestList& aDigests,
    bool aPrivacyRequested) {
  MOZ_RELEASE_ASSERT(mInitPromise);

  mInitPromise->Then(
      mStsThread, __func__,
      [=, this, keyDer = aKeyDer.Clone(), certDer = aCertDer.Clone(),
       self = RefPtr<MediaTransportHandlerSTS>(this)]() {
        if (!mIceCtx) {
          return;  // Probably due to XPCOM shutdown
        }

        MOZ_ASSERT(aComponentCount);
        RefPtr<DtlsIdentity> dtlsIdentity(
            DtlsIdentity::Deserialize(keyDer, certDer, aAuthType));
        if (!dtlsIdentity) {
          MOZ_ASSERT(false);
          return;
        }

        RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId));
        if (!stream) {
          MOZ_ASSERT(false);
          return;
        }

        CSFLogDebug(LOGTAG, "%s: Activating ICE media stream=%s components=%u",
                    mIceCtx->name().c_str(), aTransportId.c_str(),
                    static_cast<unsigned>(aComponentCount));

        std::vector<std::string> attrs;
        attrs.reserve(2 /* ufrag + pwd */);
        attrs.push_back("ice-ufrag:" + aUfrag);
        attrs.push_back("ice-pwd:" + aPassword);

        // If we started an ICE restart in EnsureProvisionalTransport, this is
        // where we decide whether to commit or rollback.
        nsresult rv = stream->ConnectToPeer(aLocalUfrag, aLocalPwd, attrs);
        if (NS_FAILED(rv)) {
          CSFLogError(LOGTAG, "Couldn't parse ICE attributes, rv=%u",
                      static_cast<unsigned>(rv));
          MOZ_ASSERT(false);
          return;
        }

        Transport transport = mTransports[aTransportId];
        if (!transport.mFlow) {
          transport.mFlow =
              CreateTransportFlow(aTransportId, false, dtlsIdentity,
                                  aDtlsClient, aDigests, aPrivacyRequested);
          if (!transport.mFlow) {
            return;
          }
          TransportLayer* dtls =
              transport.mFlow->GetLayer(TransportLayerDtls::ID());
          dtls->SignalStateChange.connect(
              this, &MediaTransportHandlerSTS::OnStateChange);
          if (aComponentCount < 2) {
            dtls->SignalStateChange.connect(
                this, &MediaTransportHandlerSTS::OnRtcpStateChange);
          }
        }

        if (aComponentCount == 2) {
          if (!transport.mRtcpFlow) {
            transport.mRtcpFlow =
                CreateTransportFlow(aTransportId, true, dtlsIdentity,
                                    aDtlsClient, aDigests, aPrivacyRequested);
            if (!transport.mRtcpFlow) {
              return;
            }
            TransportLayer* dtls =
                transport.mRtcpFlow->GetLayer(TransportLayerDtls::ID());
            dtls->SignalStateChange.connect(
                this, &MediaTransportHandlerSTS::OnRtcpStateChange);
          }
        } else {
          transport.mRtcpFlow = nullptr;
          // components are 1-indexed
          stream->DisableComponent(2);
        }

        mTransports[aTransportId] = transport;
      },
      [](const std::string& aError) {});
}

void MediaTransportHandlerSTS::SetTargetForDefaultLocalAddressLookup(
    const std::string& aTargetIp, uint16_t aTargetPort) {
  MOZ_RELEASE_ASSERT(mInitPromise);

  mInitPromise->Then(
      mStsThread, __func__,
      [=, this, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
        if (!mIceCtx) {
          return;  // Probably due to XPCOM shutdown
        }

        mIceCtx->SetTargetForDefaultLocalAddressLookup(aTargetIp, aTargetPort);
      },
      [](const std::string& aError) {});
}

void MediaTransportHandlerSTS::StartIceGathering(
    bool aDefaultRouteOnly, bool aObfuscateHostAddresses,
    const nsTArray<NrIceStunAddr>& aStunAddrs) {
  MOZ_RELEASE_ASSERT(mInitPromise);

  mInitPromise->Then(
      mStsThread, __func__,
      [=, this, stunAddrs = aStunAddrs.Clone(),
       self = RefPtr<MediaTransportHandlerSTS>(this)]() {
        if (!mIceCtx) {
          return;  // Probably due to XPCOM shutdown
        }

        mObfuscateHostAddresses = aObfuscateHostAddresses;

        // Belt and suspenders - in e10s mode, the call below to SetStunAddrs
        // needs to have the proper flags set on ice ctx.  For non-e10s,
        // setting those flags happens in StartGathering.  We could probably
        // just set them here, and only do it here.
        mIceCtx->SetCtxFlags(aDefaultRouteOnly);

        if (stunAddrs.Length()) {
          mIceCtx->SetStunAddrs(stunAddrs);
        }

        // Start gathering, but only if there are streams
        if (!mIceCtx->GetStreams().empty()) {
          mIceCtx->StartGathering(aDefaultRouteOnly, aObfuscateHostAddresses);
        }
      },
      [](const std::string& aError) {});
}

void MediaTransportHandlerSTS::StartIceChecks(
    bool aIsControlling, const std::vector<std::string>& aIceOptions) {
  MOZ_RELEASE_ASSERT(mInitPromise);

  mInitPromise->Then(
      mStsThread, __func__,
      [=, this, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
        if (!mIceCtx) {
          return;  // Probably due to XPCOM shutdown
        }

        nsresult rv = mIceCtx->ParseGlobalAttributes(aIceOptions);
        if (NS_FAILED(rv)) {
          CSFLogError(LOGTAG, "%s: couldn't parse global parameters",
                      __FUNCTION__);
          return;
        }

        rv = mIceCtx->SetControlling(aIsControlling ? NrIceCtx::ICE_CONTROLLING
                                                    : NrIceCtx::ICE_CONTROLLED);
        if (NS_FAILED(rv)) {
          CSFLogError(LOGTAG, "%s: couldn't set controlling to %d",
                      __FUNCTION__, aIsControlling);
          return;
        }

        rv = mIceCtx->StartChecks();
        if (NS_FAILED(rv)) {
          CSFLogError(LOGTAG, "%s: couldn't start checks", __FUNCTION__);
          return;
        }
      },
      [](const std::string& aError) {});
}

void TokenizeCandidate(const std::string& aCandidate,
                       std::vector<std::string>& aTokens) {
  aTokens.clear();

  std::istringstream iss(aCandidate);
  std::string token;
  while (std::getline(iss, token, ' ')) {
    aTokens.push_back(token);
  }
}

void MediaTransportHandlerSTS::AddIceCandidate(
    const std::string& aTransportId, const std::string& aCandidate,
    const std::string& aUfrag, const std::string& aObfuscatedAddress) {
  MOZ_RELEASE_ASSERT(mInitPromise);

  mInitPromise->Then(
      mStsThread, __func__,
      [=, this, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
        if (!mIceCtx) {
          return;  // Probably due to XPCOM shutdown
        }

        std::vector<std::string> tokens;
        TokenizeCandidate(aCandidate, tokens);

        RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId));
        if (!stream) {
          CSFLogError(LOGTAG,
                      "No ICE stream for candidate with transport id %s: %s",
                      aTransportId.c_str(), aCandidate.c_str());
          return;
        }

        nsresult rv = stream->ParseTrickleCandidate(aCandidate, aUfrag,
                                                    aObfuscatedAddress);
        if (NS_SUCCEEDED(rv)) {
          // If the address is not obfuscated, we want to track it as
          // explicitly signaled so that we know it is fine to reveal
          // the address later on.
          if (mObfuscateHostAddresses && tokens.size() > 4 &&
              aObfuscatedAddress.empty()) {
            mSignaledAddresses.insert(tokens[4]);
          }
        } else {
          CSFLogError(LOGTAG,
                      "Couldn't process ICE candidate with transport id %s: "
                      "%s",
                      aTransportId.c_str(), aCandidate.c_str());
        }
      },
      [](const std::string& aError) {});
}

void MediaTransportHandlerSTS::UpdateNetworkState(bool aOnline) {
  MOZ_RELEASE_ASSERT(mInitPromise);

  mInitPromise->Then(
      mStsThread, __func__,
      [=, this, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
        if (!mIceCtx) {
          return;  // Probably due to XPCOM shutdown
        }

        mIceCtx->UpdateNetworkState(aOnline);
      },
      [](const std::string& aError) {});
}

void MediaTransportHandlerSTS::RemoveTransportsExcept(
    const std::set<std::string>& aTransportIds) {
  MOZ_RELEASE_ASSERT(mInitPromise);

  mInitPromise->Then(
      mStsThread, __func__,
      [=, this, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
        if (!mIceCtx) {
          return;  // Probably due to XPCOM shutdown
        }

        for (auto it = mTransports.begin(); it != mTransports.end();) {
          const std::string transportId(it->first);
          if (!aTransportIds.count(transportId)) {
            OnStateChange(transportId, TransportLayer::TS_CLOSED, {});
            OnRtcpStateChange(transportId, TransportLayer::TS_CLOSED);
            // Erase the transport before destroying the ice stream so that
            // the close_notify alerts have a chance to be sent as the
            // TransportFlow destructors execute.
            it = mTransports.erase(it);
            // We're already on the STS thread, but the TransportFlow
            // destructor executed when mTransports.erase(it) is called
            // above dispatches the call to DestroyFinal to the STS thread. If
            // we don't also dispatch the call to destroy the NrIceMediaStream
            // to the STS thread, it will tear down the NrIceMediaStream
            // before the TransportFlow is destroyed.  Without a valid
            // NrIceMediaStream the close_notify alert cannot be sent.
            mStsThread->Dispatch(NS_NewRunnableFunction(
                __func__, [iceCtx = RefPtr<NrIceCtx>(mIceCtx), transportId] {
                  iceCtx->DestroyStream(transportId);
                }));
          } else {
            MOZ_ASSERT(it->second.mFlow);
            ++it;
          }
        }
      },
      [](const std::string& aError) {});
}

void MediaTransportHandlerSTS::SendPacket(const std::string& aTransportId,
                                          MediaPacket&& aPacket) {
  MOZ_RELEASE_ASSERT(mInitPromise);

  mInitPromise->Then(
      mStsThread, __func__,
      [this, self = RefPtr<MediaTransportHandlerSTS>(this), aTransportId,
       aPacket = std::move(aPacket)]() mutable {
        if (!mIceCtx) {
          return;  // Probably due to XPCOM shutdown
        }

        MOZ_ASSERT(aPacket.type() != MediaPacket::UNCLASSIFIED);
        RefPtr<TransportFlow> flow =
            GetTransportFlow(aTransportId, aPacket.type() == MediaPacket::RTCP);

        if (!flow) {
          CSFLogError(LOGTAG,
                      "%s: No such transport flow (%s) for outgoing packet",
                      mIceCtx->name().c_str(), aTransportId.c_str());
          return;
        }

        TransportLayer* layer = nullptr;
        switch (aPacket.type()) {
          case MediaPacket::SCTP:
            layer = flow->GetLayer(TransportLayerDtls::ID());
            break;
          case MediaPacket::RTP:
          case MediaPacket::RTCP:
            layer = flow->GetLayer(TransportLayerSrtp::ID());
            break;
          default:
            // Maybe it would be useful to allow the injection of other packet
            // types for testing?
            MOZ_ASSERT(false);
            return;
        }

        MOZ_ASSERT(layer);

        if (int error = layer->SendPacket(aPacket); error < 0) {
          CSFLogError(LOGTAG,
                      "%s: Transport flow (%s) failed to send packet. error=%d",
                      mIceCtx->name().c_str(), aTransportId.c_str(), error);
        }
      },
      [](const std::string& aError) {});
}

TransportLayer::State MediaTransportHandler::GetState(
    const std::string& aTransportId, bool aRtcp) const {
  MutexAutoLock lock(mStateCacheMutex);
  const std::map<std::string, TransportLayer::State>* cache = nullptr;
  if (aRtcp) {
    cache = &mRtcpStateCache;
  } else {
    cache = &mStateCache;
  }

  auto it = cache->find(aTransportId);
  if (it != cache->end()) {
    return it->second;
  }
  return TransportLayer::TS_NONE;
}

void MediaTransportHandler::OnCandidate(const std::string& aTransportId,
                                        CandidateInfo&& aCandidateInfo) {
  mCandidateGathered.Notify(aTransportId, std::move(aCandidateInfo));
}

void MediaTransportHandler::OnCandidateError(
    IceCandidateErrorInfo&& aErrorInfo) {
  mCandidateError.Notify(std::move(aErrorInfo));
}

void MediaTransportHandler::OnAlpnNegotiated(const std::string& aAlpn) {
  const bool privacyRequested = aAlpn == "c-webrtc";
  mAlpnNegotiated.Notify(aAlpn, privacyRequested);
}

void MediaTransportHandler::OnGatheringStateChange(
    const std::string& aTransportId, dom::RTCIceGathererState aState) {
  mGatheringStateChange.Notify(aTransportId, aState);
}

void MediaTransportHandler::OnConnectionStateChange(
    const std::string& aTransportId, dom::RTCIceTransportState aState) {
  mConnectionStateChange.Notify(aTransportId, aState);
}

void MediaTransportHandler::OnPacketReceived(std::string&& aTransportId,
                                             MediaPacket&& aPacket) {
  switch (aPacket.type()) {
    case MediaPacket::UNCLASSIFIED:
    case MediaPacket::DTLS:
    case MediaPacket::SRTP:
    case MediaPacket::SRTCP:
      // Shouldn't happen, and nothing would care if it did
      break;
    case MediaPacket::RTP:
    case MediaPacket::RTCP:
      mRtpPacketReceived.Notify(std::forward<std::string>(aTransportId),
                                std::forward<MediaPacket>(aPacket));
      break;
    case MediaPacket::SCTP:
      mSctpPacketReceived.Notify(std::forward<std::string>(aTransportId),
                                 std::forward<MediaPacket>(aPacket));
      break;
  }
}

void MediaTransportHandler::OnEncryptedSending(const std::string& aTransportId,
                                               MediaPacket&& aPacket) {
  mEncryptedSending.Notify(aTransportId, std::move(aPacket));
}

void MediaTransportHandler::OnStateChange(
    const std::string& aTransportId, TransportLayer::State aState,
    nsTArray<nsTArray<uint8_t>>&& aRemoteCerts) {
  {
    MutexAutoLock lock(mStateCacheMutex);
    mStateCache[aTransportId] = aState;
  }
  mStateChange.Notify(aTransportId, aState, std::move(aRemoteCerts));
}

void MediaTransportHandler::OnRtcpStateChange(const std::string& aTransportId,
                                              TransportLayer::State aState) {
  {
    MutexAutoLock lock(mStateCacheMutex);
    mRtcpStateCache[aTransportId] = aState;
  }
  mRtcpStateChange.Notify(aTransportId, aState);
}

static uint16_t ToDtlsWireVersion(uint16_t aProtocolVersion) {
  switch (aProtocolVersion) {
    case SSL_LIBRARY_VERSION_DTLS_1_0:
      return SSL_LIBRARY_VERSION_DTLS_1_0_WIRE;
    case SSL_LIBRARY_VERSION_DTLS_1_2:
      return SSL_LIBRARY_VERSION_DTLS_1_2_WIRE;
    case SSL_LIBRARY_VERSION_DTLS_1_3:
      return SSL_LIBRARY_VERSION_DTLS_1_3_WIRE;
    default:
      return 0;
  }
}

RefPtr<dom::RTCStatsPromise> MediaTransportHandlerSTS::GetIceStats(
    const std::string& aTransportId, DOMHighResTimeStamp aNow) {
  MOZ_RELEASE_ASSERT(mInitPromise);

  return mInitPromise->Then(
      mStsThread, __func__, [=, this, self = RefPtr(this)]() {
        auto stats = MakeUnique<dom::RTCStatsCollection>();
        if (mIceCtx) {
          dom::RTCIceRole iceRole =
              mIceCtx->GetControlling() == NrIceCtx::ICE_CONTROLLING
                  ? dom::RTCIceRole::Controlling
                  : dom::RTCIceRole::Controlled;
          for (const auto& stream : mIceCtx->GetStreams()) {
            if (aTransportId.empty() || aTransportId == stream->GetId()) {
              dom::RTCTransportStats transport;
              transport.mId.Construct(
                  NS_ConvertASCIItoUTF16(stream->GetId().c_str()));
              transport.mTimestamp.Construct(aNow);
              transport.mType.Construct(dom::RTCStatsType::Transport);
              transport.mIceRole.Construct(iceRole);
              std::string ufrag = stream->GetUfrag();
              if (!ufrag.empty()) {
                transport.mIceLocalUsernameFragment.Construct(
                    NS_ConvertASCIItoUTF16(ufrag.c_str()));
              }
              switch (stream->state()) {
                case NrIceMediaStream::ICE_CONNECTING:
                  transport.mIceState.Construct(
                      dom::RTCIceTransportState::Checking);
                  break;
                case NrIceMediaStream::ICE_OPEN:
                  transport.mIceState.Construct(
                      dom::RTCIceTransportState::Connected);
                  break;
                case NrIceMediaStream::ICE_CLOSED:
                  transport.mIceState.Construct(
                      dom::RTCIceTransportState::Closed);
                  break;
              }
              // XXX(Bug 1225723) Determine if dtlsState should be `required`.
              transport.mDtlsState = dom::RTCDtlsTransportState::New;
              auto transportIt = mTransports.find(stream->GetId());
              if (transportIt != mTransports.end() &&
                  transportIt->second.mFlow) {
                if (auto* dtlsLayer = static_cast<TransportLayerDtls*>(
                        transportIt->second.mFlow->GetLayer(
                            TransportLayerDtls::ID()))) {
                  transport.mDtlsRole.Construct(
                      dtlsLayer->role() == TransportLayerDtls::CLIENT
                          ? dom::RTCDtlsRole::Client
                          : dom::RTCDtlsRole::Server);
                  switch (dtlsLayer->state()) {
                    case TransportLayer::TS_NONE:
                    case TransportLayer::TS_INIT:
                      transport.mDtlsState = dom::RTCDtlsTransportState::New;
                      break;
                    case TransportLayer::TS_CONNECTING:
                      transport.mDtlsState =
                          dom::RTCDtlsTransportState::Connecting;
                      break;
                    case TransportLayer::TS_OPEN:
                      transport.mDtlsState =
                          dom::RTCDtlsTransportState::Connected;
                      break;
                    case TransportLayer::TS_CLOSED:
                      transport.mDtlsState = dom::RTCDtlsTransportState::Closed;
                      break;
                    case TransportLayer::TS_ERROR:
                      transport.mDtlsState = dom::RTCDtlsTransportState::Failed;
                      break;
                  }
                  uint16_t srtpCipher = 0;
                  if (NS_SUCCEEDED(dtlsLayer->GetSrtpCipher(&srtpCipher))) {
                    const char* name =
                        TransportLayerDtls::GetSrtpCipherName(srtpCipher);
                    if (name) {
                      transport.mSrtpCipher.Construct(
                          NS_ConvertASCIItoUTF16(name));
                    }
                  }
                  SSLChannelInfo channelInfo;
                  if (NS_SUCCEEDED(dtlsLayer->GetChannelInfo(&channelInfo))) {
                    if (uint16_t v =
                            ToDtlsWireVersion(channelInfo.protocolVersion)) {
                      transport.mTlsVersion.Construct(
                          nsFmtString(u"{:04X}", v));
                    }
                    SSLCipherSuiteInfo info;
                    if (SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &info,
                                               sizeof(info)) == SECSuccess &&
                        info.cipherSuiteName) {
                      transport.mDtlsCipher.Construct(
                          NS_ConvertASCIItoUTF16(info.cipherSuiteName));
                    }
                  }
                }
              }
              // XXX(Bug 2037532) Fill missing fields on the transport.
              GetIceStats(*stream, aNow, stats.get(), transport);

              // XXX(Bug 1632090) Instead of extending the array 1-by-1 (which
              // might involve multiple reallocations) and potentially crashing
              // here, SetCapacity could be called outside the loop once.
              if (!stats->mTransportStats.AppendElement(transport, fallible)) {
                mozalloc_handle_oom(0);
              }
            }
          }
        }
        return dom::RTCStatsPromise::CreateAndResolve(std::move(stats),
                                                      __func__);
      });
}

RefPtr<MediaTransportHandler::IceLogPromise>
MediaTransportHandlerSTS::GetIceLog(const nsCString& aPattern) {
  return InvokeAsync(
      mStsThread, __func__, [=, self = RefPtr<MediaTransportHandlerSTS>(this)] {
        dom::Sequence<nsString> converted;
        RLogConnector* logs = RLogConnector::GetInstance();
        std::deque<std::string> result;
        // Might not exist yet.
        if (logs) {
          logs->Filter(aPattern.get(), 0, &result);
        }
        /// XXX(Bug 1631386) Check if we should reject the promise instead of
        /// crashing in an OOM situation.
        if (!converted.SetCapacity(result.size(), fallible)) {
          mozalloc_handle_oom(sizeof(nsString) * result.size());
        }
        for (auto& line : result) {
          // Cannot fail, SetCapacity was called before.
          (void)converted.AppendElement(NS_ConvertUTF8toUTF16(line.c_str()),
                                        fallible);
        }
        return IceLogPromise::CreateAndResolve(std::move(converted), __func__);
      });
}

void MediaTransportHandlerSTS::ClearIceLog() {
  if (!mStsThread->IsOnCurrentThread()) {
    mStsThread->Dispatch(WrapRunnable(RefPtr<MediaTransportHandlerSTS>(this),
                                      &MediaTransportHandlerSTS::ClearIceLog),
                         NS_DISPATCH_NORMAL);
    return;
  }

  RLogConnector* logs = RLogConnector::GetInstance();
  if (logs) {
    logs->Clear();
  }
}

void MediaTransportHandlerSTS::EnterPrivateMode() {
  if (!mStsThread->IsOnCurrentThread()) {
    mStsThread->Dispatch(
        WrapRunnable(RefPtr<MediaTransportHandlerSTS>(this),
                     &MediaTransportHandlerSTS::EnterPrivateMode),
        NS_DISPATCH_NORMAL);
    return;
  }

  RLogConnector::GetInstance()->EnterPrivateMode();
}

void MediaTransportHandlerSTS::ExitPrivateMode() {
  if (!mStsThread->IsOnCurrentThread()) {
    mStsThread->Dispatch(
        WrapRunnable(RefPtr<MediaTransportHandlerSTS>(this),
                     &MediaTransportHandlerSTS::ExitPrivateMode),
        NS_DISPATCH_NORMAL);
    return;
  }

  auto* log = RLogConnector::GetInstance();
  MOZ_ASSERT(log);
  if (log) {
    log->ExitPrivateMode();
  }
}

static void ToRTCIceCandidateStats(
    const std::vector<NrIceCandidate>& candidates,
    dom::RTCStatsType candidateType, const nsString& transportId,
    DOMHighResTimeStamp now, dom::RTCStatsCollection* stats,
    bool obfuscateHostAddresses,
    const std::set<std::string>& signaledAddresses) {
  MOZ_ASSERT(stats);
  for (const auto& candidate : candidates) {
    dom::RTCIceCandidateStats cand;
    cand.mType.Construct(candidateType);
    NS_ConvertASCIItoUTF16 codeword(candidate.codeword.c_str());
    cand.mTransportId = transportId;
    cand.mId.Construct(codeword);
    cand.mTimestamp.Construct(now);
    cand.mCandidateType.Construct(dom::RTCIceCandidateType(candidate.type));
    cand.mPriority.Construct(candidate.priority);
    // https://tools.ietf.org/html/draft-ietf-rtcweb-mdns-ice-candidates-03#section-3.3.1
    // This obfuscates the address with the mDNS address if one exists
    if (!candidate.mdns_addr.empty()) {
      cand.mAddress.Construct(
          NS_ConvertASCIItoUTF16(candidate.mdns_addr.c_str()));
    } else if (obfuscateHostAddresses &&
               candidate.type == NrIceCandidate::ICE_PEER_REFLEXIVE &&
               signaledAddresses.find(candidate.cand_addr.host) ==
                   signaledAddresses.end()) {
      cand.mAddress.Construct(NS_ConvertASCIItoUTF16("(redacted)"));
    } else {
      cand.mAddress.Construct(
          NS_ConvertASCIItoUTF16(candidate.cand_addr.host.c_str()));
    }
    cand.mPort.Construct(candidate.cand_addr.port);
    cand.mProtocol.Construct(
        NS_ConvertASCIItoUTF16(candidate.cand_addr.transport.c_str()));
    if (candidateType == dom::RTCStatsType::Local_candidate &&
        dom::RTCIceCandidateType(candidate.type) ==
            dom::RTCIceCandidateType::Relay) {
      cand.mRelayProtocol.Construct(
          NS_ConvertASCIItoUTF16(candidate.local_addr.transport.c_str()));
    }
    cand.mUsernameFragment.Construct(
        NS_ConvertASCIItoUTF16(candidate.username_fragment.c_str()));
    // Foundation is not set for peer-reflexive candidates.
    if (candidate.type != NrIceCandidate::ICE_PEER_REFLEXIVE) {
      cand.mFoundation.Construct(
          NS_ConvertASCIItoUTF16(candidate.foundation.c_str()));
    }
    if (candidate.tcp_type == NrIceCandidate::ICE_ACTIVE) {
      cand.mTcpType.Construct(dom::RTCIceTcpCandidateType::Active);
    } else if (candidate.tcp_type == NrIceCandidate::ICE_PASSIVE) {
      cand.mTcpType.Construct(dom::RTCIceTcpCandidateType::Passive);
    }
    cand.mProxied.Construct(NS_ConvertASCIItoUTF16(
        candidate.is_proxied ? "proxied" : "non-proxied"));
    if (!stats->mIceCandidateStats.AppendElement(cand, fallible)) {
      // XXX(Bug 1632090) Instead of extending the array 1-by-1 (which might
      // involve multiple reallocations) and potentially crashing here,
      // SetCapacity could be called outside the loop once.
      mozalloc_handle_oom(0);
    }
    if (candidate.trickled) {
      if (!stats->mTrickledIceCandidateStats.AppendElement(cand, fallible)) {
        mozalloc_handle_oom(0);
      }
    }
  }
}

void MediaTransportHandlerSTS::GetIceStats(
    const NrIceMediaStream& aStream, DOMHighResTimeStamp aNow,
    dom::RTCStatsCollection* aStats, dom::RTCTransportStats& aTransport) const {
  MOZ_ASSERT(mStsThread->IsOnCurrentThread());

  NS_ConvertASCIItoUTF16 transportId(aStream.GetId().c_str());

  std::vector<NrIceCandidatePair> candPairs;
  nsresult res = aStream.GetCandidatePairs(&candPairs);
  if (NS_FAILED(res)) {
    CSFLogError(LOGTAG,
                "%s: Error getting candidate pairs for transport id \"%s\"",
                __FUNCTION__, aStream.GetId().c_str());
    return;
  }

  for (auto& candPair : candPairs) {
    NS_ConvertASCIItoUTF16 codeword(candPair.codeword.c_str());
    NS_ConvertASCIItoUTF16 localCodeword(candPair.local.codeword.c_str());
    NS_ConvertASCIItoUTF16 remoteCodeword(candPair.remote.codeword.c_str());
    // Only expose candidate-pair statistics to chrome, until we've thought
    // through the implications of exposing it to content.

    dom::RTCIceCandidatePairStats s;
    s.mId.Construct(codeword);
    s.mTransportId = transportId;
    s.mTimestamp.Construct(aNow);
    s.mType.Construct(dom::RTCStatsType::Candidate_pair);
    s.mLocalCandidateId.Construct(localCodeword);
    s.mRemoteCandidateId.Construct(remoteCodeword);
    s.mNominated.Construct(candPair.nominated);
    s.mWritable.Construct(candPair.writable);
    s.mReadable.Construct(candPair.readable);
    s.mPriority.Construct(candPair.priority);
    s.mSelected.Construct(candPair.selected);
    s.mBytesSent.Construct(candPair.bytes_sent);
    s.mBytesReceived.Construct(candPair.bytes_recvd);
    s.mLastPacketSentTimestamp.Construct(candPair.ms_since_last_send);
    s.mLastPacketReceivedTimestamp.Construct(candPair.ms_since_last_recv);
    s.mState.Construct(dom::RTCStatsIceCandidatePairState(candPair.state));
    s.mResponsesReceived.Construct(candPair.responses_recvd);
    s.mCurrentRoundTripTime.Construct(candPair.current_rtt_ms / 1000.0);
    s.mTotalRoundTripTime.Construct(candPair.total_rtt_ms / 1000.0);
    s.mComponentId.Construct(candPair.component_id);
    if (candPair.selected && candPair.component_id == 1) {
      aTransport.mSelectedCandidatePairId.Construct(codeword);
    }
    if (!aStats->mIceCandidatePairStats.AppendElement(s, fallible)) {
      // XXX(Bug 1632090) Instead of extending the array 1-by-1 (which might
      // involve multiple reallocations) and potentially crashing here,
      // SetCapacity could be called outside the loop once.
      mozalloc_handle_oom(0);
    }
  }

  std::vector<NrIceCandidate> candidates;
  if (NS_SUCCEEDED(aStream.GetLocalCandidates(&candidates))) {
    ToRTCIceCandidateStats(candidates, dom::RTCStatsType::Local_candidate,
                           transportId, aNow, aStats, mObfuscateHostAddresses,
                           mSignaledAddresses);
    // add the local candidates unparsed string to a sequence
    for (const auto& candidate : candidates) {
      if (!aStats->mRawLocalCandidates.AppendElement(
              NS_ConvertASCIItoUTF16(candidate.label.c_str()), fallible)) {
        // XXX(Bug 1632090) Instead of extending the array 1-by-1 (which might
        // involve multiple reallocations) and potentially crashing here,
        // SetCapacity could be called outside the loop once.
        mozalloc_handle_oom(0);
      }
    }
  }
  candidates.clear();

  if (NS_SUCCEEDED(aStream.GetRemoteCandidates(&candidates))) {
    ToRTCIceCandidateStats(candidates, dom::RTCStatsType::Remote_candidate,
                           transportId, aNow, aStats, mObfuscateHostAddresses,
                           mSignaledAddresses);
    // add the remote candidates unparsed string to a sequence
    for (const auto& candidate : candidates) {
      if (!aStats->mRawRemoteCandidates.AppendElement(
              NS_ConvertASCIItoUTF16(candidate.label.c_str()), fallible)) {
        // XXX(Bug 1632090) Instead of extending the array 1-by-1 (which might
        // involve multiple reallocations) and potentially crashing here,
        // SetCapacity could be called outside the loop once.
        mozalloc_handle_oom(0);
      }
    }
  }
}

RefPtr<TransportFlow> MediaTransportHandlerSTS::GetTransportFlow(
    const std::string& aTransportId, bool aIsRtcp) const {
  auto it = mTransports.find(aTransportId);
  if (it == mTransports.end()) {
    return nullptr;
  }

  if (aIsRtcp) {
    return it->second.mRtcpFlow ? it->second.mRtcpFlow : it->second.mFlow;
    ;
  }

  return it->second.mFlow;
}

RefPtr<TransportFlow> MediaTransportHandlerSTS::CreateTransportFlow(
    const std::string& aTransportId, bool aIsRtcp,
    const RefPtr<DtlsIdentity>& aDtlsIdentity, bool aDtlsClient,
    const DtlsDigestList& aDigests, bool aPrivacyRequested) {
  nsresult rv;
  RefPtr flow = MakeRefPtr<TransportFlow>(aTransportId);

  // The media streams are made on STS so we need to defer setup.
  auto ice = MakeUnique<TransportLayerIce>();
  auto dtls = MakeUnique<TransportLayerDtls>();
  auto srtp = MakeUnique<TransportLayerSrtp>(*dtls);
  dtls->SetRole(aDtlsClient ? TransportLayerDtls::CLIENT
                            : TransportLayerDtls::SERVER);

  dtls->SetIdentity(aDtlsIdentity);

  dtls->SetMinMaxVersion(
      static_cast<TransportLayerDtls::Version>(mMinDtlsVersion),
      static_cast<TransportLayerDtls::Version>(mMaxDtlsVersion));

  for (const auto& digest : aDigests) {
    rv = dtls->SetVerificationDigest(digest);
    if (NS_FAILED(rv)) {
      CSFLogError(LOGTAG, "Could not set fingerprint");
      return nullptr;
    }
  }

  std::vector<uint16_t> srtpCiphers =
      TransportLayerDtls::GetDefaultSrtpCiphers();

  rv = dtls->SetSrtpCiphers(srtpCiphers);
  if (NS_FAILED(rv)) {
    CSFLogError(LOGTAG, "Couldn't set SRTP ciphers");
    return nullptr;
  }

  // Always permits negotiation of the confidential mode.
  // Only allow non-confidential (which is an allowed default),
  // if we aren't confidential.
  std::set<std::string> alpn = {"c-webrtc"};
  std::string alpnDefault;
  if (!aPrivacyRequested) {
    alpnDefault = "webrtc";
    alpn.insert(alpnDefault);
  }
  rv = dtls->SetAlpn(alpn, alpnDefault);
  if (NS_FAILED(rv)) {
    CSFLogError(LOGTAG, "Couldn't set ALPN");
    return nullptr;
  }

  ice->SetParameters(mIceCtx->GetStream(aTransportId), aIsRtcp ? 2 : 1);
  NS_ENSURE_SUCCESS(ice->Init(), nullptr);
  NS_ENSURE_SUCCESS(dtls->Init(), nullptr);
  NS_ENSURE_SUCCESS(srtp->Init(), nullptr);
  dtls->Chain(ice.get());
  srtp->Chain(ice.get());

  dtls->SignalPacketReceived.connect(this,
                                     &MediaTransportHandlerSTS::PacketReceived);
  srtp->SignalPacketReceived.connect(this,
                                     &MediaTransportHandlerSTS::PacketReceived);
  ice->SignalPacketSending.connect(
      this, &MediaTransportHandlerSTS::EncryptedPacketSending);
  flow->PushLayer(ice.release());
  flow->PushLayer(dtls.release());
  flow->PushLayer(srtp.release());
  return flow;
}

static mozilla::dom::RTCIceGathererState toDomIceGathererState(
    NrIceMediaStream::GatheringState aState) {
  switch (aState) {
    case NrIceMediaStream::ICE_STREAM_GATHER_INIT:
      return dom::RTCIceGathererState::New;
    case NrIceMediaStream::ICE_STREAM_GATHER_STARTED:
      return dom::RTCIceGathererState::Gathering;
    case NrIceMediaStream::ICE_STREAM_GATHER_COMPLETE:
      return dom::RTCIceGathererState::Complete;
  }
  MOZ_CRASH();
}

void MediaTransportHandlerSTS::OnGatheringStateChange(
    const std::string& aTransportId, NrIceMediaStream::GatheringState aState) {
  OnGatheringStateChange(aTransportId, toDomIceGathererState(aState));
}

static mozilla::dom::RTCIceTransportState toDomIceTransportState(
    NrIceCtx::ConnectionState aState) {
  switch (aState) {
    case NrIceCtx::ICE_CTX_INIT:
      return dom::RTCIceTransportState::New;
    case NrIceCtx::ICE_CTX_CHECKING:
      return dom::RTCIceTransportState::Checking;
    case NrIceCtx::ICE_CTX_CONNECTED:
      return dom::RTCIceTransportState::Connected;
    case NrIceCtx::ICE_CTX_COMPLETED:
      return dom::RTCIceTransportState::Completed;
    case NrIceCtx::ICE_CTX_FAILED:
      return dom::RTCIceTransportState::Failed;
    case NrIceCtx::ICE_CTX_DISCONNECTED:
      return dom::RTCIceTransportState::Disconnected;
    case NrIceCtx::ICE_CTX_CLOSED:
      return dom::RTCIceTransportState::Closed;
  }
  MOZ_CRASH();
}

void MediaTransportHandlerSTS::OnConnectionStateChange(
    NrIceMediaStream* aIceStream, NrIceCtx::ConnectionState aState) {
  OnConnectionStateChange(aIceStream->GetId(), toDomIceTransportState(aState));
}

// The stuff below here will eventually go into the MediaTransportChild class
void MediaTransportHandlerSTS::OnCandidateFound(
    NrIceMediaStream* aStream, const std::string& aCandidate,
    const std::string& aUfrag, const std::string& aMDNSAddr,
    const std::string& aActualAddr) {
  CandidateInfo info;
  info.mCandidate = aCandidate;
  MOZ_ASSERT(!aUfrag.empty());
  info.mUfrag = aUfrag;
  NrIceCandidate defaultRtpCandidate;
  NrIceCandidate defaultRtcpCandidate;
  nsresult rv = aStream->GetDefaultCandidate(1, &defaultRtpCandidate);
  if (NS_SUCCEEDED(rv)) {
    if (!defaultRtpCandidate.mdns_addr.empty()) {
      info.mDefaultHostRtp = "0.0.0.0";
      info.mDefaultPortRtp = 9;
    } else {
      info.mDefaultHostRtp = defaultRtpCandidate.cand_addr.host;
      info.mDefaultPortRtp = defaultRtpCandidate.cand_addr.port;
    }
  } else {
    CSFLogError(LOGTAG,
                "%s: GetDefaultCandidates failed for transport id %s, "
                "res=%u",
                __FUNCTION__, aStream->GetId().c_str(),
                static_cast<unsigned>(rv));
  }

  // Optional; component won't exist if doing rtcp-mux
  if (NS_SUCCEEDED(aStream->GetDefaultCandidate(2, &defaultRtcpCandidate))) {
    if (!defaultRtcpCandidate.mdns_addr.empty()) {
      info.mDefaultHostRtcp = defaultRtcpCandidate.mdns_addr;
    } else {
      info.mDefaultHostRtcp = defaultRtcpCandidate.cand_addr.host;
    }
    info.mDefaultPortRtcp = defaultRtcpCandidate.cand_addr.port;
  }

  info.mMDNSAddress = aMDNSAddr;
  info.mActualAddress = aActualAddr;

  OnCandidate(aStream->GetId(), std::move(info));
}

void MediaTransportHandlerSTS::OnCandidateError(NrIceMediaStream* aStream,
                                                const std::string& aAddress,
                                                uint16_t aPort,
                                                const std::string& aUrl,
                                                uint16_t aErrorCode,
                                                const std::string& aErrorText) {
  IceCandidateErrorInfo info;
  info.mAddress = aAddress;
  info.mPort = aPort;
  info.mUrl = aUrl;
  info.mErrorCode = aErrorCode;
  info.mErrorText = aErrorText;
  OnCandidateError(std::move(info));
}

void MediaTransportHandlerSTS::OnStateChange(TransportLayer* aLayer,
                                             TransportLayer::State aState) {
  nsTArray<nsTArray<uint8_t>> remoteCerts;
  if (aState == TransportLayer::TS_OPEN) {
    MOZ_ASSERT(aLayer->id() == TransportLayerDtls::ID());
    TransportLayerDtls* dtlsLayer = static_cast<TransportLayerDtls*>(aLayer);
    OnAlpnNegotiated(dtlsLayer->GetNegotiatedAlpn());
    remoteCerts = dtlsLayer->GetPeerCertChainDer();
  }

  // DTLS state indicates the readiness of the transport as a whole, because
  // SRTP uses the keys from the DTLS handshake.
  MediaTransportHandler::OnStateChange(aLayer->flow_id(), aState,
                                       std::move(remoteCerts));
}

void MediaTransportHandlerSTS::OnRtcpStateChange(TransportLayer* aLayer,
                                                 TransportLayer::State aState) {
  MediaTransportHandler::OnRtcpStateChange(aLayer->flow_id(), aState);
}

void MediaTransportHandlerSTS::PacketReceived(TransportLayer* aLayer,
                                              MediaPacket& aPacket) {
  MEDIA_TRANSPORT_HANDLER_PACKET_RECEIVED(aPacket);
  OnPacketReceived(std::string(aLayer->flow_id()), std::move(aPacket));
}

void MediaTransportHandlerSTS::EncryptedPacketSending(TransportLayer* aLayer,
                                                      MediaPacket& aPacket) {
  OnEncryptedSending(aLayer->flow_id(), std::move(aPacket));
}

}  // namespace mozilla

#undef MEDIA_TRANSPORT_HANDLER_PACKET_RECEIVED

Messung V0.5 in Prozent
C=88 H=95 G=91

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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 und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002