Create the JsepTransportController and JsepTransport2.
JsepTransportController process the entire SDP and handle the RTCP-mux, SRTP setup, BUNDLE related logic internally. This will replace the current TransportController. JsepTransport2 is used by the JsepTransportController which processes the transport part of SDP and owns the DtlsTransport created internally. JsepTransport2 will replace JsepTransport and be renamed eventually. Bug: webrtc:8587 Change-Id: Ib02dfa52fe9b7a5b8b132afcc8e4363eb8bd9cf4 Reviewed-on: https://webrtc-review.googlesource.com/48841 Commit-Queue: Zhi Huang <zhihuang@webrtc.org> Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org> Reviewed-by: Peter Thatcher <pthatcher@webrtc.org> Cr-Commit-Position: refs/heads/master@{#22164}
This commit is contained in:
parent
fbf3bce431
commit
e818b6ef7f
@ -66,6 +66,7 @@ rtc_static_library("rtc_p2p") {
|
||||
"base/transportdescription.h",
|
||||
"base/transportdescriptionfactory.cc",
|
||||
"base/transportdescriptionfactory.h",
|
||||
"base/transportfactoryinterface.h",
|
||||
"base/transportinfo.h",
|
||||
"base/turnport.cc",
|
||||
"base/turnport.h",
|
||||
|
||||
@ -121,20 +121,26 @@ DtlsTransport::DtlsTransport(IceTransportInternal* ice_transport,
|
||||
ice_transport_(ice_transport),
|
||||
downward_(NULL),
|
||||
srtp_ciphers_(GetSupportedDtlsSrtpCryptoSuites(crypto_options)),
|
||||
ssl_role_(rtc::SSL_CLIENT),
|
||||
ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12),
|
||||
crypto_options_(crypto_options) {
|
||||
RTC_DCHECK(ice_transport_);
|
||||
ice_transport_->SignalWritableState.connect(this,
|
||||
&DtlsTransport::OnWritableState);
|
||||
ice_transport_->SignalReadPacket.connect(this, &DtlsTransport::OnReadPacket);
|
||||
ice_transport_->SignalSentPacket.connect(this, &DtlsTransport::OnSentPacket);
|
||||
ice_transport_->SignalReadyToSend.connect(this,
|
||||
&DtlsTransport::OnReadyToSend);
|
||||
ice_transport_->SignalReceivingState.connect(
|
||||
this, &DtlsTransport::OnReceivingState);
|
||||
ice_transport_->SignalNetworkRouteChanged.connect(
|
||||
this, &DtlsTransport::OnNetworkRouteChanged);
|
||||
ConnectToIceTransport();
|
||||
}
|
||||
|
||||
DtlsTransport::DtlsTransport(
|
||||
std::unique_ptr<IceTransportInternal> ice_transport,
|
||||
const rtc::CryptoOptions& crypto_options)
|
||||
: transport_name_(ice_transport->transport_name()),
|
||||
component_(ice_transport->component()),
|
||||
network_thread_(rtc::Thread::Current()),
|
||||
ice_transport_(ice_transport.get()),
|
||||
owned_ice_transport_(std::move(ice_transport)),
|
||||
downward_(NULL),
|
||||
srtp_ciphers_(GetSupportedDtlsSrtpCryptoSuites(crypto_options)),
|
||||
ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12),
|
||||
crypto_options_(crypto_options) {
|
||||
RTC_DCHECK(owned_ice_transport_);
|
||||
ConnectToIceTransport();
|
||||
}
|
||||
|
||||
DtlsTransport::~DtlsTransport() = default;
|
||||
@ -198,9 +204,10 @@ bool DtlsTransport::SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DtlsTransport::SetSslRole(rtc::SSLRole role) {
|
||||
bool DtlsTransport::SetDtlsRole(rtc::SSLRole role) {
|
||||
if (dtls_) {
|
||||
if (ssl_role_ != role) {
|
||||
RTC_DCHECK(dtls_role_);
|
||||
if (*dtls_role_ != role) {
|
||||
RTC_LOG(LS_ERROR)
|
||||
<< "SSL Role can't be reversed after the session is setup.";
|
||||
return false;
|
||||
@ -208,12 +215,15 @@ bool DtlsTransport::SetSslRole(rtc::SSLRole role) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ssl_role_ = role;
|
||||
dtls_role_ = std::move(role);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DtlsTransport::GetSslRole(rtc::SSLRole* role) const {
|
||||
*role = ssl_role_;
|
||||
bool DtlsTransport::GetDtlsRole(rtc::SSLRole* role) const {
|
||||
if (!dtls_role_) {
|
||||
return false;
|
||||
}
|
||||
*role = *dtls_role_;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -330,6 +340,7 @@ bool DtlsTransport::ExportKeyingMaterial(const std::string& label,
|
||||
}
|
||||
|
||||
bool DtlsTransport::SetupDtls() {
|
||||
RTC_DCHECK(dtls_role_);
|
||||
StreamInterfaceChannel* downward = new StreamInterfaceChannel(ice_transport_);
|
||||
|
||||
dtls_.reset(rtc::SSLStreamAdapter::Create(downward));
|
||||
@ -344,7 +355,7 @@ bool DtlsTransport::SetupDtls() {
|
||||
dtls_->SetIdentity(local_certificate_->identity()->GetReference());
|
||||
dtls_->SetMode(rtc::SSL_MODE_DTLS);
|
||||
dtls_->SetMaxProtocolVersion(ssl_max_version_);
|
||||
dtls_->SetServerRole(ssl_role_);
|
||||
dtls_->SetServerRole(*dtls_role_);
|
||||
dtls_->SignalEvent.connect(this, &DtlsTransport::OnDtlsEvent);
|
||||
dtls_->SignalSSLHandshakeError.connect(this,
|
||||
&DtlsTransport::OnDtlsHandshakeError);
|
||||
@ -456,6 +467,20 @@ int DtlsTransport::SetOption(rtc::Socket::Option opt, int value) {
|
||||
return ice_transport_->SetOption(opt, value);
|
||||
}
|
||||
|
||||
void DtlsTransport::ConnectToIceTransport() {
|
||||
RTC_DCHECK(ice_transport_);
|
||||
ice_transport_->SignalWritableState.connect(this,
|
||||
&DtlsTransport::OnWritableState);
|
||||
ice_transport_->SignalReadPacket.connect(this, &DtlsTransport::OnReadPacket);
|
||||
ice_transport_->SignalSentPacket.connect(this, &DtlsTransport::OnSentPacket);
|
||||
ice_transport_->SignalReadyToSend.connect(this,
|
||||
&DtlsTransport::OnReadyToSend);
|
||||
ice_transport_->SignalReceivingState.connect(
|
||||
this, &DtlsTransport::OnReceivingState);
|
||||
ice_transport_->SignalNetworkRouteChanged.connect(
|
||||
this, &DtlsTransport::OnNetworkRouteChanged);
|
||||
}
|
||||
|
||||
// The state transition logic here is as follows:
|
||||
// (1) If we're not doing DTLS-SRTP, then the state is just the
|
||||
// state of the underlying impl()
|
||||
@ -543,7 +568,7 @@ void DtlsTransport::OnReadPacket(rtc::PacketTransportInternal* transport,
|
||||
// the peer has chosen the client role, and proceed with the handshake.
|
||||
// The fingerprint will be verified when it's set.
|
||||
if (!dtls_ && local_certificate_) {
|
||||
SetSslRole(rtc::SSL_SERVER);
|
||||
SetDtlsRole(rtc::SSL_SERVER);
|
||||
SetupDtls();
|
||||
}
|
||||
} else {
|
||||
@ -677,7 +702,7 @@ void DtlsTransport::MaybeStartDtls() {
|
||||
// Now that the handshake has started, we can process a cached ClientHello
|
||||
// (if one exists).
|
||||
if (cached_client_hello_.size()) {
|
||||
if (ssl_role_ == rtc::SSL_SERVER) {
|
||||
if (*dtls_role_ == rtc::SSL_SERVER) {
|
||||
LOG_J(LS_INFO, this) << "Handling cached DTLS ClientHello packet.";
|
||||
if (!HandleDtlsPacket(cached_client_hello_.data<char>(),
|
||||
cached_client_hello_.size())) {
|
||||
|
||||
@ -89,8 +89,12 @@ class DtlsTransport : public DtlsTransportInternal {
|
||||
//
|
||||
// |crypto_options| are the options used for the DTLS handshake. This affects
|
||||
// whether GCM crypto suites are negotiated.
|
||||
// TODO(zhihuang): Remove this once we switch to JsepTransportController.
|
||||
explicit DtlsTransport(IceTransportInternal* ice_transport,
|
||||
const rtc::CryptoOptions& crypto_options);
|
||||
explicit DtlsTransport(std::unique_ptr<IceTransportInternal> ice_transport,
|
||||
const rtc::CryptoOptions& crypto_options);
|
||||
|
||||
~DtlsTransport() override;
|
||||
|
||||
const rtc::CryptoOptions& crypto_options() const override;
|
||||
@ -114,7 +118,7 @@ class DtlsTransport : public DtlsTransportInternal {
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override;
|
||||
|
||||
// SetRemoteFingerprint must be called after SetLocalCertificate, and any
|
||||
// other methods like SetSslRole. It's what triggers the actual DTLS setup.
|
||||
// other methods like SetDtlsRole. It's what triggers the actual DTLS setup.
|
||||
// TODO(deadbeef): Rename to "Start" like in ORTC?
|
||||
bool SetRemoteFingerprint(const std::string& digest_alg,
|
||||
const uint8_t* digest,
|
||||
@ -128,13 +132,13 @@ class DtlsTransport : public DtlsTransportInternal {
|
||||
|
||||
bool GetOption(rtc::Socket::Option opt, int* value) override;
|
||||
|
||||
virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version);
|
||||
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override;
|
||||
|
||||
// Find out which DTLS-SRTP cipher was negotiated
|
||||
bool GetSrtpCryptoSuite(int* cipher) override;
|
||||
|
||||
bool GetSslRole(rtc::SSLRole* role) const override;
|
||||
bool SetSslRole(rtc::SSLRole role) override;
|
||||
bool GetDtlsRole(rtc::SSLRole* role) const override;
|
||||
bool SetDtlsRole(rtc::SSLRole role) override;
|
||||
|
||||
// Find out which DTLS cipher was negotiated
|
||||
bool GetSslCipherSuite(int* cipher) override;
|
||||
@ -186,6 +190,8 @@ class DtlsTransport : public DtlsTransportInternal {
|
||||
}
|
||||
|
||||
private:
|
||||
void ConnectToIceTransport();
|
||||
|
||||
void OnWritableState(rtc::PacketTransportInternal* transport);
|
||||
void OnReadPacket(rtc::PacketTransportInternal* transport,
|
||||
const char* data,
|
||||
@ -215,13 +221,14 @@ class DtlsTransport : public DtlsTransportInternal {
|
||||
rtc::Thread* network_thread_; // Everything should occur on this thread.
|
||||
// Underlying ice_transport, not owned by this class.
|
||||
IceTransportInternal* const ice_transport_;
|
||||
std::unique_ptr<IceTransportInternal> owned_ice_transport_;
|
||||
std::unique_ptr<rtc::SSLStreamAdapter> dtls_; // The DTLS stream
|
||||
StreamInterfaceChannel*
|
||||
downward_; // Wrapper for ice_transport_, owned by dtls_.
|
||||
std::vector<int> srtp_ciphers_; // SRTP ciphers to use with DTLS.
|
||||
bool dtls_active_ = false;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate_;
|
||||
rtc::SSLRole ssl_role_;
|
||||
rtc::Optional<rtc::SSLRole> dtls_role_;
|
||||
rtc::SSLProtocolVersion ssl_max_version_;
|
||||
rtc::CryptoOptions crypto_options_;
|
||||
rtc::Buffer remote_fingerprint_value_;
|
||||
|
||||
@ -353,10 +353,10 @@ class DtlsTransportTestBase {
|
||||
void Negotiate(bool client1_server = true) {
|
||||
client1_.SetupTransports(ICEROLE_CONTROLLING);
|
||||
client2_.SetupTransports(ICEROLE_CONTROLLED);
|
||||
client1_.dtls_transport()->SetSslRole(client1_server ? rtc::SSL_SERVER
|
||||
: rtc::SSL_CLIENT);
|
||||
client2_.dtls_transport()->SetSslRole(client1_server ? rtc::SSL_CLIENT
|
||||
: rtc::SSL_SERVER);
|
||||
client1_.dtls_transport()->SetDtlsRole(client1_server ? rtc::SSL_SERVER
|
||||
: rtc::SSL_CLIENT);
|
||||
client2_.dtls_transport()->SetDtlsRole(client1_server ? rtc::SSL_CLIENT
|
||||
: rtc::SSL_SERVER);
|
||||
if (client2_.certificate()) {
|
||||
SetRemoteFingerprintFromCert(client1_.dtls_transport(),
|
||||
client2_.certificate());
|
||||
@ -627,8 +627,8 @@ class DtlsEventOrderingTest
|
||||
client1_.SetupTransports(ICEROLE_CONTROLLING, simulated_delay_ms);
|
||||
client2_.SetupTransports(ICEROLE_CONTROLLED, simulated_delay_ms);
|
||||
// Similar to how NegotiateOrdering works.
|
||||
client1_.dtls_transport()->SetSslRole(rtc::SSL_SERVER);
|
||||
client2_.dtls_transport()->SetSslRole(rtc::SSL_CLIENT);
|
||||
client1_.dtls_transport()->SetDtlsRole(rtc::SSL_SERVER);
|
||||
client2_.dtls_transport()->SetDtlsRole(rtc::SSL_CLIENT);
|
||||
SetRemoteFingerprintFromCert(client2_.dtls_transport(),
|
||||
client1_.certificate());
|
||||
|
||||
|
||||
@ -59,9 +59,9 @@ class DtlsTransportInternal : public rtc::PacketTransportInternal {
|
||||
|
||||
virtual bool IsDtlsActive() const = 0;
|
||||
|
||||
virtual bool GetSslRole(rtc::SSLRole* role) const = 0;
|
||||
virtual bool GetDtlsRole(rtc::SSLRole* role) const = 0;
|
||||
|
||||
virtual bool SetSslRole(rtc::SSLRole role) = 0;
|
||||
virtual bool SetDtlsRole(rtc::SSLRole role) = 0;
|
||||
|
||||
// Finds out which DTLS-SRTP cipher was negotiated.
|
||||
// TODO(zhihuang): Remove this once all dependencies implement this.
|
||||
@ -98,6 +98,8 @@ class DtlsTransportInternal : public rtc::PacketTransportInternal {
|
||||
const uint8_t* digest,
|
||||
size_t digest_len) = 0;
|
||||
|
||||
virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) = 0;
|
||||
|
||||
// Expose the underneath IceTransport.
|
||||
virtual IceTransportInternal* ice_transport() = 0;
|
||||
|
||||
|
||||
@ -13,11 +13,13 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "p2p/base/dtlstransportinternal.h"
|
||||
#include "p2p/base/fakeicetransport.h"
|
||||
#include "rtc_base/fakesslidentity.h"
|
||||
#include "rtc_base/ptr_util.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
@ -38,10 +40,8 @@ class FakeDtlsTransport : public DtlsTransportInternal {
|
||||
this, &FakeDtlsTransport::OnNetworkRouteChanged);
|
||||
}
|
||||
|
||||
// If this constructor is called, a new fake ICE transport will be created,
|
||||
// and this FakeDtlsTransport will take the ownership.
|
||||
explicit FakeDtlsTransport(const std::string& name, int component)
|
||||
: owned_ice_transport_(new FakeIceTransport(name, component)),
|
||||
explicit FakeDtlsTransport(std::unique_ptr<FakeIceTransport> ice)
|
||||
: owned_ice_transport_(std::move(ice)),
|
||||
transport_name_(owned_ice_transport_->transport_name()),
|
||||
component_(owned_ice_transport_->component()),
|
||||
dtls_fingerprint_("", nullptr, 0) {
|
||||
@ -52,6 +52,11 @@ class FakeDtlsTransport : public DtlsTransportInternal {
|
||||
this, &FakeDtlsTransport::OnNetworkRouteChanged);
|
||||
}
|
||||
|
||||
// If this constructor is called, a new fake ICE transport will be created,
|
||||
// and this FakeDtlsTransport will take the ownership.
|
||||
explicit FakeDtlsTransport(const std::string& name, int component)
|
||||
: FakeDtlsTransport(rtc::MakeUnique<FakeIceTransport>(name, component)) {}
|
||||
|
||||
~FakeDtlsTransport() override {
|
||||
if (dest_ && dest_->dest_ == this) {
|
||||
dest_->dest_ = nullptr;
|
||||
@ -101,6 +106,10 @@ class FakeDtlsTransport : public DtlsTransportInternal {
|
||||
dest->SetDestination(this, true);
|
||||
}
|
||||
dtls_state_ = DTLS_TRANSPORT_CONNECTED;
|
||||
// If the |dtls_role_| is unset, set it to SSL_CLIENT by default.
|
||||
if (!dtls_role_) {
|
||||
dtls_role_ = std::move(rtc::SSL_CLIENT);
|
||||
}
|
||||
SignalDtlsState(this, dtls_state_);
|
||||
ice_transport_->SetDestination(
|
||||
static_cast<FakeIceTransport*>(dest->ice_transport()), asymmetric);
|
||||
@ -125,12 +134,18 @@ class FakeDtlsTransport : public DtlsTransportInternal {
|
||||
dtls_fingerprint_ = rtc::SSLFingerprint(alg, digest, digest_len);
|
||||
return true;
|
||||
}
|
||||
bool SetSslRole(rtc::SSLRole role) override {
|
||||
ssl_role_ = role;
|
||||
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override {
|
||||
return true;
|
||||
}
|
||||
bool GetSslRole(rtc::SSLRole* role) const override {
|
||||
*role = ssl_role_;
|
||||
bool SetDtlsRole(rtc::SSLRole role) override {
|
||||
dtls_role_ = std::move(role);
|
||||
return true;
|
||||
}
|
||||
bool GetDtlsRole(rtc::SSLRole* role) const override {
|
||||
if (!dtls_role_) {
|
||||
return false;
|
||||
}
|
||||
*role = *dtls_role_;
|
||||
return true;
|
||||
}
|
||||
const rtc::CryptoOptions& crypto_options() const override {
|
||||
@ -261,7 +276,7 @@ class FakeDtlsTransport : public DtlsTransportInternal {
|
||||
bool do_dtls_ = false;
|
||||
rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12;
|
||||
rtc::SSLFingerprint dtls_fingerprint_;
|
||||
rtc::SSLRole ssl_role_ = rtc::SSL_CLIENT;
|
||||
rtc::Optional<rtc::SSLRole> dtls_role_;
|
||||
int crypto_suite_ = rtc::SRTP_AES128_CM_SHA1_80;
|
||||
rtc::CryptoOptions crypto_options_;
|
||||
|
||||
|
||||
@ -144,7 +144,16 @@ class FakeIceTransport : public IceTransportInternal {
|
||||
void AddRemoteCandidate(const Candidate& candidate) override {
|
||||
remote_candidates_.push_back(candidate);
|
||||
}
|
||||
void RemoveRemoteCandidate(const Candidate& candidate) override {}
|
||||
void RemoveRemoteCandidate(const Candidate& candidate) override {
|
||||
auto it = std::find(remote_candidates_.begin(), remote_candidates_.end(),
|
||||
candidate);
|
||||
if (it == remote_candidates_.end()) {
|
||||
RTC_LOG(LS_INFO) << "Trying to remove a candidate which doesn't exist.";
|
||||
return;
|
||||
}
|
||||
|
||||
remote_candidates_.erase(it);
|
||||
}
|
||||
|
||||
bool GetStats(ConnectionInfos* candidate_pair_stats_list,
|
||||
CandidateStatsList* candidate_stats_list) override {
|
||||
|
||||
42
p2p/base/transportfactoryinterface.h
Normal file
42
p2p/base/transportfactoryinterface.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_TRANSPORTFACTORYINTERFACE_H_
|
||||
#define P2P_BASE_TRANSPORTFACTORYINTERFACE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "p2p/base/dtlstransportinternal.h"
|
||||
#include "p2p/base/icetransportinternal.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// This interface is used to create DTLS/ICE transports. The external transports
|
||||
// can be injected into the JsepTransportController through it. For example, the
|
||||
// FakeIceTransport/FakeDtlsTransport can be injected by setting a
|
||||
// FakeTransportFactory which implements this interface to the
|
||||
// JsepTransportController.
|
||||
class TransportFactoryInterface {
|
||||
public:
|
||||
virtual ~TransportFactoryInterface() {}
|
||||
|
||||
virtual std::unique_ptr<IceTransportInternal> CreateIceTransport(
|
||||
const std::string& transport_name,
|
||||
int component) = 0;
|
||||
|
||||
virtual std::unique_ptr<DtlsTransportInternal> CreateDtlsTransport(
|
||||
std::unique_ptr<IceTransportInternal> ice,
|
||||
const rtc::CryptoOptions& crypto_options) = 0;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_TRANSPORTFACTORYINTERFACE_H_
|
||||
@ -44,6 +44,10 @@ rtc_static_library("rtc_pc_base") {
|
||||
"externalhmac.h",
|
||||
"jseptransport.cc",
|
||||
"jseptransport.h",
|
||||
"jseptransport2.cc",
|
||||
"jseptransport2.h",
|
||||
"jseptransportcontroller.cc",
|
||||
"jseptransportcontroller.h",
|
||||
"mediasession.cc",
|
||||
"mediasession.h",
|
||||
"rtcpmuxfilter.cc",
|
||||
@ -64,6 +68,8 @@ rtc_static_library("rtc_pc_base") {
|
||||
"srtptransport.h",
|
||||
"transportcontroller.cc",
|
||||
"transportcontroller.h",
|
||||
"transportstats.cc",
|
||||
"transportstats.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
@ -270,7 +276,9 @@ if (rtc_include_tests) {
|
||||
"channelmanager_unittest.cc",
|
||||
"currentspeakermonitor_unittest.cc",
|
||||
"dtlssrtptransport_unittest.cc",
|
||||
"jseptransport2_unittest.cc",
|
||||
"jseptransport_unittest.cc",
|
||||
"jseptransportcontroller_unittest.cc",
|
||||
"mediasession_unittest.cc",
|
||||
"rtcpmuxfilter_unittest.cc",
|
||||
"rtptransport_unittest.cc",
|
||||
|
||||
@ -263,8 +263,8 @@ bool DtlsSrtpTransport::ExtractParams(
|
||||
memcpy(&server_write_key[key_len], &dtls_buffer[offset], salt_len);
|
||||
|
||||
rtc::SSLRole role;
|
||||
if (!dtls_transport->GetSslRole(&role)) {
|
||||
RTC_LOG(LS_WARNING) << "GetSslRole failed";
|
||||
if (!dtls_transport->GetDtlsRole(&role)) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to get the DTLS role.";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -25,18 +25,8 @@ using webrtc::SdpType;
|
||||
|
||||
namespace cricket {
|
||||
|
||||
TransportChannelStats::TransportChannelStats() = default;
|
||||
|
||||
TransportChannelStats::TransportChannelStats(const TransportChannelStats&) =
|
||||
default;
|
||||
|
||||
TransportChannelStats::~TransportChannelStats() = default;
|
||||
|
||||
TransportStats::TransportStats() = default;
|
||||
|
||||
TransportStats::~TransportStats() = default;
|
||||
|
||||
bool BadTransportDescription(const std::string& desc, std::string* err_desc) {
|
||||
static bool BadTransportDescription(const std::string& desc,
|
||||
std::string* err_desc) {
|
||||
if (err_desc) {
|
||||
*err_desc = desc;
|
||||
}
|
||||
@ -289,7 +279,7 @@ bool JsepTransport::ApplyNegotiatedTransportDescription(
|
||||
std::string* error_desc) {
|
||||
// Set SSL role. Role must be set before fingerprint is applied, which
|
||||
// initiates DTLS setup.
|
||||
if (ssl_role_ && !dtls_transport->SetSslRole(*ssl_role_)) {
|
||||
if (ssl_role_ && !dtls_transport->SetDtlsRole(*ssl_role_)) {
|
||||
return BadTransportDescription("Failed to set SSL role for the channel.",
|
||||
error_desc);
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#include "p2p/base/p2pconstants.h"
|
||||
#include "p2p/base/transportinfo.h"
|
||||
#include "pc/sessiondescription.h"
|
||||
#include "pc/transportstats.h"
|
||||
#include "rtc_base/constructormagic.h"
|
||||
#include "rtc_base/messagequeue.h"
|
||||
#include "rtc_base/rtccertificate.h"
|
||||
@ -33,34 +34,6 @@ namespace cricket {
|
||||
|
||||
class DtlsTransportInternal;
|
||||
|
||||
struct TransportChannelStats {
|
||||
TransportChannelStats();
|
||||
TransportChannelStats(const TransportChannelStats&);
|
||||
~TransportChannelStats();
|
||||
|
||||
int component = 0;
|
||||
CandidateStatsList candidate_stats_list;
|
||||
ConnectionInfos connection_infos;
|
||||
int srtp_crypto_suite = rtc::SRTP_INVALID_CRYPTO_SUITE;
|
||||
int ssl_cipher_suite = rtc::TLS_NULL_WITH_NULL_NULL;
|
||||
DtlsTransportState dtls_state = DTLS_TRANSPORT_NEW;
|
||||
};
|
||||
|
||||
// Information about all the channels of a transport.
|
||||
// TODO(hta): Consider if a simple vector is as good as a map.
|
||||
typedef std::vector<TransportChannelStats> TransportChannelStatsList;
|
||||
|
||||
// Information about the stats of a transport.
|
||||
struct TransportStats {
|
||||
TransportStats();
|
||||
~TransportStats();
|
||||
|
||||
std::string transport_name;
|
||||
TransportChannelStatsList channel_stats;
|
||||
};
|
||||
|
||||
bool BadTransportDescription(const std::string& desc, std::string* err_desc);
|
||||
|
||||
// Helper class used by TransportController that processes
|
||||
// TransportDescriptions. A TransportDescription represents the
|
||||
// transport-specific properties of an SDP m= section, processed according to
|
||||
|
||||
617
pc/jseptransport2.cc
Normal file
617
pc/jseptransport2.cc
Normal file
@ -0,0 +1,617 @@
|
||||
/*
|
||||
* 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/jseptransport2.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility> // for std::pair
|
||||
|
||||
#include "api/candidate.h"
|
||||
#include "p2p/base/p2pconstants.h"
|
||||
#include "p2p/base/p2ptransportchannel.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "rtc_base/bind.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/ptr_util.h"
|
||||
|
||||
using webrtc::SdpType;
|
||||
|
||||
namespace cricket {
|
||||
|
||||
static bool VerifyIceParams(const JsepTransportDescription& 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;
|
||||
}
|
||||
|
||||
JsepTransportDescription::JsepTransportDescription() {}
|
||||
|
||||
JsepTransportDescription::JsepTransportDescription(
|
||||
bool rtcp_mux_enabled,
|
||||
const std::vector<CryptoParams>& cryptos,
|
||||
const std::vector<int>& encrypted_header_extension_ids,
|
||||
const TransportDescription& transport_desc)
|
||||
: rtcp_mux_enabled(rtcp_mux_enabled),
|
||||
cryptos(cryptos),
|
||||
encrypted_header_extension_ids(encrypted_header_extension_ids),
|
||||
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),
|
||||
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;
|
||||
transport_desc = from.transport_desc;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsepTransport2::JsepTransport2(
|
||||
const std::string& mid,
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& local_certificate,
|
||||
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)
|
||||
: mid_(mid),
|
||||
local_certificate_(local_certificate),
|
||||
rtp_dtls_transport_(std::move(rtp_dtls_transport)),
|
||||
rtcp_dtls_transport_(std::move(rtcp_dtls_transport)) {
|
||||
RTC_DCHECK(rtp_dtls_transport_);
|
||||
if (unencrypted_rtp_transport) {
|
||||
RTC_DCHECK(!sdes_transport);
|
||||
RTC_DCHECK(!dtls_srtp_transport);
|
||||
unencrypted_rtp_transport_ = std::move(unencrypted_rtp_transport);
|
||||
} else if (sdes_transport) {
|
||||
RTC_DCHECK(!unencrypted_rtp_transport);
|
||||
RTC_DCHECK(!dtls_srtp_transport);
|
||||
sdes_transport_ = std::move(sdes_transport);
|
||||
} else {
|
||||
RTC_DCHECK(dtls_srtp_transport);
|
||||
RTC_DCHECK(!unencrypted_rtp_transport);
|
||||
RTC_DCHECK(!sdes_transport);
|
||||
dtls_srtp_transport_ = std::move(dtls_srtp_transport);
|
||||
}
|
||||
}
|
||||
|
||||
JsepTransport2::~JsepTransport2() {}
|
||||
|
||||
webrtc::RTCError JsepTransport2::SetLocalJsepTransportDescription(
|
||||
const JsepTransportDescription& jsep_description,
|
||||
SdpType type) {
|
||||
webrtc::RTCError error;
|
||||
|
||||
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.
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
SetLocalIceParameters(rtp_dtls_transport_->ice_transport());
|
||||
|
||||
if (rtcp_dtls_transport_) {
|
||||
SetLocalIceParameters(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(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 JsepTransport2::SetRemoteJsepTransportDescription(
|
||||
const JsepTransportDescription& jsep_description,
|
||||
webrtc::SdpType type) {
|
||||
webrtc::RTCError error;
|
||||
|
||||
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.
|
||||
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.");
|
||||
}
|
||||
} else if (dtls_srtp_transport_) {
|
||||
RTC_DCHECK(!unencrypted_rtp_transport_);
|
||||
RTC_DCHECK(!sdes_transport_);
|
||||
dtls_srtp_transport_->UpdateSendEncryptedHeaderExtensionIds(
|
||||
jsep_description.encrypted_header_extension_ids);
|
||||
}
|
||||
|
||||
remote_description_.reset(new JsepTransportDescription(jsep_description));
|
||||
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);
|
||||
}
|
||||
if (!error.ok()) {
|
||||
remote_description_.reset();
|
||||
return error;
|
||||
}
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
|
||||
webrtc::RTCError JsepTransport2::AddRemoteCandidates(
|
||||
const Candidates& candidates) {
|
||||
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_.get()
|
||||
: rtcp_dtls_transport_.get();
|
||||
if (!transport) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Candidate has an unknown component: " +
|
||||
candidate.ToString() + " for mid " + mid());
|
||||
}
|
||||
transport->ice_transport()->AddRemoteCandidate(candidate);
|
||||
}
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
|
||||
void JsepTransport2::SetNeedsIceRestartFlag() {
|
||||
if (!needs_ice_restart_) {
|
||||
needs_ice_restart_ = true;
|
||||
RTC_LOG(LS_VERBOSE) << "needs-ice-restart flag set for transport " << mid();
|
||||
}
|
||||
}
|
||||
|
||||
rtc::Optional<rtc::SSLRole> JsepTransport2::GetDtlsRole() const {
|
||||
RTC_DCHECK(rtp_dtls_transport_);
|
||||
rtc::SSLRole dtls_role;
|
||||
if (!rtp_dtls_transport_->GetDtlsRole(&dtls_role)) {
|
||||
return rtc::Optional<rtc::SSLRole>();
|
||||
}
|
||||
|
||||
return rtc::Optional<rtc::SSLRole>(dtls_role);
|
||||
}
|
||||
|
||||
bool JsepTransport2::GetStats(TransportStats* stats) {
|
||||
stats->transport_name = mid();
|
||||
stats->channel_stats.clear();
|
||||
bool ret = GetTransportStats(rtp_dtls_transport_.get(), stats);
|
||||
if (rtcp_dtls_transport_) {
|
||||
ret &= GetTransportStats(rtcp_dtls_transport_.get(), stats);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
webrtc::RTCError JsepTransport2::VerifyCertificateFingerprint(
|
||||
const rtc::RTCCertificate* certificate,
|
||||
const rtc::SSLFingerprint* fingerprint) const {
|
||||
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::Create(
|
||||
fingerprint->algorithm, certificate->identity()));
|
||||
RTC_DCHECK(fp_tmp.get() != NULL);
|
||||
if (*fp_tmp == *fingerprint) {
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
std::ostringstream desc;
|
||||
desc << "Local fingerprint does not match identity. Expected: ";
|
||||
desc << fp_tmp->ToString();
|
||||
desc << " Got: " << fingerprint->ToString();
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, desc.str());
|
||||
}
|
||||
|
||||
void JsepTransport2::SetLocalIceParameters(
|
||||
IceTransportInternal* ice_transport) {
|
||||
RTC_DCHECK(ice_transport);
|
||||
RTC_DCHECK(local_description_);
|
||||
ice_transport->SetIceParameters(
|
||||
local_description_->transport_desc.GetIceParameters());
|
||||
}
|
||||
|
||||
void JsepTransport2::SetRemoteIceParameters(
|
||||
IceTransportInternal* ice_transport) {
|
||||
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 JsepTransport2::SetNegotiatedDtlsParameters(
|
||||
DtlsTransportInternal* dtls_transport,
|
||||
rtc::Optional<rtc::SSLRole> dtls_role,
|
||||
rtc::SSLFingerprint* remote_fingerprint) {
|
||||
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,
|
||||
reinterpret_cast<const uint8_t*>(remote_fingerprint->digest.data()),
|
||||
remote_fingerprint->digest.size())) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to apply remote fingerprint.");
|
||||
}
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
|
||||
bool JsepTransport2::SetRtcpMux(bool enable,
|
||||
webrtc::SdpType type,
|
||||
ContentSource source) {
|
||||
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 JsepTransport2::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 {
|
||||
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_.reset();
|
||||
// Notify the JsepTransportController to update the aggregate states.
|
||||
SignalRtcpMuxActive();
|
||||
}
|
||||
|
||||
bool JsepTransport2::SetSdes(const std::vector<CryptoParams>& cryptos,
|
||||
const std::vector<int>& encrypted_extension_ids,
|
||||
webrtc::SdpType type,
|
||||
ContentSource source) {
|
||||
bool ret = false;
|
||||
ret = sdes_negotiator_.Process(cryptos, type, source);
|
||||
if (!ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (source == ContentSource::CS_LOCAL) {
|
||||
recv_extension_ids_ = std::move(encrypted_extension_ids);
|
||||
} else {
|
||||
send_extension_ids_ = std::move(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 JsepTransport2::NegotiateAndSetDtlsParameters(
|
||||
SdpType local_description_type) {
|
||||
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;
|
||||
rtc::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 = rtc::MakeUnique<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 = rtc::MakeUnique<rtc::SSLFingerprint>("", nullptr, 0);
|
||||
}
|
||||
// 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.
|
||||
webrtc::RTCError error = SetNegotiatedDtlsParameters(
|
||||
rtp_dtls_transport_.get(), negotiated_dtls_role,
|
||||
remote_fingerprint.get());
|
||||
if (!error.ok()) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (rtcp_dtls_transport_) {
|
||||
error = SetNegotiatedDtlsParameters(rtcp_dtls_transport_.get(),
|
||||
negotiated_dtls_role,
|
||||
remote_fingerprint.get());
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
webrtc::RTCError JsepTransport2::NegotiateDtlsRole(
|
||||
SdpType local_description_type,
|
||||
ConnectionRole local_connection_role,
|
||||
ConnectionRole remote_connection_role,
|
||||
rtc::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.
|
||||
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 ? std::move(rtc::SSL_CLIENT)
|
||||
: std::move(rtc::SSL_SERVER));
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
|
||||
bool JsepTransport2::GetTransportStats(DtlsTransportInternal* dtls_transport,
|
||||
TransportStats* stats) {
|
||||
RTC_DCHECK(dtls_transport);
|
||||
TransportChannelStats substats;
|
||||
substats.component = dtls_transport == rtcp_dtls_transport_.get()
|
||||
? ICE_CANDIDATE_COMPONENT_RTCP
|
||||
: ICE_CANDIDATE_COMPONENT_RTP;
|
||||
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.connection_infos, &substats.candidate_stats_list)) {
|
||||
return false;
|
||||
}
|
||||
stats->channel_stats.push_back(substats);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
245
pc/jseptransport2.h
Normal file
245
pc/jseptransport2.h
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_JSEPTRANSPORT2_H_
|
||||
#define PC_JSEPTRANSPORT2_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/candidate.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/optional.h"
|
||||
#include "p2p/base/dtlstransport.h"
|
||||
#include "p2p/base/p2pconstants.h"
|
||||
#include "p2p/base/transportinfo.h"
|
||||
#include "pc/dtlssrtptransport.h"
|
||||
#include "pc/rtcpmuxfilter.h"
|
||||
#include "pc/rtptransport.h"
|
||||
#include "pc/sessiondescription.h"
|
||||
#include "pc/srtpfilter.h"
|
||||
#include "pc/srtptransport.h"
|
||||
#include "pc/transportstats.h"
|
||||
#include "rtc_base/constructormagic.h"
|
||||
#include "rtc_base/messagequeue.h"
|
||||
#include "rtc_base/rtccertificate.h"
|
||||
#include "rtc_base/sigslot.h"
|
||||
#include "rtc_base/sslstreamadapter.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class DtlsTransportInternal;
|
||||
|
||||
struct JsepTransportDescription {
|
||||
public:
|
||||
JsepTransportDescription();
|
||||
JsepTransportDescription(
|
||||
bool rtcp_mux_enabled,
|
||||
const std::vector<CryptoParams>& cryptos,
|
||||
const std::vector<int>& encrypted_header_extension_ids,
|
||||
const TransportDescription& transport_description);
|
||||
JsepTransportDescription(const JsepTransportDescription& from);
|
||||
~JsepTransportDescription();
|
||||
|
||||
JsepTransportDescription& operator=(const JsepTransportDescription& from);
|
||||
|
||||
bool rtcp_mux_enabled = true;
|
||||
std::vector<CryptoParams> cryptos;
|
||||
std::vector<int> encrypted_header_extension_ids;
|
||||
// TODO(zhihuang): Add the ICE and DTLS related variables and methods from
|
||||
// TransportDescription and remove this extra layer of abstraction.
|
||||
TransportDescription transport_desc;
|
||||
};
|
||||
|
||||
// Helper class used by JsepTransportController that processes
|
||||
// TransportDescriptions. A TransportDescription represents the
|
||||
// transport-specific properties of an SDP m= section, processed according to
|
||||
// JSEP. Each transport consists of DTLS and ICE transport channels for RTP
|
||||
// (and possibly RTCP, if rtcp-mux isn't used).
|
||||
//
|
||||
// On Threading: JsepTransport performs work solely on the network thread, and
|
||||
// so its methods should only be called on the network thread.
|
||||
class JsepTransport2 : public sigslot::has_slots<> {
|
||||
public:
|
||||
// |mid| is just used for log statements in order to identify the Transport.
|
||||
// Note that |local_certificate| is allowed to be null since a remote
|
||||
// description may be set before a local certificate is generated.
|
||||
JsepTransport2(
|
||||
const std::string& mid,
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& local_certificate,
|
||||
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);
|
||||
|
||||
~JsepTransport2() override;
|
||||
|
||||
// Returns the MID of this transport. This is only used for logging.
|
||||
const std::string& mid() const { return mid_; }
|
||||
|
||||
// Must be called before applying local session description.
|
||||
// Needed in order to verify the local fingerprint.
|
||||
void SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& local_certificate) {
|
||||
local_certificate_ = local_certificate;
|
||||
}
|
||||
|
||||
// Return the local certificate provided by SetLocalCertificate.
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const {
|
||||
return local_certificate_;
|
||||
}
|
||||
|
||||
webrtc::RTCError SetLocalJsepTransportDescription(
|
||||
const JsepTransportDescription& jsep_description,
|
||||
webrtc::SdpType type);
|
||||
|
||||
// Set the remote TransportDescription to be used by DTLS and ICE channels
|
||||
// that are part of this Transport.
|
||||
webrtc::RTCError SetRemoteJsepTransportDescription(
|
||||
const JsepTransportDescription& jsep_description,
|
||||
webrtc::SdpType type);
|
||||
|
||||
webrtc::RTCError AddRemoteCandidates(const Candidates& candidates);
|
||||
|
||||
// Set the "needs-ice-restart" flag as described in JSEP. After the flag is
|
||||
// set, offers should generate new ufrags/passwords until an ICE restart
|
||||
// occurs.
|
||||
//
|
||||
// This and the below method can be called safely from any thread as long as
|
||||
// SetXTransportDescription is not in progress.
|
||||
void SetNeedsIceRestartFlag();
|
||||
// Returns true if the ICE restart flag above was set, and no ICE restart has
|
||||
// occurred yet for this transport (by applying a local description with
|
||||
// changed ufrag/password).
|
||||
bool needs_ice_restart() const { return needs_ice_restart_; }
|
||||
|
||||
// Returns role if negotiated, or empty Optional if it hasn't been negotiated
|
||||
// yet.
|
||||
rtc::Optional<rtc::SSLRole> GetDtlsRole() const;
|
||||
|
||||
// TODO(deadbeef): Make this const. See comment in transportcontroller.h.
|
||||
bool GetStats(TransportStats* stats);
|
||||
|
||||
const JsepTransportDescription* local_description() const {
|
||||
return local_description_.get();
|
||||
}
|
||||
|
||||
const JsepTransportDescription* remote_description() const {
|
||||
return remote_description_.get();
|
||||
}
|
||||
|
||||
webrtc::RtpTransportInternal* rtp_transport() const {
|
||||
if (dtls_srtp_transport_) {
|
||||
return dtls_srtp_transport_.get();
|
||||
} else if (sdes_transport_) {
|
||||
return sdes_transport_.get();
|
||||
} else {
|
||||
return unencrypted_rtp_transport_.get();
|
||||
}
|
||||
}
|
||||
|
||||
DtlsTransportInternal* rtp_dtls_transport() const {
|
||||
return rtp_dtls_transport_.get();
|
||||
}
|
||||
|
||||
DtlsTransportInternal* rtcp_dtls_transport() const {
|
||||
return rtcp_dtls_transport_.get();
|
||||
}
|
||||
|
||||
// This is signaled when RTCP-mux becomes active and
|
||||
// |rtcp_dtls_transport_| is destroyed. The JsepTransportController will
|
||||
// handle the signal and update the aggregate transport states.
|
||||
sigslot::signal<> SignalRtcpMuxActive;
|
||||
|
||||
// TODO(deadbeef): The methods below are only public for testing. Should make
|
||||
// them utility functions or objects so they can be tested independently from
|
||||
// this class.
|
||||
|
||||
// Returns an error if the certificate's identity does not match the
|
||||
// fingerprint, or either is NULL.
|
||||
webrtc::RTCError VerifyCertificateFingerprint(
|
||||
const rtc::RTCCertificate* certificate,
|
||||
const rtc::SSLFingerprint* fingerprint) const;
|
||||
|
||||
private:
|
||||
bool SetRtcpMux(bool enable, webrtc::SdpType type, ContentSource source);
|
||||
|
||||
void ActivateRtcpMux();
|
||||
|
||||
bool SetSdes(const std::vector<CryptoParams>& cryptos,
|
||||
const std::vector<int>& encrypted_extension_ids,
|
||||
webrtc::SdpType type,
|
||||
ContentSource source);
|
||||
|
||||
// Negotiates and sets the DTLS parameters based on the current local and
|
||||
// remote transport description, such as the DTLS role to use, and whether
|
||||
// DTLS should be activated.
|
||||
//
|
||||
// Called when an answer TransportDescription is applied.
|
||||
webrtc::RTCError NegotiateAndSetDtlsParameters(
|
||||
webrtc::SdpType local_description_type);
|
||||
|
||||
// Negotiates the DTLS role based off the offer and answer as specified by
|
||||
// RFC 4145, section-4.1. Returns an RTCError if role cannot be determined
|
||||
// from the local description and remote description.
|
||||
webrtc::RTCError NegotiateDtlsRole(
|
||||
webrtc::SdpType local_description_type,
|
||||
ConnectionRole local_connection_role,
|
||||
ConnectionRole remote_connection_role,
|
||||
rtc::Optional<rtc::SSLRole>* negotiated_dtls_role);
|
||||
|
||||
// Pushes down the ICE parameters from the local description, such
|
||||
// as the ICE ufrag and pwd.
|
||||
void SetLocalIceParameters(IceTransportInternal* ice);
|
||||
|
||||
// Pushes down the ICE parameters from the remote description.
|
||||
void SetRemoteIceParameters(IceTransportInternal* ice);
|
||||
|
||||
// Pushes down the DTLS parameters obtained via negotiation.
|
||||
webrtc::RTCError SetNegotiatedDtlsParameters(
|
||||
DtlsTransportInternal* dtls_transport,
|
||||
rtc::Optional<rtc::SSLRole> dtls_role,
|
||||
rtc::SSLFingerprint* remote_fingerprint);
|
||||
|
||||
bool GetTransportStats(DtlsTransportInternal* dtls_transport,
|
||||
TransportStats* stats);
|
||||
|
||||
const std::string mid_;
|
||||
// needs-ice-restart bit as described in JSEP.
|
||||
bool needs_ice_restart_ = false;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate_;
|
||||
std::unique_ptr<JsepTransportDescription> local_description_;
|
||||
std::unique_ptr<JsepTransportDescription> remote_description_;
|
||||
|
||||
// To avoid downcasting and make it type safe, keep three unique pointers for
|
||||
// different SRTP mode and only one of these is non-nullptr.
|
||||
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_;
|
||||
|
||||
SrtpFilter sdes_negotiator_;
|
||||
RtcpMuxFilter rtcp_mux_negotiator_;
|
||||
|
||||
// Cache the encrypted header extension IDs for SDES negoitation.
|
||||
rtc::Optional<std::vector<int>> send_extension_ids_;
|
||||
rtc::Optional<std::vector<int>> recv_extension_ids_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(JsepTransport2);
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // PC_JSEPTRANSPORT2_H_
|
||||
1000
pc/jseptransport2_unittest.cc
Normal file
1000
pc/jseptransport2_unittest.cc
Normal file
File diff suppressed because it is too large
Load Diff
@ -119,7 +119,7 @@ TEST_F(JsepTransportTest, TestDtlsTransportParameters) {
|
||||
// Verify that SSL role and remote fingerprint were set correctly based on
|
||||
// transport descriptions.
|
||||
rtc::SSLRole role;
|
||||
EXPECT_TRUE(fake_dtls_transports_[0]->GetSslRole(&role));
|
||||
EXPECT_TRUE(fake_dtls_transports_[0]->GetDtlsRole(&role));
|
||||
EXPECT_EQ(rtc::SSL_SERVER, role); // Because remote description was "active".
|
||||
EXPECT_EQ(remote_desc.identity_fingerprint->ToString(),
|
||||
fake_dtls_transports_[0]->dtls_fingerprint().ToString());
|
||||
@ -152,7 +152,7 @@ TEST_F(JsepTransportTest, TestDtlsTransportParametersWithPassiveAnswer) {
|
||||
// Verify that SSL role and remote fingerprint were set correctly based on
|
||||
// transport descriptions.
|
||||
rtc::SSLRole role;
|
||||
EXPECT_TRUE(fake_dtls_transports_[0]->GetSslRole(&role));
|
||||
EXPECT_TRUE(fake_dtls_transports_[0]->GetDtlsRole(&role));
|
||||
EXPECT_EQ(rtc::SSL_CLIENT,
|
||||
role); // Because remote description was "passive".
|
||||
EXPECT_EQ(remote_desc.identity_fingerprint->ToString(),
|
||||
@ -194,7 +194,7 @@ TEST_F(JsepTransportTest, TestTransportParametersAppliedToTwoComponents) {
|
||||
EXPECT_EQ(kIcePwd2, fake_ice_transports_[i]->remote_ice_pwd());
|
||||
// Verify parameters of DTLS transports.
|
||||
rtc::SSLRole role;
|
||||
EXPECT_TRUE(fake_dtls_transports_[i]->GetSslRole(&role));
|
||||
EXPECT_TRUE(fake_dtls_transports_[i]->GetDtlsRole(&role));
|
||||
EXPECT_EQ(rtc::SSL_SERVER,
|
||||
role); // Because remote description was "active".
|
||||
EXPECT_EQ(remote_desc.identity_fingerprint->ToString(),
|
||||
|
||||
1024
pc/jseptransportcontroller.cc
Normal file
1024
pc/jseptransportcontroller.cc
Normal file
File diff suppressed because it is too large
Load Diff
295
pc/jseptransportcontroller.h
Normal file
295
pc/jseptransportcontroller.h
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright 2017 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_JSEPTRANSPORTCONTROLLER_H_
|
||||
#define PC_JSEPTRANSPORTCONTROLLER_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/candidate.h"
|
||||
#include "api/peerconnectioninterface.h"
|
||||
#include "media/sctp/sctptransportinternal.h"
|
||||
#include "p2p/base/dtlstransport.h"
|
||||
#include "p2p/base/p2ptransportchannel.h"
|
||||
#include "p2p/base/transportfactoryinterface.h"
|
||||
#include "pc/channel.h"
|
||||
#include "pc/dtlssrtptransport.h"
|
||||
#include "pc/jseptransport2.h"
|
||||
#include "pc/rtptransport.h"
|
||||
#include "pc/srtptransport.h"
|
||||
#include "rtc_base/asyncinvoker.h"
|
||||
#include "rtc_base/constructormagic.h"
|
||||
#include "rtc_base/refcountedobject.h"
|
||||
#include "rtc_base/sigslot.h"
|
||||
#include "rtc_base/sslstreamadapter.h"
|
||||
|
||||
namespace rtc {
|
||||
class Thread;
|
||||
class PacketTransportInternal;
|
||||
} // namespace rtc
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class JsepTransportController : public sigslot::has_slots<>,
|
||||
public rtc::MessageHandler {
|
||||
public:
|
||||
struct Config {
|
||||
// If |redetermine_role_on_ice_restart| is true, ICE role is redetermined
|
||||
// upon setting a local transport description that indicates an ICE
|
||||
// restart.
|
||||
bool redetermine_role_on_ice_restart = true;
|
||||
rtc::SSLProtocolVersion ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12;
|
||||
// |crypto_options| is used to determine if created DTLS transports
|
||||
// negotiate GCM crypto suites or not.
|
||||
rtc::CryptoOptions crypto_options;
|
||||
PeerConnectionInterface::BundlePolicy bundle_policy =
|
||||
PeerConnectionInterface::kBundlePolicyBalanced;
|
||||
PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy =
|
||||
PeerConnectionInterface::kRtcpMuxPolicyRequire;
|
||||
bool disable_encryption = false;
|
||||
bool enable_external_auth = false;
|
||||
// Used to inject the ICE/DTLS transports created externally.
|
||||
cricket::TransportFactoryInterface* external_transport_factory = nullptr;
|
||||
};
|
||||
|
||||
// The ICE related events are signaled on the |signaling_thread|.
|
||||
// All the transport related methods are called on the |network_thread|.
|
||||
JsepTransportController(rtc::Thread* signaling_thread,
|
||||
rtc::Thread* network_thread,
|
||||
cricket::PortAllocator* port_allocator,
|
||||
Config config);
|
||||
virtual ~JsepTransportController();
|
||||
|
||||
// The main method to be called; applies a description at the transport
|
||||
// level, creating/destroying transport objects as needed and updating their
|
||||
// properties. This includes RTP, DTLS, and ICE (but not SCTP). At least not
|
||||
// yet? May make sense to in the future.
|
||||
RTCError SetLocalDescription(SdpType type,
|
||||
const cricket::SessionDescription* description);
|
||||
|
||||
RTCError SetRemoteDescription(SdpType type,
|
||||
const cricket::SessionDescription* description);
|
||||
|
||||
// Get transports to be used for the provided |mid|. If bundling is enabled,
|
||||
// calling GetRtpTransport for multiple MIDs may yield the same object.
|
||||
RtpTransportInternal* GetRtpTransport(const std::string& mid) const;
|
||||
cricket::DtlsTransportInternal* GetDtlsTransport(
|
||||
const std::string& mid) const;
|
||||
cricket::DtlsTransportInternal* GetRtcpDtlsTransport(
|
||||
const std::string& mid) const;
|
||||
|
||||
/*********************
|
||||
* ICE-related methods
|
||||
********************/
|
||||
// This method is public to allow PeerConnection to update it from
|
||||
// SetConfiguration.
|
||||
void SetIceConfig(const cricket::IceConfig& config);
|
||||
// Set the "needs-ice-restart" flag as described in JSEP. After the flag is
|
||||
// set, offers should generate new ufrags/passwords until an ICE restart
|
||||
// occurs.
|
||||
void SetNeedsIceRestartFlag();
|
||||
// Returns true if the ICE restart flag above was set, and no ICE restart has
|
||||
// occurred yet for this transport (by applying a local description with
|
||||
// changed ufrag/password). If the transport has been deleted as a result of
|
||||
// bundling, returns false.
|
||||
bool NeedsIceRestart(const std::string& mid) const;
|
||||
// Start gathering candidates for any new transports, or transports doing an
|
||||
// ICE restart.
|
||||
void MaybeStartGathering();
|
||||
RTCError AddRemoteCandidates(
|
||||
const std::string& mid,
|
||||
const std::vector<cricket::Candidate>& candidates);
|
||||
RTCError RemoveRemoteCandidates(
|
||||
const std::vector<cricket::Candidate>& candidates);
|
||||
|
||||
/**********************
|
||||
* DTLS-related methods
|
||||
*********************/
|
||||
// Specifies the identity to use in this session.
|
||||
// Can only be called once.
|
||||
bool SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate(
|
||||
const std::string& mid) const;
|
||||
// Caller owns returned certificate. This method mainly exists for stats
|
||||
// reporting.
|
||||
std::unique_ptr<rtc::SSLCertificate> GetRemoteSSLCertificate(
|
||||
const std::string& mid) const;
|
||||
// Get negotiated role, if one has been negotiated.
|
||||
rtc::Optional<rtc::SSLRole> GetDtlsRole(const std::string& mid) const;
|
||||
|
||||
// TODO(deadbeef): GetStats isn't const because all the way down to
|
||||
// OpenSSLStreamAdapter, GetSslCipherSuite and GetDtlsSrtpCryptoSuite are not
|
||||
// const. Fix this.
|
||||
bool GetStats(const std::string& mid, cricket::TransportStats* stats);
|
||||
void SetMetricsObserver(webrtc::MetricsObserverInterface* metrics_observer);
|
||||
|
||||
// All of these signals are fired on the signaling thread.
|
||||
|
||||
// If any transport failed => failed,
|
||||
// Else if all completed => completed,
|
||||
// Else if all connected => connected,
|
||||
// Else => connecting
|
||||
sigslot::signal1<cricket::IceConnectionState> SignalIceConnectionState;
|
||||
|
||||
// If all transports done gathering => complete,
|
||||
// Else if any are gathering => gathering,
|
||||
// Else => new
|
||||
sigslot::signal1<cricket::IceGatheringState> SignalIceGatheringState;
|
||||
|
||||
// (mid, candidates)
|
||||
sigslot::signal2<const std::string&, const std::vector<cricket::Candidate>&>
|
||||
SignalIceCandidatesGathered;
|
||||
|
||||
sigslot::signal1<const std::vector<cricket::Candidate>&>
|
||||
SignalIceCandidatesRemoved;
|
||||
|
||||
sigslot::signal1<rtc::SSLHandshakeError> SignalDtlsHandshakeError;
|
||||
|
||||
// This will be fired when BUNDLE is enabled, the PeerConnection will handle
|
||||
// the signal and set the RtpTransport for the BaseChannel.
|
||||
// The first argument is the MID and the second is the new RtpTransport.
|
||||
// Before firing this signal, the previous RtpTransport must no longer be
|
||||
// referenced.
|
||||
sigslot::signal2<const std::string&, RtpTransportInternal*>
|
||||
SignalRtpTransportChanged;
|
||||
|
||||
// SCTP version of the signal above. PeerConnection will set a new
|
||||
// DtlsTransport for the SctpTransport.
|
||||
sigslot::signal2<const std::string&, cricket::DtlsTransportInternal*>
|
||||
SignalDtlsTransportChanged;
|
||||
|
||||
private:
|
||||
void OnMessage(rtc::Message* pmsg) override;
|
||||
|
||||
RTCError ApplyDescription_n(bool local,
|
||||
SdpType type,
|
||||
const cricket::SessionDescription* description);
|
||||
|
||||
void HandleRejectedContent(const cricket::ContentInfo& content_info);
|
||||
void HandleBundledContent(const cricket::ContentInfo& content_info);
|
||||
|
||||
cricket::JsepTransportDescription CreateJsepTransportDescription(
|
||||
cricket::ContentInfo content_info,
|
||||
cricket::TransportInfo transport_info,
|
||||
const std::vector<int>& encrypted_extension_ids);
|
||||
|
||||
rtc::Optional<std::string> bundled_mid() const {
|
||||
rtc::Optional<std::string> bundled_mid;
|
||||
if (bundle_group_) {
|
||||
bundled_mid = std::move(*(bundle_group_->FirstContentName()));
|
||||
}
|
||||
return bundled_mid;
|
||||
}
|
||||
|
||||
bool IsBundled(const std::string& mid) const {
|
||||
return bundle_group_ && bundle_group_->HasContentName(mid);
|
||||
}
|
||||
|
||||
bool ShouldUpdateBundleGroup(SdpType type,
|
||||
const cricket::SessionDescription* description);
|
||||
|
||||
std::vector<int> MergeEncryptedHeaderExtensionIdsForBundle(
|
||||
const cricket::SessionDescription* description);
|
||||
|
||||
std::vector<int> GetEncryptedHeaderExtensionIds(
|
||||
const cricket::ContentInfo& content_info);
|
||||
|
||||
const cricket::JsepTransport2* GetJsepTransport(const std::string& mid) const;
|
||||
cricket::JsepTransport2* GetJsepTransport(const std::string& mid);
|
||||
|
||||
void MaybeCreateJsepTransport(const std::string& mid,
|
||||
const cricket::ContentInfo& content_info);
|
||||
void MaybeDestroyJsepTransport(const std::string& mid);
|
||||
void DestroyAllJsepTransports_n();
|
||||
|
||||
void SetIceRole_n(cricket::IceRole ice_role);
|
||||
|
||||
cricket::IceRole DetermineIceRole(
|
||||
cricket::JsepTransport2* jsep_transport,
|
||||
const cricket::TransportInfo& transport_info,
|
||||
SdpType type,
|
||||
bool local);
|
||||
|
||||
std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
|
||||
const std::string& transport_name,
|
||||
bool rtcp);
|
||||
|
||||
std::unique_ptr<webrtc::RtpTransport> CreateUnencryptedRtpTransport(
|
||||
const std::string& transport_name,
|
||||
rtc::PacketTransportInternal* rtp_packet_transport,
|
||||
rtc::PacketTransportInternal* rtcp_packet_transport);
|
||||
std::unique_ptr<webrtc::SrtpTransport> CreateSdesTransport(
|
||||
const std::string& transport_name,
|
||||
rtc::PacketTransportInternal* rtp_packet_transport,
|
||||
rtc::PacketTransportInternal* rtcp_packet_transport);
|
||||
std::unique_ptr<webrtc::DtlsSrtpTransport> CreateDtlsSrtpTransport(
|
||||
const std::string& transport_name,
|
||||
cricket::DtlsTransportInternal* rtp_dtls_transport,
|
||||
cricket::DtlsTransportInternal* rtcp_dtls_transport);
|
||||
|
||||
// Collect all the DtlsTransports, including RTP and RTCP, from the
|
||||
// JsepTransports. JsepTransportController can iterate all the DtlsTransports
|
||||
// and update the aggregate states.
|
||||
std::vector<cricket::DtlsTransportInternal*> GetDtlsTransports();
|
||||
|
||||
// Handlers for signals from Transport.
|
||||
void OnTransportWritableState_n(rtc::PacketTransportInternal* transport);
|
||||
void OnTransportReceivingState_n(rtc::PacketTransportInternal* transport);
|
||||
void OnTransportGatheringState_n(cricket::IceTransportInternal* transport);
|
||||
void OnTransportCandidateGathered_n(cricket::IceTransportInternal* transport,
|
||||
const cricket::Candidate& candidate);
|
||||
void OnTransportCandidatesRemoved(const cricket::Candidates& candidates);
|
||||
void OnTransportCandidatesRemoved_n(cricket::IceTransportInternal* transport,
|
||||
const cricket::Candidates& candidates);
|
||||
void OnTransportRoleConflict_n(cricket::IceTransportInternal* transport);
|
||||
void OnTransportStateChanged_n(cricket::IceTransportInternal* transport);
|
||||
|
||||
void UpdateAggregateStates_n();
|
||||
|
||||
void OnDtlsHandshakeError(rtc::SSLHandshakeError error);
|
||||
|
||||
rtc::Thread* const signaling_thread_ = nullptr;
|
||||
rtc::Thread* const network_thread_ = nullptr;
|
||||
cricket::PortAllocator* const port_allocator_ = nullptr;
|
||||
|
||||
std::map<std::string, std::unique_ptr<cricket::JsepTransport2>>
|
||||
jsep_transports_by_mid_;
|
||||
|
||||
// Aggregate state for TransportChannelImpls.
|
||||
cricket::IceConnectionState ice_connection_state_ =
|
||||
cricket::kIceConnectionConnecting;
|
||||
cricket::IceGatheringState ice_gathering_state_ = cricket::kIceGatheringNew;
|
||||
|
||||
Config config_;
|
||||
const cricket::SessionDescription* local_desc_ = nullptr;
|
||||
const cricket::SessionDescription* remote_desc_ = nullptr;
|
||||
rtc::Optional<bool> initial_offerer_;
|
||||
|
||||
rtc::Optional<cricket::ContentGroup> bundle_group_;
|
||||
|
||||
cricket::IceConfig ice_config_;
|
||||
cricket::IceRole ice_role_ = cricket::ICEROLE_CONTROLLING;
|
||||
uint64_t ice_tiebreaker_ = rtc::CreateRandomId64();
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
|
||||
rtc::AsyncInvoker invoker_;
|
||||
|
||||
webrtc::MetricsObserverInterface* metrics_observer_ = nullptr;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(JsepTransportController);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_JSEPTRANSPORTCONTROLLER_H_
|
||||
1210
pc/jseptransportcontroller_unittest.cc
Normal file
1210
pc/jseptransportcontroller_unittest.cc
Normal file
File diff suppressed because it is too large
Load Diff
@ -11,8 +11,8 @@
|
||||
#include "pc/srtpfilter.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "media/base/rtputils.h"
|
||||
#include "pc/srtpsession.h"
|
||||
@ -35,6 +35,31 @@ bool SrtpFilter::IsActive() const {
|
||||
return state_ >= ST_ACTIVE;
|
||||
}
|
||||
|
||||
bool SrtpFilter::Process(const std::vector<CryptoParams>& cryptos,
|
||||
webrtc::SdpType type,
|
||||
ContentSource source) {
|
||||
bool ret = false;
|
||||
switch (type) {
|
||||
case webrtc::SdpType::kOffer:
|
||||
ret = SetOffer(cryptos, source);
|
||||
break;
|
||||
case webrtc::SdpType::kPrAnswer:
|
||||
ret = SetProvisionalAnswer(cryptos, source);
|
||||
break;
|
||||
case webrtc::SdpType::kAnswer:
|
||||
ret = SetAnswer(cryptos, source);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SrtpFilter::SetOffer(const std::vector<CryptoParams>& offer_params,
|
||||
ContentSource source) {
|
||||
if (!ExpectOffer(source)) {
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "api/cryptoparams.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/optional.h"
|
||||
#include "pc/sessiondescription.h"
|
||||
#include "rtc_base/basictypes.h"
|
||||
@ -54,6 +55,13 @@ class SrtpFilter {
|
||||
// Whether the filter is active (i.e. crypto has been properly negotiated).
|
||||
bool IsActive() const;
|
||||
|
||||
// Handle the offer/answer negotiation of the crypto parameters internally.
|
||||
// TODO(zhihuang): Make SetOffer/ProvisionalAnswer/Answer private as helper
|
||||
// methods once start using Process.
|
||||
bool Process(const std::vector<CryptoParams>& cryptos,
|
||||
webrtc::SdpType type,
|
||||
ContentSource source);
|
||||
|
||||
// Indicates which crypto algorithms and keys were contained in the offer.
|
||||
// offer_params should contain a list of available parameters to use, or none,
|
||||
// if crypto is not desired. This must be called before SetAnswer.
|
||||
|
||||
25
pc/transportstats.cc
Normal file
25
pc/transportstats.cc
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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/transportstats.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
TransportChannelStats::TransportChannelStats() = default;
|
||||
|
||||
TransportChannelStats::TransportChannelStats(const TransportChannelStats&) =
|
||||
default;
|
||||
|
||||
TransportChannelStats::~TransportChannelStats() = default;
|
||||
|
||||
TransportStats::TransportStats() = default;
|
||||
|
||||
TransportStats::~TransportStats() = default;
|
||||
|
||||
} // namespace cricket
|
||||
50
pc/transportstats.h
Normal file
50
pc/transportstats.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_TRANSPORTSTATS_H_
|
||||
#define PC_TRANSPORTSTATS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "p2p/base/dtlstransport.h"
|
||||
#include "p2p/base/port.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
struct TransportChannelStats {
|
||||
TransportChannelStats();
|
||||
TransportChannelStats(const TransportChannelStats&);
|
||||
~TransportChannelStats();
|
||||
|
||||
int component = 0;
|
||||
CandidateStatsList candidate_stats_list;
|
||||
ConnectionInfos connection_infos;
|
||||
int srtp_crypto_suite = rtc::SRTP_INVALID_CRYPTO_SUITE;
|
||||
int ssl_cipher_suite = rtc::TLS_NULL_WITH_NULL_NULL;
|
||||
DtlsTransportState dtls_state = DTLS_TRANSPORT_NEW;
|
||||
};
|
||||
|
||||
// Information about all the channels of a transport.
|
||||
// TODO(hta): Consider if a simple vector is as good as a map.
|
||||
typedef std::vector<TransportChannelStats> TransportChannelStatsList;
|
||||
|
||||
// Information about the stats of a transport.
|
||||
struct TransportStats {
|
||||
TransportStats();
|
||||
~TransportStats();
|
||||
|
||||
std::string transport_name;
|
||||
TransportChannelStatsList channel_stats;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // PC_TRANSPORTSTATS_H_
|
||||
Loading…
x
Reference in New Issue
Block a user