diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn index f5c00cc0a9..dcc7caf66c 100644 --- a/p2p/BUILD.gn +++ b/p2p/BUILD.gn @@ -43,6 +43,8 @@ rtc_static_library("rtc_p2p") { "base/icetransportinternal.h", "base/mdns_message.cc", "base/mdns_message.h", + "base/no_op_dtls_transport.cc", + "base/no_op_dtls_transport.h", "base/p2p_constants.cc", "base/p2p_constants.h", "base/p2p_transport_channel.cc", diff --git a/p2p/base/no_op_dtls_transport.cc b/p2p/base/no_op_dtls_transport.cc new file mode 100644 index 0000000000..3662d88c0f --- /dev/null +++ b/p2p/base/no_op_dtls_transport.cc @@ -0,0 +1,143 @@ +/* + * 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 +#include +#include + +#include "p2p/base/no_op_dtls_transport.h" + +#include "absl/memory/memory.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" +#include "logging/rtc_event_log/rtc_event_log.h" +#include "p2p/base/packet_transport_internal.h" +#include "rtc_base/buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/dscp.h" +#include "rtc_base/logging.h" +#include "rtc_base/message_queue.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/stream.h" +#include "rtc_base/thread.h" + +namespace cricket { + +NoOpDtlsTransport::NoOpDtlsTransport( + std::unique_ptr ice_transport, + const webrtc::CryptoOptions& crypto_options) + : crypto_options_(webrtc::CryptoOptions::NoGcm()), + ice_transport_(std::move(ice_transport)) { + RTC_DCHECK(ice_transport_); + ice_transport_->SignalWritableState.connect( + this, &NoOpDtlsTransport::OnWritableState); + ice_transport_->SignalReadyToSend.connect(this, + &NoOpDtlsTransport::OnReadyToSend); +} + +NoOpDtlsTransport::~NoOpDtlsTransport() {} +const webrtc::CryptoOptions& NoOpDtlsTransport::crypto_options() const { + return crypto_options_; +} +DtlsTransportState NoOpDtlsTransport::dtls_state() const { + return DTLS_TRANSPORT_CONNECTED; +} +int NoOpDtlsTransport::component() const { + return kNoOpDtlsTransportComponent; +} +bool NoOpDtlsTransport::IsDtlsActive() const { + return true; +} +bool NoOpDtlsTransport::GetDtlsRole(rtc::SSLRole* role) const { + return false; +} +bool NoOpDtlsTransport::SetDtlsRole(rtc::SSLRole role) { + return false; +} +bool NoOpDtlsTransport::GetSrtpCryptoSuite(int* cipher) { + return false; +} +bool NoOpDtlsTransport::GetSslCipherSuite(int* cipher) { + return false; +} +rtc::scoped_refptr NoOpDtlsTransport::GetLocalCertificate() + const { + return rtc::scoped_refptr(); +} +bool NoOpDtlsTransport::SetLocalCertificate( + const rtc::scoped_refptr& certificate) { + return false; +} +std::unique_ptr NoOpDtlsTransport::GetRemoteSSLCertChain() + const { + return std::unique_ptr(); +} +bool NoOpDtlsTransport::ExportKeyingMaterial(const std::string& label, + const uint8_t* context, + size_t context_len, + bool use_context, + uint8_t* result, + size_t result_len) { + return false; +} +bool NoOpDtlsTransport::SetRemoteFingerprint(const std::string& digest_alg, + const uint8_t* digest, + size_t digest_len) { + return true; +} +bool NoOpDtlsTransport::SetSslMaxProtocolVersion( + rtc::SSLProtocolVersion version) { + return true; +} +IceTransportInternal* NoOpDtlsTransport::ice_transport() { + return ice_transport_.get(); +} + +void NoOpDtlsTransport::OnReadyToSend(rtc::PacketTransportInternal* transport) { + RTC_DCHECK_RUN_ON(&thread_checker_); + if (is_writable_) { + SignalReadyToSend(this); + } +} + +void NoOpDtlsTransport::OnWritableState( + rtc::PacketTransportInternal* transport) { + RTC_DCHECK_RUN_ON(&thread_checker_); + is_writable_ = ice_transport_->writable(); + if (is_writable_) { + SignalWritableState(this); + } +} +const std::string& NoOpDtlsTransport::transport_name() const { + return ice_transport_->transport_name(); +} +bool NoOpDtlsTransport::writable() const { + return ice_transport_->writable(); +} +bool NoOpDtlsTransport::receiving() const { + return ice_transport_->receiving(); +} +int NoOpDtlsTransport::SendPacket(const char* data, + size_t len, + const rtc::PacketOptions& options, + int flags) { + return 0; +} + +int NoOpDtlsTransport::SetOption(rtc::Socket::Option opt, int value) { + return ice_transport_->SetOption(opt, value); +} + +int NoOpDtlsTransport::GetError() { + return ice_transport_->GetError(); +} + +} // namespace cricket diff --git a/p2p/base/no_op_dtls_transport.h b/p2p/base/no_op_dtls_transport.h new file mode 100644 index 0000000000..4211160c73 --- /dev/null +++ b/p2p/base/no_op_dtls_transport.h @@ -0,0 +1,109 @@ +/* + * Copyright 2019 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_NO_OP_DTLS_TRANSPORT_H_ +#define P2P_BASE_NO_OP_DTLS_TRANSPORT_H_ + +#include +#include +#include + +#include "api/crypto/crypto_options.h" +#include "p2p/base/dtls_transport_internal.h" +#include "p2p/base/ice_transport_internal.h" +#include "p2p/base/packet_transport_internal.h" +#include "rtc_base/buffer.h" +#include "rtc_base/buffer_queue.h" +#include "rtc_base/constructor_magic.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/stream.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/thread_checker.h" + +namespace cricket { + +constexpr int kNoOpDtlsTransportComponent = -1; + +// This implementation wraps a cricket::DtlsTransport, and takes +// ownership of it. +// The implementation does not perform any operations, except of being +// "connected". The purpose of this implementation is to disable RTP transport +// while MediaTransport is used. +// +// This implementation is only temporary. Long-term we will refactor and disable +// RTP transport entirely when MediaTransport is used. Always connected (after +// ICE), no-op, dtls transport. This is used when DTLS is disabled. +// +// MaybeCreateJsepTransport controller expects DTLS connection to send a +// 'connected' signal _after_ it is created (if it is created in a connected +// state, that would not be noticed by jsep transport controller). Therefore, +// the no-op dtls transport will wait for ICE event "writable", and then +// immediately report that it's connected (emulating 0-rtt connection). +// +// We could simply not set a dtls to active (not set a certificate on the DTLS), +// and it would use an underyling connection instead. +// However, when MediaTransport is used, we want to entirely disable +// dtls/srtp/rtp, in order to avoid multiplexing issues, such as "Failed to +// unprotect RTCP packet". +class NoOpDtlsTransport : public DtlsTransportInternal { + public: + NoOpDtlsTransport(std::unique_ptr ice_transport, + const webrtc::CryptoOptions& crypto_options); + + ~NoOpDtlsTransport() override; + const webrtc::CryptoOptions& crypto_options() const override; + DtlsTransportState dtls_state() const override; + int component() const override; + bool IsDtlsActive() const override; + bool GetDtlsRole(rtc::SSLRole* role) const override; + bool SetDtlsRole(rtc::SSLRole role) override; + bool GetSrtpCryptoSuite(int* cipher) override; + bool GetSslCipherSuite(int* cipher) override; + rtc::scoped_refptr GetLocalCertificate() const override; + bool SetLocalCertificate( + const rtc::scoped_refptr& certificate) override; + std::unique_ptr GetRemoteSSLCertChain() const override; + bool ExportKeyingMaterial(const std::string& label, + const uint8_t* context, + size_t context_len, + bool use_context, + uint8_t* result, + size_t result_len) override; + bool SetRemoteFingerprint(const std::string& digest_alg, + const uint8_t* digest, + size_t digest_len) override; + bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override; + IceTransportInternal* ice_transport() override; + + const std::string& transport_name() const override; + bool writable() const override; + bool receiving() const override; + + private: + void OnReadyToSend(rtc::PacketTransportInternal* transport); + void OnWritableState(rtc::PacketTransportInternal* transport); + + int SendPacket(const char* data, + size_t len, + const rtc::PacketOptions& options, + int flags) override; + int SetOption(rtc::Socket::Option opt, int value) override; + int GetError() override; + + rtc::ThreadChecker thread_checker_; + + webrtc::CryptoOptions crypto_options_; + std::unique_ptr ice_transport_; + bool is_writable_ = false; +}; + +} // namespace cricket + +#endif // P2P_BASE_NO_OP_DTLS_TRANSPORT_H_ diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc index dc05073c1b..926d71dcc6 100644 --- a/pc/jsep_transport_controller.cc +++ b/pc/jsep_transport_controller.cc @@ -15,6 +15,8 @@ #include #include "absl/memory/memory.h" +#include "p2p/base/ice_transport_internal.h" +#include "p2p/base/no_op_dtls_transport.h" #include "p2p/base/port.h" #include "pc/srtp_filter.h" #include "rtc_base/bind.h" @@ -409,23 +411,35 @@ void JsepTransportController::SetMediaTransportFactory( config_.media_transport_factory = media_transport_factory; } -std::unique_ptr -JsepTransportController::CreateDtlsTransport(const std::string& transport_name, - bool rtcp) { - RTC_DCHECK(network_thread_->IsCurrent()); +std::unique_ptr +JsepTransportController::CreateIceTransport(const std::string transport_name, + bool rtcp) { int component = rtcp ? cricket::ICE_CANDIDATE_COMPONENT_RTCP : cricket::ICE_CANDIDATE_COMPONENT_RTP; - std::unique_ptr dtls; if (config_.external_transport_factory) { - auto ice = config_.external_transport_factory->CreateIceTransport( + return config_.external_transport_factory->CreateIceTransport( transport_name, component); + } else { + return absl::make_unique( + transport_name, component, port_allocator_, async_resolver_factory_, + config_.event_log); + } +} + +std::unique_ptr +JsepTransportController::CreateDtlsTransport( + std::unique_ptr ice) { + RTC_DCHECK(network_thread_->IsCurrent()); + + std::unique_ptr dtls; + if (config_.media_transport_factory) { + dtls = absl::make_unique( + std::move(ice), config_.crypto_options); + } else if (config_.external_transport_factory) { dtls = config_.external_transport_factory->CreateDtlsTransport( std::move(ice), config_.crypto_options); } else { - auto ice = absl::make_unique( - transport_name, component, port_allocator_, async_resolver_factory_, - config_.event_log); dtls = absl::make_unique( std::move(ice), config_.crypto_options, config_.event_log); } @@ -1032,26 +1046,30 @@ RTCError JsepTransportController::MaybeCreateJsepTransport( "SDES and DTLS-SRTP cannot be enabled at the same time."); } + std::unique_ptr ice = + CreateIceTransport(content_info.name, /*rtcp=*/false); + + std::unique_ptr media_transport = + MaybeCreateMediaTransport(content_info, local, ice.get()); + std::unique_ptr rtp_dtls_transport = - CreateDtlsTransport(content_info.name, /*rtcp =*/false); + CreateDtlsTransport(std::move(ice)); std::unique_ptr rtcp_dtls_transport; std::unique_ptr unencrypted_rtp_transport; std::unique_ptr sdes_transport; std::unique_ptr dtls_srtp_transport; - std::unique_ptr media_transport; if (config_.rtcp_mux_policy != PeerConnectionInterface::kRtcpMuxPolicyRequire && content_info.type == cricket::MediaProtocolType::kRtp) { - rtcp_dtls_transport = - CreateDtlsTransport(content_info.name, /*rtcp =*/true); + RTC_DCHECK(media_transport == nullptr); + rtcp_dtls_transport = CreateDtlsTransport( + CreateIceTransport(content_info.name, /*rtcp=*/true)); } - media_transport = MaybeCreateMediaTransport( - content_info, local, rtp_dtls_transport->ice_transport()); // TODO(sukhanov): Do not create RTP/RTCP transports if media transport is - // used. + // used, and remove the no-op dtls transport when that's done. if (config_.disable_encryption) { unencrypted_rtp_transport = CreateUnencryptedRtpTransport( content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); diff --git a/pc/jsep_transport_controller.h b/pc/jsep_transport_controller.h index 1f4a632d47..e57b7d8fce 100644 --- a/pc/jsep_transport_controller.h +++ b/pc/jsep_transport_controller.h @@ -295,7 +295,9 @@ class JsepTransportController : public sigslot::has_slots<> { bool local); std::unique_ptr CreateDtlsTransport( - const std::string& transport_name, + std::unique_ptr ice); + std::unique_ptr CreateIceTransport( + const std::string transport_name, bool rtcp); std::unique_ptr CreateUnencryptedRtpTransport( diff --git a/pc/jsep_transport_controller_unittest.cc b/pc/jsep_transport_controller_unittest.cc index 018140d6a7..d6a31c81b7 100644 --- a/pc/jsep_transport_controller_unittest.cc +++ b/pc/jsep_transport_controller_unittest.cc @@ -16,6 +16,7 @@ #include "api/test/fake_media_transport.h" #include "p2p/base/fake_dtls_transport.h" #include "p2p/base/fake_ice_transport.h" +#include "p2p/base/no_op_dtls_transport.h" #include "p2p/base/transport_factory_interface.h" #include "p2p/base/transport_info.h" #include "pc/jsep_transport_controller.h" @@ -418,10 +419,10 @@ TEST_F(JsepTransportControllerTest, GetMediaTransportInCaller) { FakeMediaTransportFactory fake_media_transport_factory; JsepTransportController::Config config; - config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate; + config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire; config.media_transport_factory = &fake_media_transport_factory; CreateJsepTransportController(config); - auto description = CreateSessionDescriptionWithoutBundle(); + auto description = CreateSessionDescriptionWithBundleGroup(); AddCryptoSettings(description.get()); EXPECT_TRUE(transport_controller_ @@ -439,16 +440,20 @@ TEST_F(JsepTransportControllerTest, GetMediaTransportInCaller) { // Return nullptr for non-existing mids. EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2)); + + EXPECT_EQ(cricket::kNoOpDtlsTransportComponent, + transport_controller_->GetDtlsTransport(kAudioMid1)->component()) + << "Because media transport is used, expected no-op DTLS transport."; } TEST_F(JsepTransportControllerTest, GetMediaTransportInCallee) { FakeMediaTransportFactory fake_media_transport_factory; JsepTransportController::Config config; - config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate; + config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire; config.media_transport_factory = &fake_media_transport_factory; CreateJsepTransportController(config); - auto description = CreateSessionDescriptionWithoutBundle(); + auto description = CreateSessionDescriptionWithBundleGroup(); AddCryptoSettings(description.get()); EXPECT_TRUE(transport_controller_ ->SetRemoteDescription(SdpType::kOffer, description.get()) @@ -465,6 +470,10 @@ TEST_F(JsepTransportControllerTest, GetMediaTransportInCallee) { // Return nullptr for non-existing mids. EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2)); + + EXPECT_EQ(cricket::kNoOpDtlsTransportComponent, + transport_controller_->GetDtlsTransport(kAudioMid1)->component()) + << "Because media transport is used, expected no-op DTLS transport."; } TEST_F(JsepTransportControllerTest, GetMediaTransportIsNotSetIfNoSdes) { @@ -490,6 +499,10 @@ TEST_F(JsepTransportControllerTest, GetMediaTransportIsNotSetIfNoSdes) { .ok()); EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1)); + EXPECT_EQ(cricket::ICE_CANDIDATE_COMPONENT_RTP, + transport_controller_->GetDtlsTransport(kAudioMid1)->component()) + << "Because media transport is NOT used (fallback to RTP), expected " + "actual DTLS transport for RTP"; } TEST_F(JsepTransportControllerTest, @@ -497,7 +510,7 @@ TEST_F(JsepTransportControllerTest, FakeMediaTransportFactory fake_media_transport_factory; JsepTransportController::Config config; - config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate; + config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire; config.media_transport_factory = &fake_media_transport_factory; CreateJsepTransportController(config); auto description = CreateSessionDescriptionWithoutBundle(); @@ -813,25 +826,24 @@ TEST_F(JsepTransportControllerTest, JsepTransportController::Config config; config.media_transport_factory = &fake_media_transport_factory; CreateJsepTransportController(config); - auto description = CreateSessionDescriptionWithoutBundle(); + + // Media Transport is only used with bundle. + auto description = CreateSessionDescriptionWithBundleGroup(); AddCryptoSettings(description.get()); EXPECT_TRUE(transport_controller_ ->SetLocalDescription(SdpType::kOffer, description.get()) .ok()); - auto fake_audio_dtls = static_cast( - transport_controller_->GetDtlsTransport(kAudioMid1)); - auto fake_video_dtls = static_cast( - transport_controller_->GetDtlsTransport(kVideoMid1)); - fake_audio_dtls->SetWritable(true); - fake_video_dtls->SetWritable(true); - // Decreasing connection count from 2 to 1 triggers connection state event. - fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2); - fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1); - fake_video_dtls->fake_ice_transport()->SetConnectionCount(2); - fake_video_dtls->fake_ice_transport()->SetConnectionCount(1); - fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED); - fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED); + auto fake_audio_ice = static_cast( + transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport()); + auto fake_video_ice = static_cast( + transport_controller_->GetDtlsTransport(kVideoMid1)->ice_transport()); + fake_audio_ice->SetConnectionCount(2); + fake_audio_ice->SetConnectionCount(1); + fake_video_ice->SetConnectionCount(2); + fake_video_ice->SetConnectionCount(1); + fake_audio_ice->SetWritable(true); + fake_video_ice->SetWritable(true); // Still not connected, because we are waiting for media transport. EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_, @@ -864,19 +876,17 @@ TEST_F(JsepTransportControllerTest, ->SetLocalDescription(SdpType::kOffer, description.get()) .ok()); - auto fake_audio_dtls = static_cast( - transport_controller_->GetDtlsTransport(kAudioMid1)); - auto fake_video_dtls = static_cast( - transport_controller_->GetDtlsTransport(kVideoMid1)); - fake_audio_dtls->SetWritable(true); - fake_video_dtls->SetWritable(true); + auto fake_audio_ice = static_cast( + transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport()); + auto fake_video_ice = static_cast( + transport_controller_->GetDtlsTransport(kVideoMid1)->ice_transport()); + fake_audio_ice->SetWritable(true); + fake_video_ice->SetWritable(true); // Decreasing connection count from 2 to 1 triggers connection state event. - fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2); - fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1); - fake_video_dtls->fake_ice_transport()->SetConnectionCount(2); - fake_video_dtls->fake_ice_transport()->SetConnectionCount(1); - fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED); - fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED); + fake_audio_ice->SetConnectionCount(2); + fake_audio_ice->SetConnectionCount(1); + fake_video_ice->SetConnectionCount(2); + fake_video_ice->SetConnectionCount(1); FakeMediaTransport* media_transport = static_cast( transport_controller_->GetMediaTransport(kAudioMid1));