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

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/transportflow.h"
#include "transport/transportlayerice.h"
#include "transport/transportlayerdtls.h"
#include "transport/transportlayersrtp.h"

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

// Parsing STUN/TURN URIs
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsURLHelper.h"
#include "nsIURLParser.h"

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

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

// DTLS
#include "sdp/SdpAttribute.h"

#include "transport/runnable_utils.h"

#include "mozilla/Algorithm.h"

#include "mozilla/dom/RTCStatsReportBinding.h"

#include "nss.h"                // For NSS_NoDB_Init
#include "mozilla/PublicSSL.h"  // For psm::InitializeCipherSuite

#include "nsISocketTransportService.h"
#include "nsDNSService2.h"

#include <string>
#include <vector>
#include <map>

#ifdef MOZ_GECKO_PROFILER
#  include "mozilla/ProfilerMarkers.h"

#  define MEDIA_TRANSPORT_HANDLER_PACKET_RECEIVED(aPacket) \
    PROFILER_MARKER_TEXT(                                  \
        "WebRTC Packet Received", MEDIA_RT, {},            \
        ProfilerString8View::WrapNullTerminatedString(     \
            MediaPacket::EnumValueToString((aPacket).type())));
#else
#  define MEDIA_TRANSPORT_HANDLER_PACKET_RECEIVED(aPacket)
#endif

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(nsISerialEventTarget* aCallbackThread);

  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 Destroy_s();
  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::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 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) 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;
  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(
    nsISerialEventTarget* aCallbackThread) {
  RefPtr<MediaTransportHandler> result;
  if (XRE_IsContentProcess() &&
      Preferences::GetBool("media.peerconnection.mtransport_process") &&
      StaticPrefs::network_process_enabled()) {
    result = new MediaTransportHandlerIPC(aCallbackThread);
  } else {
    result = new MediaTransportHandlerSTS(aCallbackThread);
  }
  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(new 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(
    nsISerialEventTarget* aCallbackThread)
    : MediaTransportHandler(aCallbackThread) {
  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;
}

// list of known acceptable ports for webrtc
int16_t gGoodWebrtcPortList[] = {
    53,    // Some deplyoments use DNS port to punch through overzealous NATs
    3478,  // stun or turn
    5349,  // stuns or turns
    0,     // Sentinel value: This MUST be zero
};

static nsresult addNrIceServer(const nsString& aIceUrl,
                               const dom::RTCIceServer& aIceServer,
                               std::vector<NrIceStunServer>* aStunServersOut,
                               std::vector<NrIceTurnServer>* aTurnServersOut) {
  // Without STUN/TURN handlers, NS_NewURI returns nsSimpleURI rather than
  // nsStandardURL. To parse STUN/TURN URI's to spec
  // http://tools.ietf.org/html/draft-nandakumar-rtcweb-stun-uri-02#section-3
  // http://tools.ietf.org/html/draft-petithuguenin-behave-turn-uri-03#section-3
  // we parse out the query-string, and use ParseAuthority() on the rest
  RefPtr<nsIURI> url;
  nsresult rv = NS_NewURI(getter_AddRefs(url), aIceUrl);
  NS_ENSURE_SUCCESS(rv, rv);
  bool isStun = url->SchemeIs("stun");
  bool isStuns = url->SchemeIs("stuns");
  bool isTurn = url->SchemeIs("turn");
  bool isTurns = url->SchemeIs("turns");
  if (!(isStun || isStuns || isTurn || isTurns)) {
    return NS_ERROR_FAILURE;
  }
  if (isStuns) {
    return NS_OK;  // TODO: Support STUNS (Bug 1056934)
  }

  nsAutoCString spec;
  rv = url->GetSpec(spec);
  NS_ENSURE_SUCCESS(rv, rv);

  // TODO(jib@mozilla.com): Revisit once nsURI supports STUN/TURN (Bug 833509)
  int32_t port;
  nsAutoCString host;
  nsAutoCString transport;
  {
    uint32_t hostPos;
    int32_t hostLen;
    nsAutoCString path;
    rv = url->GetPathQueryRef(path);
    NS_ENSURE_SUCCESS(rv, rv);

    // Tolerate query-string + parse 'transport=[udp|tcp]' by hand.
    int32_t questionmark = path.FindChar('?');
    if (questionmark >= 0) {
      const nsCString match = "transport="_ns;

      for (int32_t i = questionmark, endPos; i >= 0; i = endPos) {
        endPos = path.FindCharInSet("&", i + 1);
        const nsDependentCSubstring fieldvaluepair =
            Substring(path, i + 1, endPos);
        if (StringBeginsWith(fieldvaluepair, match)) {
          transport = Substring(fieldvaluepair, match.Length());
          ToLowerCase(transport);
        }
      }
      path.SetLength(questionmark);
    }

    rv = net_GetAuthURLParser()->ParseAuthority(
        path.get(), static_cast<int>(path.Length()), nullptr, nullptr, nullptr,
        nullptr, &hostPos, &hostLen, &port);
    NS_ENSURE_SUCCESS(rv, rv);
    if (!hostLen) {
      return NS_ERROR_FAILURE;
    }
    if (hostPos > 1) {
      /* The username was removed */
      return NS_ERROR_FAILURE;
    }
    path.Mid(host, hostPos, hostLen);
    // Strip off brackets around IPv6 literals
    host.Trim("[]");
  }
  if (port == -1) port = (isStuns || isTurns) ? 5349 : 3478;

  // First check the known good ports for webrtc
  bool goodPort = false;
  for (int i = 0; !goodPort && gGoodWebrtcPortList[i]; i++) {
    if (port == gGoodWebrtcPortList[i]) {
      goodPort = true;
    }
  }

  // if not in the list of known good ports for webrtc, check
  // the generic block list using NS_CheckPortSafety.
  if (!goodPort) {
    rv = NS_CheckPortSafety(port, nullptr);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  if (isStuns || isTurns) {
    // Should we barf if transport is set to udp or something?
    transport = kNrIceTransportTls;
  }

  if (transport.IsEmpty()) {
    transport = kNrIceTransportUdp;
  }

  if (isTurn || isTurns) {
    std::string pwd(
        NS_ConvertUTF16toUTF8(aIceServer.mCredential.Value()).get());
    std::string username(
        NS_ConvertUTF16toUTF8(aIceServer.mUsername.Value()).get());

    std::vector<unsigned char> password(pwd.begin(), pwd.end());

    UniquePtr<NrIceTurnServer> server(NrIceTurnServer::Create(
        host.get(), port, username, password, transport.get()));
    if (!server) {
      return NS_ERROR_FAILURE;
    }
    if (server->HasFqdn()) {
      // Add an IPv4 entry, then an IPv6 entry
      aTurnServersOut->push_back(*server);
      server->SetUseIPv6IfFqdn();
    }
    aTurnServersOut->emplace_back(std::move(*server));
  } else {
    UniquePtr<NrIceStunServer> server(
        NrIceStunServer::Create(host.get(), port, transport.get()));
    if (!server) {
      return NS_ERROR_FAILURE;
    }
    if (server->HasFqdn()) {
      // Add an IPv4 entry, then an IPv6 entry
      aStunServersOut->push_back(*server);
      server->SetUseIPv6IfFqdn();
    }
    aStunServersOut->emplace_back(std::move(*server));
  }
  return NS_OK;
}

/* static */
nsresult MediaTransportHandler::ConvertIceServers(
    const nsTArray<dom::RTCIceServer>& aIceServers,
    std::vector<NrIceStunServer>* aStunServers,
    std::vector<NrIceTurnServer>* aTurnServers) {
  for (const auto& iceServer : aIceServers) {
    NS_ENSURE_STATE(iceServer.mUrls.WasPassed());
    NS_ENSURE_STATE(iceServer.mUrls.Value().IsStringSequence());
    for (const auto& iceUrl : iceServer.mUrls.Value().GetAsStringSequence()) {
      nsresult rv =
          addNrIceServer(iceUrl, iceServer, aStunServers, aTurnServers);
      if (NS_FAILED(rv)) {
        CSFLogError(LOGTAG, "%s: invalid STUN/TURN server: %s", __FUNCTION__,
                    NS_ConvertUTF16toUTF8(iceUrl).get());
        return rv;
      }
    }
  }

  return NS_OK;
}

static NrIceCtx::GlobalConfig GetGlobalConfig() {
  NrIceCtx::GlobalConfig config;
  config.mAllowLinkLocal =
      Preferences::GetBool("media.peerconnection.ice.link_local"false);
  config.mAllowLoopback =
      Preferences::GetBool("media.peerconnection.ice.loopback"false);
  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 = filtering_type;
    natConfig.mMappingType = 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 = redirect_address;
      std::stringstream str(redirect_targets.Data());
      std::string target;
      while (getline(str, target, ',')) {
        CSFLogDebug(LOGTAG, "Adding target: %s", target.c_str());
        natConfig.mRedirectTargets.AppendElement(target);
      }
    }
    return Some(natConfig);
  }
  return Nothing();
}

void MediaTransportHandlerSTS::CreateIceCtx(const std::string& aName) {
  mInitPromise = InvokeAsync(
      GetMainThreadSerialEventTarget(), __func__,
      [=, 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();

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

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

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

              mDNSResolver = new 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__);
            });
      });
}

