summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/pc/jsep_transport.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /third_party/libwebrtc/pc/jsep_transport.cc
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/pc/jsep_transport.cc')
-rw-r--r--third_party/libwebrtc/pc/jsep_transport.cc711
1 files changed, 711 insertions, 0 deletions
diff --git a/third_party/libwebrtc/pc/jsep_transport.cc b/third_party/libwebrtc/pc/jsep_transport.cc
new file mode 100644
index 0000000000..ec186626b7
--- /dev/null
+++ b/third_party/libwebrtc/pc/jsep_transport.cc
@@ -0,0 +1,711 @@
+/*
+ * Copyright 2018 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "pc/jsep_transport.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "api/array_view.h"
+#include "api/candidate.h"
+#include "p2p/base/p2p_constants.h"
+#include "p2p/base/p2p_transport_channel.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/copy_on_write_buffer.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/strings/string_builder.h"
+#include "rtc_base/trace_event.h"
+
+using webrtc::SdpType;
+
+namespace cricket {
+
+JsepTransportDescription::JsepTransportDescription() {}
+
+JsepTransportDescription::JsepTransportDescription(
+ bool rtcp_mux_enabled,
+ const std::vector<CryptoParams>& cryptos,
+ const std::vector<int>& encrypted_header_extension_ids,
+ int rtp_abs_sendtime_extn_id,
+ const TransportDescription& transport_desc)
+ : rtcp_mux_enabled(rtcp_mux_enabled),
+ cryptos(cryptos),
+ encrypted_header_extension_ids(encrypted_header_extension_ids),
+ rtp_abs_sendtime_extn_id(rtp_abs_sendtime_extn_id),
+ transport_desc(transport_desc) {}
+
+JsepTransportDescription::JsepTransportDescription(
+ const JsepTransportDescription& from)
+ : rtcp_mux_enabled(from.rtcp_mux_enabled),
+ cryptos(from.cryptos),
+ encrypted_header_extension_ids(from.encrypted_header_extension_ids),
+ rtp_abs_sendtime_extn_id(from.rtp_abs_sendtime_extn_id),
+ transport_desc(from.transport_desc) {}
+
+JsepTransportDescription::~JsepTransportDescription() = default;
+
+JsepTransportDescription& JsepTransportDescription::operator=(
+ const JsepTransportDescription& from) {
+ if (this == &from) {
+ return *this;
+ }
+ rtcp_mux_enabled = from.rtcp_mux_enabled;
+ cryptos = from.cryptos;
+ encrypted_header_extension_ids = from.encrypted_header_extension_ids;
+ rtp_abs_sendtime_extn_id = from.rtp_abs_sendtime_extn_id;
+ transport_desc = from.transport_desc;
+
+ return *this;
+}
+
+JsepTransport::JsepTransport(
+ const std::string& mid,
+ const rtc::scoped_refptr<rtc::RTCCertificate>& local_certificate,
+ rtc::scoped_refptr<webrtc::IceTransportInterface> ice_transport,
+ rtc::scoped_refptr<webrtc::IceTransportInterface> rtcp_ice_transport,
+ std::unique_ptr<webrtc::RtpTransport> unencrypted_rtp_transport,
+ std::unique_ptr<webrtc::SrtpTransport> sdes_transport,
+ std::unique_ptr<webrtc::DtlsSrtpTransport> dtls_srtp_transport,
+ std::unique_ptr<DtlsTransportInternal> rtp_dtls_transport,
+ std::unique_ptr<DtlsTransportInternal> rtcp_dtls_transport,
+ std::unique_ptr<SctpTransportInternal> sctp_transport,
+ std::function<void()> rtcp_mux_active_callback)
+ : network_thread_(rtc::Thread::Current()),
+ mid_(mid),
+ local_certificate_(local_certificate),
+ ice_transport_(std::move(ice_transport)),
+ rtcp_ice_transport_(std::move(rtcp_ice_transport)),
+ unencrypted_rtp_transport_(std::move(unencrypted_rtp_transport)),
+ sdes_transport_(std::move(sdes_transport)),
+ dtls_srtp_transport_(std::move(dtls_srtp_transport)),
+ rtp_dtls_transport_(rtp_dtls_transport
+ ? rtc::make_ref_counted<webrtc::DtlsTransport>(
+ std::move(rtp_dtls_transport))
+ : nullptr),
+ rtcp_dtls_transport_(rtcp_dtls_transport
+ ? rtc::make_ref_counted<webrtc::DtlsTransport>(
+ std::move(rtcp_dtls_transport))
+ : nullptr),
+ sctp_transport_(sctp_transport
+ ? rtc::make_ref_counted<webrtc::SctpTransport>(
+ std::move(sctp_transport))
+ : nullptr),
+ rtcp_mux_active_callback_(std::move(rtcp_mux_active_callback)) {
+ TRACE_EVENT0("webrtc", "JsepTransport::JsepTransport");
+ RTC_DCHECK(ice_transport_);
+ RTC_DCHECK(rtp_dtls_transport_);
+ // `rtcp_ice_transport_` must be present iff `rtcp_dtls_transport_` is
+ // present.
+ RTC_DCHECK_EQ((rtcp_ice_transport_ != nullptr),
+ (rtcp_dtls_transport_ != nullptr));
+ // Verify the "only one out of these three can be set" invariant.
+ if (unencrypted_rtp_transport_) {
+ RTC_DCHECK(!sdes_transport);
+ RTC_DCHECK(!dtls_srtp_transport);
+ } else if (sdes_transport_) {
+ RTC_DCHECK(!unencrypted_rtp_transport);
+ RTC_DCHECK(!dtls_srtp_transport);
+ } else {
+ RTC_DCHECK(dtls_srtp_transport_);
+ RTC_DCHECK(!unencrypted_rtp_transport);
+ RTC_DCHECK(!sdes_transport);
+ }
+
+ if (sctp_transport_) {
+ sctp_transport_->SetDtlsTransport(rtp_dtls_transport_);
+ }
+}
+
+JsepTransport::~JsepTransport() {
+ TRACE_EVENT0("webrtc", "JsepTransport::~JsepTransport");
+ if (sctp_transport_) {
+ sctp_transport_->Clear();
+ }
+
+ // Clear all DtlsTransports. There may be pointers to these from
+ // other places, so we can't assume they'll be deleted by the destructor.
+ rtp_dtls_transport_->Clear();
+ if (rtcp_dtls_transport_) {
+ rtcp_dtls_transport_->Clear();
+ }
+
+ // ICE will be the last transport to be deleted.
+}
+
+webrtc::RTCError JsepTransport::SetLocalJsepTransportDescription(
+ const JsepTransportDescription& jsep_description,
+ SdpType type) {
+ webrtc::RTCError error;
+ TRACE_EVENT0("webrtc", "JsepTransport::SetLocalJsepTransportDescription");
+ RTC_DCHECK_RUN_ON(network_thread_);
+
+ IceParameters ice_parameters =
+ jsep_description.transport_desc.GetIceParameters();
+ webrtc::RTCError ice_parameters_result = ice_parameters.Validate();
+ if (!ice_parameters_result.ok()) {
+ rtc::StringBuilder sb;
+ sb << "Invalid ICE parameters: " << ice_parameters_result.message();
+ return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
+ sb.Release());
+ }
+
+ if (!SetRtcpMux(jsep_description.rtcp_mux_enabled, type,
+ ContentSource::CS_LOCAL)) {
+ return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Failed to setup RTCP mux.");
+ }
+
+ // If doing SDES, setup the SDES crypto parameters.
+ if (sdes_transport_) {
+ RTC_DCHECK(!unencrypted_rtp_transport_);
+ RTC_DCHECK(!dtls_srtp_transport_);
+ if (!SetSdes(jsep_description.cryptos,
+ jsep_description.encrypted_header_extension_ids, type,
+ ContentSource::CS_LOCAL)) {
+ return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Failed to setup SDES crypto parameters.");
+ }
+ } else if (dtls_srtp_transport_) {
+ RTC_DCHECK(!unencrypted_rtp_transport_);
+ RTC_DCHECK(!sdes_transport_);
+ dtls_srtp_transport_->UpdateRecvEncryptedHeaderExtensionIds(
+ jsep_description.encrypted_header_extension_ids);
+ }
+ bool ice_restarting =
+ local_description_ != nullptr &&
+ IceCredentialsChanged(local_description_->transport_desc.ice_ufrag,
+ local_description_->transport_desc.ice_pwd,
+ ice_parameters.ufrag, ice_parameters.pwd);
+ local_description_.reset(new JsepTransportDescription(jsep_description));
+
+ rtc::SSLFingerprint* local_fp =
+ local_description_->transport_desc.identity_fingerprint.get();
+
+ if (!local_fp) {
+ local_certificate_ = nullptr;
+ } else {
+ error = VerifyCertificateFingerprint(local_certificate_.get(), local_fp);
+ if (!error.ok()) {
+ local_description_.reset();
+ return error;
+ }
+ }
+ RTC_DCHECK(rtp_dtls_transport_->internal());
+ rtp_dtls_transport_->internal()->ice_transport()->SetIceParameters(
+ ice_parameters);
+
+ if (rtcp_dtls_transport_) {
+ RTC_DCHECK(rtcp_dtls_transport_->internal());
+ rtcp_dtls_transport_->internal()->ice_transport()->SetIceParameters(
+ ice_parameters);
+ }
+ // If PRANSWER/ANSWER is set, we should decide transport protocol type.
+ if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
+ error = NegotiateAndSetDtlsParameters(type);
+ }
+ if (!error.ok()) {
+ local_description_.reset();
+ return error;
+ }
+
+ if (needs_ice_restart_ && ice_restarting) {
+ needs_ice_restart_ = false;
+ RTC_LOG(LS_VERBOSE) << "needs-ice-restart flag cleared for transport "
+ << mid();
+ }
+
+ return webrtc::RTCError::OK();
+}
+
+webrtc::RTCError JsepTransport::SetRemoteJsepTransportDescription(
+ const JsepTransportDescription& jsep_description,
+ webrtc::SdpType type) {
+ TRACE_EVENT0("webrtc", "JsepTransport::SetLocalJsepTransportDescription");
+ webrtc::RTCError error;
+
+ RTC_DCHECK_RUN_ON(network_thread_);
+
+ IceParameters ice_parameters =
+ jsep_description.transport_desc.GetIceParameters();
+ webrtc::RTCError ice_parameters_result = ice_parameters.Validate();
+ if (!ice_parameters_result.ok()) {
+ remote_description_.reset();
+ rtc::StringBuilder sb;
+ sb << "Invalid ICE parameters: " << ice_parameters_result.message();
+ return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
+ sb.Release());
+ }
+
+ if (!SetRtcpMux(jsep_description.rtcp_mux_enabled, type,
+ ContentSource::CS_REMOTE)) {
+ return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Failed to setup RTCP mux.");
+ }
+
+ // If doing SDES, setup the SDES crypto parameters.
+ if (sdes_transport_) {
+ RTC_DCHECK(!unencrypted_rtp_transport_);
+ RTC_DCHECK(!dtls_srtp_transport_);
+ if (!SetSdes(jsep_description.cryptos,
+ jsep_description.encrypted_header_extension_ids, type,
+ ContentSource::CS_REMOTE)) {
+ return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Failed to setup SDES crypto parameters.");
+ }
+ sdes_transport_->CacheRtpAbsSendTimeHeaderExtension(
+ jsep_description.rtp_abs_sendtime_extn_id);
+ } else if (dtls_srtp_transport_) {
+ RTC_DCHECK(!unencrypted_rtp_transport_);
+ RTC_DCHECK(!sdes_transport_);
+ dtls_srtp_transport_->UpdateSendEncryptedHeaderExtensionIds(
+ jsep_description.encrypted_header_extension_ids);
+ dtls_srtp_transport_->CacheRtpAbsSendTimeHeaderExtension(
+ jsep_description.rtp_abs_sendtime_extn_id);
+ }
+
+ remote_description_.reset(new JsepTransportDescription(jsep_description));
+ RTC_DCHECK(rtp_dtls_transport());
+ SetRemoteIceParameters(ice_parameters, rtp_dtls_transport()->ice_transport());
+
+ if (rtcp_dtls_transport()) {
+ SetRemoteIceParameters(ice_parameters,
+ rtcp_dtls_transport()->ice_transport());
+ }
+
+ // If PRANSWER/ANSWER is set, we should decide transport protocol type.
+ if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
+ error = NegotiateAndSetDtlsParameters(SdpType::kOffer);
+ }
+ if (!error.ok()) {
+ remote_description_.reset();
+ return error;
+ }
+ return webrtc::RTCError::OK();
+}
+
+webrtc::RTCError JsepTransport::AddRemoteCandidates(
+ const Candidates& candidates) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ if (!local_description_ || !remote_description_) {
+ return webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE,
+ mid() +
+ " is not ready to use the remote candidate "
+ "because the local or remote description is "
+ "not set.");
+ }
+
+ for (const cricket::Candidate& candidate : candidates) {
+ auto transport =
+ candidate.component() == cricket::ICE_CANDIDATE_COMPONENT_RTP
+ ? rtp_dtls_transport_
+ : rtcp_dtls_transport_;
+ if (!transport) {
+ return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Candidate has an unknown component: " +
+ candidate.ToSensitiveString() + " for mid " +
+ mid());
+ }
+ RTC_DCHECK(transport->internal() && transport->internal()->ice_transport());
+ transport->internal()->ice_transport()->AddRemoteCandidate(candidate);
+ }
+ return webrtc::RTCError::OK();
+}
+
+void JsepTransport::SetNeedsIceRestartFlag() {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ if (!needs_ice_restart_) {
+ needs_ice_restart_ = true;
+ RTC_LOG(LS_VERBOSE) << "needs-ice-restart flag set for transport " << mid();
+ }
+}
+
+absl::optional<rtc::SSLRole> JsepTransport::GetDtlsRole() const {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ RTC_DCHECK(rtp_dtls_transport_);
+ RTC_DCHECK(rtp_dtls_transport_->internal());
+ rtc::SSLRole dtls_role;
+ if (!rtp_dtls_transport_->internal()->GetDtlsRole(&dtls_role)) {
+ return absl::optional<rtc::SSLRole>();
+ }
+
+ return absl::optional<rtc::SSLRole>(dtls_role);
+}
+
+bool JsepTransport::GetStats(TransportStats* stats) {
+ TRACE_EVENT0("webrtc", "JsepTransport::GetStats");
+ RTC_DCHECK_RUN_ON(network_thread_);
+ stats->transport_name = mid();
+ stats->channel_stats.clear();
+ RTC_DCHECK(rtp_dtls_transport_->internal());
+ bool ret = GetTransportStats(rtp_dtls_transport_->internal(),
+ ICE_CANDIDATE_COMPONENT_RTP, stats);
+
+ if (rtcp_dtls_transport_) {
+ RTC_DCHECK(rtcp_dtls_transport_->internal());
+ ret &= GetTransportStats(rtcp_dtls_transport_->internal(),
+ ICE_CANDIDATE_COMPONENT_RTCP, stats);
+ }
+ return ret;
+}
+
+webrtc::RTCError JsepTransport::VerifyCertificateFingerprint(
+ const rtc::RTCCertificate* certificate,
+ const rtc::SSLFingerprint* fingerprint) const {
+ TRACE_EVENT0("webrtc", "JsepTransport::VerifyCertificateFingerprint");
+ RTC_DCHECK_RUN_ON(network_thread_);
+ if (!fingerprint) {
+ return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
+ "No fingerprint");
+ }
+ if (!certificate) {
+ return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Fingerprint provided but no identity available.");
+ }
+ std::unique_ptr<rtc::SSLFingerprint> fp_tmp =
+ rtc::SSLFingerprint::CreateUnique(fingerprint->algorithm,
+ *certificate->identity());
+ RTC_DCHECK(fp_tmp.get() != NULL);
+ if (*fp_tmp == *fingerprint) {
+ return webrtc::RTCError::OK();
+ }
+ char ss_buf[1024];
+ rtc::SimpleStringBuilder desc(ss_buf);
+ desc << "Local fingerprint does not match identity. Expected: ";
+ desc << fp_tmp->ToString();
+ desc << " Got: " << fingerprint->ToString();
+ return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
+ std::string(desc.str()));
+}
+
+void JsepTransport::SetActiveResetSrtpParams(bool active_reset_srtp_params) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ if (dtls_srtp_transport_) {
+ RTC_LOG(LS_INFO)
+ << "Setting active_reset_srtp_params of DtlsSrtpTransport to: "
+ << active_reset_srtp_params;
+ dtls_srtp_transport_->SetActiveResetSrtpParams(active_reset_srtp_params);
+ }
+}
+
+void JsepTransport::SetRemoteIceParameters(
+ const IceParameters& ice_parameters,
+ IceTransportInternal* ice_transport) {
+ TRACE_EVENT0("webrtc", "JsepTransport::SetRemoteIceParameters");
+ RTC_DCHECK_RUN_ON(network_thread_);
+ RTC_DCHECK(ice_transport);
+ RTC_DCHECK(remote_description_);
+ ice_transport->SetRemoteIceParameters(ice_parameters);
+ ice_transport->SetRemoteIceMode(remote_description_->transport_desc.ice_mode);
+}
+
+webrtc::RTCError JsepTransport::SetNegotiatedDtlsParameters(
+ DtlsTransportInternal* dtls_transport,
+ absl::optional<rtc::SSLRole> dtls_role,
+ rtc::SSLFingerprint* remote_fingerprint) {
+ RTC_DCHECK(dtls_transport);
+ return dtls_transport->SetRemoteParameters(
+ remote_fingerprint->algorithm, remote_fingerprint->digest.cdata(),
+ remote_fingerprint->digest.size(), dtls_role);
+}
+
+bool JsepTransport::SetRtcpMux(bool enable,
+ webrtc::SdpType type,
+ ContentSource source) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ bool ret = false;
+ switch (type) {
+ case SdpType::kOffer:
+ ret = rtcp_mux_negotiator_.SetOffer(enable, source);
+ break;
+ case SdpType::kPrAnswer:
+ // This may activate RTCP muxing, but we don't yet destroy the transport
+ // because the final answer may deactivate it.
+ ret = rtcp_mux_negotiator_.SetProvisionalAnswer(enable, source);
+ break;
+ case SdpType::kAnswer:
+ ret = rtcp_mux_negotiator_.SetAnswer(enable, source);
+ if (ret && rtcp_mux_negotiator_.IsActive()) {
+ ActivateRtcpMux();
+ }
+ break;
+ default:
+ RTC_DCHECK_NOTREACHED();
+ }
+
+ if (!ret) {
+ return false;
+ }
+
+ auto transport = rtp_transport();
+ transport->SetRtcpMuxEnabled(rtcp_mux_negotiator_.IsActive());
+ return ret;
+}
+
+void JsepTransport::ActivateRtcpMux() {
+ if (unencrypted_rtp_transport_) {
+ RTC_DCHECK(!sdes_transport_);
+ RTC_DCHECK(!dtls_srtp_transport_);
+ unencrypted_rtp_transport_->SetRtcpPacketTransport(nullptr);
+ } else if (sdes_transport_) {
+ RTC_DCHECK(!unencrypted_rtp_transport_);
+ RTC_DCHECK(!dtls_srtp_transport_);
+ sdes_transport_->SetRtcpPacketTransport(nullptr);
+ } else if (dtls_srtp_transport_) {
+ RTC_DCHECK(dtls_srtp_transport_);
+ RTC_DCHECK(!unencrypted_rtp_transport_);
+ RTC_DCHECK(!sdes_transport_);
+ dtls_srtp_transport_->SetDtlsTransports(rtp_dtls_transport(),
+ /*rtcp_dtls_transport=*/nullptr);
+ }
+ rtcp_dtls_transport_ = nullptr; // Destroy this reference.
+ // Notify the JsepTransportController to update the aggregate states.
+ rtcp_mux_active_callback_();
+}
+
+bool JsepTransport::SetSdes(const std::vector<CryptoParams>& cryptos,
+ const std::vector<int>& encrypted_extension_ids,
+ webrtc::SdpType type,
+ ContentSource source) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ bool ret = false;
+ ret = sdes_negotiator_.Process(cryptos, type, source);
+ if (!ret) {
+ return ret;
+ }
+
+ if (source == ContentSource::CS_LOCAL) {
+ recv_extension_ids_ = encrypted_extension_ids;
+ } else {
+ send_extension_ids_ = encrypted_extension_ids;
+ }
+
+ // If setting an SDES answer succeeded, apply the negotiated parameters
+ // to the SRTP transport.
+ if ((type == SdpType::kPrAnswer || type == SdpType::kAnswer) && ret) {
+ if (sdes_negotiator_.send_cipher_suite() &&
+ sdes_negotiator_.recv_cipher_suite()) {
+ RTC_DCHECK(send_extension_ids_);
+ RTC_DCHECK(recv_extension_ids_);
+ ret = sdes_transport_->SetRtpParams(
+ *(sdes_negotiator_.send_cipher_suite()),
+ sdes_negotiator_.send_key().data(),
+ static_cast<int>(sdes_negotiator_.send_key().size()),
+ *(send_extension_ids_), *(sdes_negotiator_.recv_cipher_suite()),
+ sdes_negotiator_.recv_key().data(),
+ static_cast<int>(sdes_negotiator_.recv_key().size()),
+ *(recv_extension_ids_));
+ } else {
+ RTC_LOG(LS_INFO) << "No crypto keys are provided for SDES.";
+ if (type == SdpType::kAnswer) {
+ // Explicitly reset the `sdes_transport_` if no crypto param is
+ // provided in the answer. No need to call `ResetParams()` for
+ // `sdes_negotiator_` because it resets the params inside `SetAnswer`.
+ sdes_transport_->ResetParams();
+ }
+ }
+ }
+ return ret;
+}
+
+webrtc::RTCError JsepTransport::NegotiateAndSetDtlsParameters(
+ SdpType local_description_type) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ if (!local_description_ || !remote_description_) {
+ return webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE,
+ "Applying an answer transport description "
+ "without applying any offer.");
+ }
+ std::unique_ptr<rtc::SSLFingerprint> remote_fingerprint;
+ absl::optional<rtc::SSLRole> negotiated_dtls_role;
+
+ rtc::SSLFingerprint* local_fp =
+ local_description_->transport_desc.identity_fingerprint.get();
+ rtc::SSLFingerprint* remote_fp =
+ remote_description_->transport_desc.identity_fingerprint.get();
+ if (remote_fp && local_fp) {
+ remote_fingerprint = std::make_unique<rtc::SSLFingerprint>(*remote_fp);
+ webrtc::RTCError error =
+ NegotiateDtlsRole(local_description_type,
+ local_description_->transport_desc.connection_role,
+ remote_description_->transport_desc.connection_role,
+ &negotiated_dtls_role);
+ if (!error.ok()) {
+ return error;
+ }
+ } else if (local_fp && (local_description_type == SdpType::kAnswer)) {
+ return webrtc::RTCError(
+ webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Local fingerprint supplied when caller didn't offer DTLS.");
+ } else {
+ // We are not doing DTLS
+ remote_fingerprint = std::make_unique<rtc::SSLFingerprint>(
+ "", rtc::ArrayView<const uint8_t>());
+ }
+ // Now that we have negotiated everything, push it downward.
+ // Note that we cache the result so that if we have race conditions
+ // between future SetRemote/SetLocal invocations and new transport
+ // creation, we have the negotiation state saved until a new
+ // negotiation happens.
+ RTC_DCHECK(rtp_dtls_transport());
+ webrtc::RTCError error = SetNegotiatedDtlsParameters(
+ rtp_dtls_transport(), negotiated_dtls_role, remote_fingerprint.get());
+ if (!error.ok()) {
+ return error;
+ }
+
+ if (rtcp_dtls_transport()) {
+ error = SetNegotiatedDtlsParameters(
+ rtcp_dtls_transport(), negotiated_dtls_role, remote_fingerprint.get());
+ }
+ return error;
+}
+
+webrtc::RTCError JsepTransport::NegotiateDtlsRole(
+ SdpType local_description_type,
+ ConnectionRole local_connection_role,
+ ConnectionRole remote_connection_role,
+ absl::optional<rtc::SSLRole>* negotiated_dtls_role) {
+ // From RFC 4145, section-4.1, The following are the values that the
+ // 'setup' attribute can take in an offer/answer exchange:
+ // Offer Answer
+ // ________________
+ // active passive / holdconn
+ // passive active / holdconn
+ // actpass active / passive / holdconn
+ // holdconn holdconn
+ //
+ // Set the role that is most conformant with RFC 5763, Section 5, bullet 1
+ // The endpoint MUST use the setup attribute defined in [RFC4145].
+ // The endpoint that is the offerer MUST use the setup attribute
+ // value of setup:actpass and be prepared to receive a client_hello
+ // before it receives the answer. The answerer MUST use either a
+ // setup attribute value of setup:active or setup:passive. Note that
+ // if the answerer uses setup:passive, then the DTLS handshake will
+ // not begin until the answerer is received, which adds additional
+ // latency. setup:active allows the answer and the DTLS handshake to
+ // occur in parallel. Thus, setup:active is RECOMMENDED. Whichever
+ // party is active MUST initiate a DTLS handshake by sending a
+ // ClientHello over each flow (host/port quartet).
+ // IOW - actpass and passive modes should be treated as server and
+ // active as client.
+ // RFC 8842 section 5.3 updates this text, so that it is mandated
+ // for the responder to handle offers with "active" and "passive"
+ // as well as "actpass"
+ bool is_remote_server = false;
+ if (local_description_type == SdpType::kOffer) {
+ if (local_connection_role != CONNECTIONROLE_ACTPASS) {
+ return webrtc::RTCError(
+ webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Offerer must use actpass value for setup attribute.");
+ }
+
+ if (remote_connection_role == CONNECTIONROLE_ACTIVE ||
+ remote_connection_role == CONNECTIONROLE_PASSIVE ||
+ remote_connection_role == CONNECTIONROLE_NONE) {
+ is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE);
+ } else {
+ return webrtc::RTCError(
+ webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Answerer must use either active or passive value "
+ "for setup attribute.");
+ }
+ // If remote is NONE or ACTIVE it will act as client.
+ } else {
+ if (remote_connection_role != CONNECTIONROLE_ACTPASS &&
+ remote_connection_role != CONNECTIONROLE_NONE) {
+ // Accept a remote role attribute that's not "actpass", but matches the
+ // current negotiated role. This is allowed by dtls-sdp, though our
+ // implementation will never generate such an offer as it's not
+ // recommended.
+ //
+ // See https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-dtls-sdp,
+ // section 5.5.
+ auto current_dtls_role = GetDtlsRole();
+ if (!current_dtls_role) {
+ // Role not assigned yet. Verify that local role fits with remote role.
+ switch (remote_connection_role) {
+ case CONNECTIONROLE_ACTIVE:
+ if (local_connection_role != CONNECTIONROLE_PASSIVE) {
+ return webrtc::RTCError(
+ webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Answerer must be passive when offerer is active");
+ }
+ break;
+ case CONNECTIONROLE_PASSIVE:
+ if (local_connection_role != CONNECTIONROLE_ACTIVE) {
+ return webrtc::RTCError(
+ webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Answerer must be active when offerer is passive");
+ }
+ break;
+ default:
+ RTC_DCHECK_NOTREACHED();
+ break;
+ }
+ } else {
+ if ((*current_dtls_role == rtc::SSL_CLIENT &&
+ remote_connection_role == CONNECTIONROLE_ACTIVE) ||
+ (*current_dtls_role == rtc::SSL_SERVER &&
+ remote_connection_role == CONNECTIONROLE_PASSIVE)) {
+ return webrtc::RTCError(
+ webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Offerer must use current negotiated role for "
+ "setup attribute.");
+ }
+ }
+ }
+
+ if (local_connection_role == CONNECTIONROLE_ACTIVE ||
+ local_connection_role == CONNECTIONROLE_PASSIVE) {
+ is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE);
+ } else {
+ return webrtc::RTCError(
+ webrtc::RTCErrorType::INVALID_PARAMETER,
+ "Answerer must use either active or passive value "
+ "for setup attribute.");
+ }
+
+ // If local is passive, local will act as server.
+ }
+
+ *negotiated_dtls_role =
+ (is_remote_server ? rtc::SSL_CLIENT : rtc::SSL_SERVER);
+ return webrtc::RTCError::OK();
+}
+
+bool JsepTransport::GetTransportStats(DtlsTransportInternal* dtls_transport,
+ int component,
+ TransportStats* stats) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ RTC_DCHECK(dtls_transport);
+ TransportChannelStats substats;
+ substats.component = component;
+ dtls_transport->GetSslVersionBytes(&substats.ssl_version_bytes);
+ dtls_transport->GetSrtpCryptoSuite(&substats.srtp_crypto_suite);
+ dtls_transport->GetSslCipherSuite(&substats.ssl_cipher_suite);
+ substats.dtls_state = dtls_transport->dtls_state();
+ rtc::SSLRole dtls_role;
+ if (dtls_transport->GetDtlsRole(&dtls_role)) {
+ substats.dtls_role = dtls_role;
+ }
+ if (!dtls_transport->ice_transport()->GetStats(
+ &substats.ice_transport_stats)) {
+ return false;
+ }
+ stats->channel_stats.push_back(substats);
+ return true;
+}
+
+} // namespace cricket