/* * 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 "tg_jsep_transport.h" #include #include #include #include #include // for std::pair #include "api/array_view.h" #include "api/candidate.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/p2p_transport_channel.h" #include "pc/sctp_data_channel_transport.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" using webrtc::SdpType; namespace cricket { static bool VerifyIceParams(const TgJsepTransportDescription& jsep_description) { // For legacy protocols. // TODO(zhihuang): Remove this once the legacy protocol is no longer // supported. if (jsep_description.transport_desc.ice_ufrag.empty() && jsep_description.transport_desc.ice_pwd.empty()) { return true; } if (jsep_description.transport_desc.ice_ufrag.length() < ICE_UFRAG_MIN_LENGTH || jsep_description.transport_desc.ice_ufrag.length() > ICE_UFRAG_MAX_LENGTH) { return false; } if (jsep_description.transport_desc.ice_pwd.length() < ICE_PWD_MIN_LENGTH || jsep_description.transport_desc.ice_pwd.length() > ICE_PWD_MAX_LENGTH) { return false; } return true; } TgJsepTransportDescription::TgJsepTransportDescription() {} TgJsepTransportDescription::TgJsepTransportDescription( bool rtcp_mux_enabled, const std::vector& cryptos, const std::vector& encrypted_header_extension_ids, int rtp_abs_sendtime_extn_id, const TransportDescription& transport_desc, absl::optional media_alt_protocol, absl::optional data_alt_protocol) : 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), media_alt_protocol(media_alt_protocol), data_alt_protocol(data_alt_protocol) {} TgJsepTransportDescription::TgJsepTransportDescription( const TgJsepTransportDescription& 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), media_alt_protocol(from.media_alt_protocol), data_alt_protocol(from.data_alt_protocol) {} TgJsepTransportDescription::~TgJsepTransportDescription() = default; TgJsepTransportDescription& TgJsepTransportDescription::operator=( const TgJsepTransportDescription& 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; media_alt_protocol = from.media_alt_protocol; data_alt_protocol = from.data_alt_protocol; return *this; } TgJsepTransport::TgJsepTransport( const std::string& mid, const rtc::scoped_refptr& local_certificate, rtc::scoped_refptr ice_transport, rtc::scoped_refptr rtcp_ice_transport, std::unique_ptr unencrypted_rtp_transport, std::unique_ptr sdes_transport, std::unique_ptr dtls_srtp_transport, std::unique_ptr datagram_rtp_transport, std::unique_ptr rtp_dtls_transport, std::unique_ptr rtcp_dtls_transport, std::unique_ptr sctp_transport, std::unique_ptr datagram_transport, webrtc::DataChannelTransportInterface* data_channel_transport) : 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 ? new rtc::RefCountedObject( std::move(rtp_dtls_transport)) : nullptr), rtcp_dtls_transport_( rtcp_dtls_transport ? new rtc::RefCountedObject( std::move(rtcp_dtls_transport)) : nullptr), sctp_data_channel_transport_( sctp_transport ? std::make_unique( sctp_transport.get()) : nullptr), sctp_transport_(sctp_transport ? new rtc::RefCountedObject( std::move(sctp_transport)) : nullptr), datagram_transport_(std::move(datagram_transport)), datagram_rtp_transport_(std::move(datagram_rtp_transport)), data_channel_transport_(data_channel_transport) { 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_); } if (datagram_rtp_transport_ && default_rtp_transport()) { composite_rtp_transport_ = std::make_unique( std::vector{ datagram_rtp_transport_.get(), default_rtp_transport()}); } if (data_channel_transport_ && sctp_data_channel_transport_) { composite_data_channel_transport_ = std::make_unique( std::vector{ data_channel_transport_, sctp_data_channel_transport_.get()}); } } TgJsepTransport::~TgJsepTransport() { 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 TgJsepTransport::SetLocalJsepTransportDescription( const TgJsepTransportDescription& jsep_description, SdpType type) { webrtc::RTCError error; RTC_DCHECK_RUN_ON(network_thread_); if (!VerifyIceParams(jsep_description)) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Invalid ice-ufrag or ice-pwd length."); } 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. { rtc::CritScope scope(&accessor_lock_); 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, jsep_description.transport_desc.ice_ufrag, jsep_description.transport_desc.ice_pwd); local_description_.reset(new TgJsepTransportDescription(jsep_description)); rtc::SSLFingerprint* local_fp = local_description_->transport_desc.identity_fingerprint.get(); if (!local_fp) { local_certificate_ = nullptr; } else { error = VerifyCertificateFingerprint(local_certificate_, local_fp); if (!error.ok()) { local_description_.reset(); return error; } } { rtc::CritScope scope(&accessor_lock_); RTC_DCHECK(rtp_dtls_transport_->internal()); SetLocalIceParameters(rtp_dtls_transport_->internal()->ice_transport()); if (rtcp_dtls_transport_) { RTC_DCHECK(rtcp_dtls_transport_->internal()); SetLocalIceParameters(rtcp_dtls_transport_->internal()->ice_transport()); } } // If PRANSWER/ANSWER is set, we should decide transport protocol type. if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { error = NegotiateAndSetDtlsParameters(type); NegotiateDatagramTransport(type); } if (!error.ok()) { local_description_.reset(); return error; } { rtc::CritScope scope(&accessor_lock_); 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 TgJsepTransport::SetRemoteJsepTransportDescription( const TgJsepTransportDescription& jsep_description, webrtc::SdpType type) { webrtc::RTCError error; RTC_DCHECK_RUN_ON(network_thread_); if (!VerifyIceParams(jsep_description)) { remote_description_.reset(); return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Invalid ice-ufrag or ice-pwd length."); } 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. { rtc::CritScope lock(&accessor_lock_); 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 TgJsepTransportDescription(jsep_description)); RTC_DCHECK(rtp_dtls_transport()); SetRemoteIceParameters(rtp_dtls_transport()->ice_transport()); if (rtcp_dtls_transport()) { SetRemoteIceParameters(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); NegotiateDatagramTransport(type); } if (!error.ok()) { remote_description_.reset(); return error; } return webrtc::RTCError::OK(); } webrtc::RTCError TgJsepTransport::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 TgJsepTransport::SetNeedsIceRestartFlag() { rtc::CritScope scope(&accessor_lock_); if (!needs_ice_restart_) { needs_ice_restart_ = true; RTC_LOG(LS_VERBOSE) << "needs-ice-restart flag set for transport " << mid(); } } absl::optional TgJsepTransport::GetDtlsRole() const { RTC_DCHECK_RUN_ON(network_thread_); rtc::CritScope scope(&accessor_lock_); 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(); } return absl::optional(dtls_role); } absl::optional TgJsepTransport::GetTransportParameters() const { rtc::CritScope scope(&accessor_lock_); if (!datagram_transport()) { return absl::nullopt; } OpaqueTransportParameters params; params.parameters = datagram_transport()->GetTransportParameters(); return params; } bool TgJsepTransport::GetStats(TransportStats* stats) { RTC_DCHECK_RUN_ON(network_thread_); rtc::CritScope scope(&accessor_lock_); stats->transport_name = mid(); stats->channel_stats.clear(); RTC_DCHECK(rtp_dtls_transport_->internal()); bool ret = GetTransportStats(rtp_dtls_transport_->internal(), stats); if (rtcp_dtls_transport_) { RTC_DCHECK(rtcp_dtls_transport_->internal()); ret &= GetTransportStats(rtcp_dtls_transport_->internal(), stats); } return ret; } webrtc::RTCError TgJsepTransport::VerifyCertificateFingerprint( const rtc::RTCCertificate* certificate, const rtc::SSLFingerprint* fingerprint) const { 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 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 TgJsepTransport::SetActiveResetSrtpParams(bool active_reset_srtp_params) { RTC_DCHECK_RUN_ON(network_thread_); rtc::CritScope scope(&accessor_lock_); if (dtls_srtp_transport_) { RTC_LOG(INFO) << "Setting active_reset_srtp_params of DtlsSrtpTransport to: " << active_reset_srtp_params; dtls_srtp_transport_->SetActiveResetSrtpParams(active_reset_srtp_params); } } void TgJsepTransport::SetLocalIceParameters(IceTransportInternal* ice_transport) { RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK(ice_transport); RTC_DCHECK(local_description_); ice_transport->SetIceParameters( local_description_->transport_desc.GetIceParameters()); } void TgJsepTransport::SetRemoteIceParameters( IceTransportInternal* ice_transport) { RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK(ice_transport); RTC_DCHECK(remote_description_); ice_transport->SetRemoteIceParameters( remote_description_->transport_desc.GetIceParameters()); ice_transport->SetRemoteIceMode(remote_description_->transport_desc.ice_mode); } webrtc::RTCError TgJsepTransport::SetNegotiatedDtlsParameters( DtlsTransportInternal* dtls_transport, absl::optional dtls_role, rtc::SSLFingerprint* remote_fingerprint) { RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK(dtls_transport); // Set SSL role. Role must be set before fingerprint is applied, which // initiates DTLS setup. if (dtls_role && !dtls_transport->SetDtlsRole(*dtls_role)) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Failed to set SSL role for the transport."); } // Apply remote fingerprint. if (!remote_fingerprint || !dtls_transport->SetRemoteFingerprint( remote_fingerprint->algorithm, remote_fingerprint->digest.cdata(), remote_fingerprint->digest.size())) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Failed to apply remote fingerprint."); } return webrtc::RTCError::OK(); } bool TgJsepTransport::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_NOTREACHED(); } if (!ret) { return false; } auto transport = rtp_transport(); transport->SetRtcpMuxEnabled(rtcp_mux_negotiator_.IsActive()); return ret; } void TgJsepTransport::ActivateRtcpMux() { { // Don't hold the network_thread_ lock while calling other functions, // since they might call other functions that call RTC_DCHECK_RUN_ON. // TODO(https://crbug.com/webrtc/10318): Simplify when possible. RTC_DCHECK_RUN_ON(network_thread_); } { rtc::CritScope scope(&accessor_lock_); 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. SignalRtcpMuxActive(); } bool TgJsepTransport::SetSdes(const std::vector& cryptos, const std::vector& encrypted_extension_ids, webrtc::SdpType type, ContentSource source) { RTC_DCHECK_RUN_ON(network_thread_); rtc::CritScope scope(&accessor_lock_); 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(sdes_negotiator_.send_key().size()), *(send_extension_ids_), *(sdes_negotiator_.recv_cipher_suite()), sdes_negotiator_.recv_key().data(), static_cast(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 TgJsepTransport::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 remote_fingerprint; absl::optional 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(*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::ArrayView()); } // 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 TgJsepTransport::NegotiateDtlsRole( SdpType local_description_type, ConnectionRole local_connection_role, ConnectionRole remote_connection_role, absl::optional* 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. 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 || (*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 actpass value or 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 TgJsepTransport::GetTransportStats(DtlsTransportInternal* dtls_transport, TransportStats* stats) { RTC_DCHECK_RUN_ON(network_thread_); rtc::CritScope scope(&accessor_lock_); RTC_DCHECK(dtls_transport); TransportChannelStats substats; if (rtcp_dtls_transport_) { substats.component = dtls_transport == rtcp_dtls_transport_->internal() ? ICE_CANDIDATE_COMPONENT_RTCP : ICE_CANDIDATE_COMPONENT_RTP; } else { substats.component = ICE_CANDIDATE_COMPONENT_RTP; } 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(); if (!dtls_transport->ice_transport()->GetStats( &substats.ice_transport_stats)) { return false; } stats->channel_stats.push_back(substats); return true; } void TgJsepTransport::NegotiateDatagramTransport(SdpType type) { RTC_DCHECK(type == SdpType::kAnswer || type == SdpType::kPrAnswer); rtc::CritScope lock(&accessor_lock_); if (!datagram_transport_) { return; // No need to negotiate the use of datagram transport. } bool compatible_datagram_transport = remote_description_->transport_desc.opaque_parameters && remote_description_->transport_desc.opaque_parameters == local_description_->transport_desc.opaque_parameters; bool use_datagram_transport_for_media = compatible_datagram_transport && remote_description_->media_alt_protocol == remote_description_->transport_desc.opaque_parameters->protocol && remote_description_->media_alt_protocol == local_description_->media_alt_protocol; bool use_datagram_transport_for_data = compatible_datagram_transport && remote_description_->data_alt_protocol == remote_description_->transport_desc.opaque_parameters->protocol && remote_description_->data_alt_protocol == local_description_->data_alt_protocol; RTC_LOG(LS_INFO) << "Negotiating datagram transport, use_datagram_transport_for_media=" << use_datagram_transport_for_media << ", use_datagram_transport_for_data=" << use_datagram_transport_for_data << " answer type=" << (type == SdpType::kAnswer ? "answer" : "pr_answer"); // A provisional or full or answer lets the peer start sending on one of the // transports. if (composite_rtp_transport_) { composite_rtp_transport_->SetSendTransport( use_datagram_transport_for_media ? datagram_rtp_transport_.get() : default_rtp_transport()); } if (composite_data_channel_transport_) { composite_data_channel_transport_->SetSendTransport( use_datagram_transport_for_data ? data_channel_transport_ : sctp_data_channel_transport_.get()); } if (type != SdpType::kAnswer) { return; } if (composite_rtp_transport_) { if (use_datagram_transport_for_media) { // Negotiated use of datagram transport for RTP, so remove the // non-datagram RTP transport. composite_rtp_transport_->RemoveTransport(default_rtp_transport()); if (unencrypted_rtp_transport_) { unencrypted_rtp_transport_ = nullptr; } else if (sdes_transport_) { sdes_transport_ = nullptr; } else { dtls_srtp_transport_ = nullptr; } } else { composite_rtp_transport_->RemoveTransport(datagram_rtp_transport_.get()); datagram_rtp_transport_ = nullptr; } } if (composite_data_channel_transport_) { if (use_datagram_transport_for_data) { // Negotiated use of datagram transport for data channels, so remove the // non-datagram data channel transport. composite_data_channel_transport_->RemoveTransport( sctp_data_channel_transport_.get()); sctp_data_channel_transport_ = nullptr; sctp_transport_ = nullptr; } else { composite_data_channel_transport_->RemoveTransport( data_channel_transport_); data_channel_transport_ = nullptr; } } else if (data_channel_transport_ && !use_datagram_transport_for_data) { // The datagram transport has been rejected without a fallback. We still // need to inform the application and delete it. SignalDataChannelTransportNegotiated(this, nullptr); data_channel_transport_ = nullptr; } if (!use_datagram_transport_for_media && !use_datagram_transport_for_data) { // Datagram transport is not being used for anything, so clean it up. datagram_transport_ = nullptr; } } } // namespace cricket