nsresult MediaTransportHandlerSTS::SetIceConfig(
    const nsTArray<dom::RTCIceServer>& aIceServers,
    dom::RTCIceTransportPolicy aIcePolicy) {
  // We rely on getting an error when this happens, so do it up front.
  std::vector<NrIceStunServer> stunServers;
  std::vector<NrIceTurnServer> turnServers;
  nsresult rv = ConvertIceServers(aIceServers, &stunServers, &turnServers);
  if (NS_FAILED(rv)) {
    return rv;
  }

  MOZ_RELEASE_ASSERT(mInitPromise);

  mInitPromise->Then(
      mStsThread, __func__,
      [=, 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.mNatSimulatorConfig = mNatConfig;

        nsresult rv;

        if (NS_FAILED(rv = mIceCtx->SetStunServers(stunServers))) {
          CSFLogError(LOGTAG, "%s: Failed to set stun servers", __FUNCTION__);
          return;
        }
        if (!mTurnDisabled) {
          if (NS_FAILED(rv = mIceCtx->SetTurnServers(turnServers))) {
            CSFLogError(LOGTAG, "%s: Failed to set turn servers", __FUNCTION__);
            return;
          }
        } else if (!turnServers.empty()) {
          CSFLogError(LOGTAG, "%s: Setting turn servers disabled",
                      __FUNCTION__);
        }
        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__);
  disconnect_all();
  // 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. However, by the time _that_ happens, we may have
  // dispatched a signal callback to mCallbackThread, so we have to dispatch
  // the final destruction to mCallbackThread.
  nsresult rv = mStsThread->Dispatch(
      NewNonOwningRunnableMethod("MediaTransportHandlerSTS::Destroy_s"this,
                                 &MediaTransportHandlerSTS::Destroy_s));
  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::Destroy_s() {
  if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
    nsresult rv = mCallbackThread->Dispatch(NewNonOwningRunnableMethod(
        __func__, this, &MediaTransportHandlerSTS::DestroyFinal));
    if (NS_SUCCEEDED(rv)) {
      return;
    }
  }

  DestroyFinal();
}

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__,
      [=, 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->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__,
      [=, 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__,
      [=, 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__,
      [=, 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__,
      [=, 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__,
      [=, 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__,
      [=, 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__,
      [=, 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)) {
            if (it->second.mFlow) {
              OnStateChange(transportId, TransportLayer::TS_NONE);
              OnRtcpStateChange(transportId, TransportLayer::TS_NONE);
            }
            // 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 {
  // TODO Bug 1520692: we should allow Datachannel to connect without
  // DTLS SRTP keys
  if (mCallbackThread) {
    MOZ_ASSERT(mCallbackThread->IsOnCurrentThread());
  }

  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,
                                        const CandidateInfo& aCandidateInfo) {
  if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
    mCallbackThread->Dispatch(
        // This is being called from sigslot, which does not hold a strong ref.
        WrapRunnable(this, &MediaTransportHandler::OnCandidate, aTransportId,
                     aCandidateInfo),
        NS_DISPATCH_NORMAL);
    return;
  }

  SignalCandidate(aTransportId, aCandidateInfo);
}

void MediaTransportHandler::OnAlpnNegotiated(const std::string& aAlpn) {
  if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
    mCallbackThread->Dispatch(
        // This is being called from sigslot, which does not hold a strong ref.
        WrapRunnable(this, &MediaTransportHandler::OnAlpnNegotiated, aAlpn),
        NS_DISPATCH_NORMAL);
    return;
  }

  const bool privacyRequested = aAlpn == "c-webrtc";
  SignalAlpnNegotiated(aAlpn, privacyRequested);
}

void MediaTransportHandler::OnGatheringStateChange(
    const std::string& aTransportId, dom::RTCIceGathererState aState) {
  if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
    mCallbackThread->Dispatch(
        // This is being called from sigslot, which does not hold a strong ref.
        WrapRunnable(this, &MediaTransportHandler::OnGatheringStateChange,
                     aTransportId, aState),
        NS_DISPATCH_NORMAL);
    return;
  }

  SignalGatheringStateChange(aTransportId, aState);
}

void MediaTransportHandler::OnConnectionStateChange(
    const std::string& aTransportId, dom::RTCIceTransportState aState) {
  if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
    mCallbackThread->Dispatch(
        // This is being called from sigslot, which does not hold a strong ref.
        WrapRunnable(this, &MediaTransportHandler::OnConnectionStateChange,
                     aTransportId, aState),
        NS_DISPATCH_NORMAL);
    return;
  }

  SignalConnectionStateChange(aTransportId, aState);
}

void MediaTransportHandler::OnPacketReceived(const std::string& aTransportId,
                                             const MediaPacket& aPacket) {
  if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
    mCallbackThread->Dispatch(
        // This is being called from sigslot, which does not hold a strong ref.
        WrapRunnable(this, &MediaTransportHandler::OnPacketReceived,
                     aTransportId, aPacket.Clone()),
        NS_DISPATCH_NORMAL);
    return;
  }

  SignalPacketReceived(aTransportId, aPacket);
}

void MediaTransportHandler::OnEncryptedSending(const std::string& aTransportId,
                                               const MediaPacket& aPacket) {
  if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
    mCallbackThread->Dispatch(
        // This is being called from sigslot, which does not hold a strong ref.
        WrapRunnable(this, &MediaTransportHandler::OnEncryptedSending,
                     aTransportId, aPacket.Clone()),
        NS_DISPATCH_NORMAL);
    return;
  }

  SignalEncryptedSending(aTransportId, aPacket);
}

void MediaTransportHandler::OnStateChange(const std::string& aTransportId,
                                          TransportLayer::State aState) {
  if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
    mCallbackThread->Dispatch(
        // This is being called from sigslot, which does not hold a strong ref.
        WrapRunnable(this, &MediaTransportHandler::OnStateChange, aTransportId,
                     aState),
        NS_DISPATCH_NORMAL);
    return;
  }

  if (aState == TransportLayer::TS_NONE) {
    mStateCache.erase(aTransportId);
  } else {
    mStateCache[aTransportId] = aState;
  }
  SignalStateChange(aTransportId, aState);
}

void MediaTransportHandler::OnRtcpStateChange(const std::string& aTransportId,
                                              TransportLayer::State aState) {
  if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
    mCallbackThread->Dispatch(
        // This is being called from sigslot, which does not hold a strong ref.
        WrapRunnable(this, &MediaTransportHandler::OnRtcpStateChange,
                     aTransportId, aState),
        NS_DISPATCH_NORMAL);
    return;
  }

  if (aState == TransportLayer::TS_NONE) {
    mRtcpStateCache.erase(aTransportId);
  } else {
    mRtcpStateCache[aTransportId] = aState;
  }
  SignalRtcpStateChange(aTransportId, aState);
}

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

  return mInitPromise->Then(mStsThread, __func__, [=, self = RefPtr(this)]() {
    UniquePtr<dom::RTCStatsCollection> stats(new dom::RTCStatsCollection);
    if (mIceCtx) {
      for (const auto& stream : mIceCtx->GetStreams()) {
        if (aTransportId.empty() || aTransportId == stream->GetId()) {
          GetIceStats(*stream, aNow, stats.get());
        }
      }
    }
    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.Construct(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.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) 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.Construct(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.mComponentId.Construct(candPair.component_id);
    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<TransportFlow> flow = new 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(), info);
}

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

  // 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);
}

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(aLayer->flow_id(), aPacket);
}

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

}  // namespace mozilla

#undef MEDIA_TRANSPORT_HANDLER_PACKET_RECEIVED

Messung V0.5
C=93 H=96 G=94

¤ Dauer der Verarbeitung: 0.8 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 und die Messung sind noch experimentell.