diff --git a/media/base/fakertp.h b/media/base/fakertp.h index 735da0172c..6786bab6d9 100644 --- a/media/base/fakertp.h +++ b/media/base/fakertp.h @@ -42,6 +42,8 @@ static const unsigned char kPcmuFrame[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; +static const int kHeaderExtensionIDs[] = {1, 4}; + // A typical PCMU RTP packet with header extensions. // PT=0, SN=1, TS=0, SSRC=1 // all data FF diff --git a/p2p/base/fakedtlstransport.h b/p2p/base/fakedtlstransport.h index 508158f678..fef00fdd62 100644 --- a/p2p/base/fakedtlstransport.h +++ b/p2p/base/fakedtlstransport.h @@ -97,6 +97,7 @@ class FakeDtlsTransport : public DtlsTransportInternal { dest->SetDestination(this, true); } dtls_state_ = DTLS_TRANSPORT_CONNECTED; + SignalDtlsState(this, dtls_state_); ice_transport_->SetDestination( static_cast(dest->ice_transport()), asymmetric); } else { diff --git a/p2p/base/fakeicetransport.h b/p2p/base/fakeicetransport.h index cc6cec99d8..ea730c32bb 100644 --- a/p2p/base/fakeicetransport.h +++ b/p2p/base/fakeicetransport.h @@ -207,6 +207,8 @@ class FakeIceTransport : public IceTransportInternal { int GetError() override { return 0; } + rtc::CopyOnWriteBuffer last_sent_packet() { return last_sent_packet_; } + rtc::Optional network_route() const override { return network_route_; } @@ -237,6 +239,7 @@ class FakeIceTransport : public IceTransportInternal { void SendPacketInternal(const rtc::CopyOnWriteBuffer& packet) { if (dest_) { + last_sent_packet_ = packet; dest_->SignalReadPacket(dest_, packet.data(), packet.size(), rtc::CreatePacketTime(0), 0); } @@ -266,6 +269,7 @@ class FakeIceTransport : public IceTransportInternal { rtc::CopyOnWriteBuffer send_packet_; rtc::Optional network_route_; std::map socket_options_; + rtc::CopyOnWriteBuffer last_sent_packet_; }; } // namespace cricket diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 01c571c660..1b405757da 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -38,6 +38,8 @@ rtc_static_library("rtc_pc_base") { "channelmanager.h", "currentspeakermonitor.cc", "currentspeakermonitor.h", + "dtlssrtptransport.cc", + "dtlssrtptransport.h", "externalhmac.cc", "externalhmac.h", "mediamonitor.cc", @@ -49,6 +51,7 @@ rtc_static_library("rtc_pc_base") { "rtptransport.cc", "rtptransport.h", "rtptransportinternal.h", + "rtptransportinternaladapter.h", "srtpfilter.cc", "srtpfilter.h", "srtpsession.cc", @@ -250,6 +253,7 @@ if (rtc_include_tests) { "channel_unittest.cc", "channelmanager_unittest.cc", "currentspeakermonitor_unittest.cc", + "dtlssrtptransport_unittest.cc", "mediasession_unittest.cc", "rtcpmuxfilter_unittest.cc", "rtptransport_unittest.cc", diff --git a/pc/dtlssrtptransport.cc b/pc/dtlssrtptransport.cc new file mode 100644 index 0000000000..1c2d21f429 --- /dev/null +++ b/pc/dtlssrtptransport.cc @@ -0,0 +1,341 @@ +/* + * 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. + */ + +#include "pc/dtlssrtptransport.h" + +#include +#include +#include + +#include "media/base/rtputils.h" +#include "rtc_base/sslstreamadapter.h" + +namespace { +// Value specified in RFC 5764. +static const char kDtlsSrtpExporterLabel[] = "EXTRACTOR-dtls_srtp"; +} // namespace + +namespace webrtc { + +DtlsSrtpTransport::DtlsSrtpTransport( + std::unique_ptr srtp_transport) + : RtpTransportInternalAdapter(srtp_transport.get()) { + srtp_transport_ = std::move(srtp_transport); + RTC_DCHECK(srtp_transport_); + srtp_transport_->SignalPacketReceived.connect( + this, &DtlsSrtpTransport::OnPacketReceived); + srtp_transport_->SignalReadyToSend.connect(this, + &DtlsSrtpTransport::OnReadyToSend); +} + +void DtlsSrtpTransport::SetDtlsTransports( + cricket::DtlsTransportInternal* rtp_dtls_transport, + cricket::DtlsTransportInternal* rtcp_dtls_transport) { + // Transport names should be the same. + if (rtp_dtls_transport && rtcp_dtls_transport) { + RTC_DCHECK(rtp_dtls_transport->transport_name() == + rtcp_dtls_transport->transport_name()); + } + + // When using DTLS-SRTP, we must reset the SrtpTransport every time the + // DtlsTransport changes and wait until the DTLS handshake is complete to set + // the newly negotiated parameters. + if (IsActive()) { + srtp_transport_->ResetParams(); + } + + if (rtcp_dtls_transport) { + // This would only be possible if using BUNDLE but not rtcp-mux, which isn't + // allowed according to the BUNDLE spec. + RTC_CHECK(!(IsActive())) + << "Setting RTCP for DTLS/SRTP after the DTLS is active " + << "should never happen."; + + RTC_LOG(LS_INFO) << "Setting RTCP Transport on " + << rtcp_dtls_transport->transport_name() << " transport " + << rtcp_dtls_transport; + SetRtcpDtlsTransport(rtcp_dtls_transport); + SetRtcpPacketTransport(rtcp_dtls_transport); + } + + RTC_LOG(LS_INFO) << "Setting RTP Transport on " + << rtp_dtls_transport->transport_name() << " transport " + << rtp_dtls_transport; + SetRtpDtlsTransport(rtp_dtls_transport); + SetRtpPacketTransport(rtp_dtls_transport); + + UpdateWritableStateAndMaybeSetupDtlsSrtp(); +} + +void DtlsSrtpTransport::SetRtcpMuxEnabled(bool enable) { + srtp_transport_->SetRtcpMuxEnabled(enable); + if (enable) { + UpdateWritableStateAndMaybeSetupDtlsSrtp(); + } +} + +void DtlsSrtpTransport::SetSendEncryptedHeaderExtensionIds( + const std::vector& send_extension_ids) { + send_extension_ids_.emplace(send_extension_ids); + // Reset the crypto parameters to update the send_extension IDs. + SetupRtpDtlsSrtp(); +} + +void DtlsSrtpTransport::SetRecvEncryptedHeaderExtensionIds( + const std::vector& recv_extension_ids) { + recv_extension_ids_.emplace(recv_extension_ids); + // Reset the crypto parameters to update the send_extension IDs. + SetupRtpDtlsSrtp(); +} + +bool DtlsSrtpTransport::IsDtlsActive() { + auto rtcp_dtls_transport = + rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_; + return (rtp_dtls_transport_ && rtp_dtls_transport_->IsDtlsActive() && + (!rtcp_dtls_transport || rtcp_dtls_transport->IsDtlsActive())); +} + +bool DtlsSrtpTransport::IsDtlsConnected() { + auto rtcp_dtls_transport = + rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_; + return (rtp_dtls_transport_ && + rtp_dtls_transport_->dtls_state() == + cricket::DTLS_TRANSPORT_CONNECTED && + (!rtcp_dtls_transport || rtcp_dtls_transport->dtls_state() == + cricket::DTLS_TRANSPORT_CONNECTED)); +} + +bool DtlsSrtpTransport::IsDtlsWritable() { + auto rtp_packet_transport = srtp_transport_->rtp_packet_transport(); + auto rtcp_packet_transport = + rtcp_mux_enabled() ? nullptr : srtp_transport_->rtcp_packet_transport(); + return rtp_packet_transport && rtp_packet_transport->writable() && + (!rtcp_packet_transport || rtcp_packet_transport->writable()); +} + +bool DtlsSrtpTransport::DtlsHandshakeCompleted() { + return IsDtlsActive() && IsDtlsConnected(); +} + +void DtlsSrtpTransport::MaybeSetupDtlsSrtp() { + if (IsActive() || !DtlsHandshakeCompleted()) { + return; + } + + SetupRtpDtlsSrtp(); + + if (!rtcp_mux_enabled() && rtcp_dtls_transport_) { + SetupRtcpDtlsSrtp(); + } +} + +void DtlsSrtpTransport::SetupRtpDtlsSrtp() { + // Use an empty encrypted header extension ID vector if not set. This could + // happen when the DTLS handshake is completed before processing the + // Offer/Answer which contains the encrypted header extension IDs. + std::vector send_extension_ids; + std::vector recv_extension_ids; + if (send_extension_ids_) { + send_extension_ids = *send_extension_ids_; + } + if (recv_extension_ids_) { + recv_extension_ids = *recv_extension_ids_; + } + + int selected_crypto_suite; + std::vector send_key; + std::vector recv_key; + + if (!ExtractParams(rtp_dtls_transport_, &selected_crypto_suite, &send_key, + &recv_key) || + !srtp_transport_->SetRtpParams( + selected_crypto_suite, &send_key[0], + static_cast(send_key.size()), send_extension_ids, + selected_crypto_suite, &recv_key[0], + static_cast(recv_key.size()), recv_extension_ids)) { + SignalDtlsSrtpSetupFailure(this, /*rtcp=*/false); + RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTP failed"; + } +} + +void DtlsSrtpTransport::SetupRtcpDtlsSrtp() { + // Return if the DTLS-SRTP is active because the encrypted header extension + // IDs don't need to be updated for RTCP and the crypto params don't need to + // be reset. + if (IsActive()) { + return; + } + + std::vector send_extension_ids; + std::vector recv_extension_ids; + if (send_extension_ids_) { + send_extension_ids = *send_extension_ids_; + } + if (recv_extension_ids_) { + recv_extension_ids = *recv_extension_ids_; + } + + int selected_crypto_suite; + std::vector rtcp_send_key; + std::vector rtcp_recv_key; + if (!ExtractParams(rtcp_dtls_transport_, &selected_crypto_suite, + &rtcp_send_key, &rtcp_recv_key) || + !srtp_transport_->SetRtcpParams( + selected_crypto_suite, &rtcp_send_key[0], + static_cast(rtcp_send_key.size()), send_extension_ids, + selected_crypto_suite, &rtcp_recv_key[0], + static_cast(rtcp_recv_key.size()), recv_extension_ids)) { + SignalDtlsSrtpSetupFailure(this, /*rtcp=*/true); + RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTCP failed"; + } +} + +bool DtlsSrtpTransport::ExtractParams( + cricket::DtlsTransportInternal* dtls_transport, + int* selected_crypto_suite, + std::vector* send_key, + std::vector* recv_key) { + if (!dtls_transport || !dtls_transport->IsDtlsActive()) { + return false; + } + + if (!dtls_transport->GetSrtpCryptoSuite(selected_crypto_suite)) { + RTC_LOG(LS_ERROR) << "No DTLS-SRTP selected crypto suite"; + return false; + } + + RTC_LOG(LS_INFO) << "Extracting keys from transport: " + << dtls_transport->transport_name(); + + int key_len; + int salt_len; + if (!rtc::GetSrtpKeyAndSaltLengths((*selected_crypto_suite), &key_len, + &salt_len)) { + RTC_LOG(LS_ERROR) << "Unknown DTLS-SRTP crypto suite" + << selected_crypto_suite; + return false; + } + + // OK, we're now doing DTLS (RFC 5764) + std::vector dtls_buffer(key_len * 2 + salt_len * 2); + + // RFC 5705 exporter using the RFC 5764 parameters + if (!dtls_transport->ExportKeyingMaterial(kDtlsSrtpExporterLabel, NULL, 0, + false, &dtls_buffer[0], + dtls_buffer.size())) { + RTC_LOG(LS_WARNING) << "DTLS-SRTP key export failed"; + RTC_NOTREACHED(); // This should never happen + return false; + } + + // Sync up the keys with the DTLS-SRTP interface + std::vector client_write_key(key_len + salt_len); + std::vector server_write_key(key_len + salt_len); + size_t offset = 0; + memcpy(&client_write_key[0], &dtls_buffer[offset], key_len); + offset += key_len; + memcpy(&server_write_key[0], &dtls_buffer[offset], key_len); + offset += key_len; + memcpy(&client_write_key[key_len], &dtls_buffer[offset], salt_len); + offset += salt_len; + 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"; + return false; + } + + if (role == rtc::SSL_SERVER) { + *send_key = server_write_key; + *recv_key = client_write_key; + } else { + *send_key = client_write_key; + *recv_key = server_write_key; + } + + return true; +} + +void DtlsSrtpTransport::SetDtlsTransport( + cricket::DtlsTransportInternal* new_dtls_transport, + cricket::DtlsTransportInternal** old_dtls_transport) { + if (*old_dtls_transport) { + (*old_dtls_transport)->SignalDtlsState.disconnect(this); + (*old_dtls_transport)->SignalWritableState.disconnect(this); + } + + *old_dtls_transport = new_dtls_transport; + + if (new_dtls_transport) { + new_dtls_transport->SignalDtlsState.connect( + this, &DtlsSrtpTransport::OnDtlsState); + new_dtls_transport->SignalWritableState.connect( + this, &DtlsSrtpTransport::OnWritableState); + } +} + +void DtlsSrtpTransport::SetRtpDtlsTransport( + cricket::DtlsTransportInternal* rtp_dtls_transport) { + SetDtlsTransport(rtp_dtls_transport, &rtp_dtls_transport_); +} + +void DtlsSrtpTransport::SetRtcpDtlsTransport( + cricket::DtlsTransportInternal* rtcp_dtls_transport) { + SetDtlsTransport(rtcp_dtls_transport, &rtcp_dtls_transport_); +} + +void DtlsSrtpTransport::UpdateWritableStateAndMaybeSetupDtlsSrtp() { + bool writable = IsDtlsWritable(); + SetWritable(writable); + if (writable) { + MaybeSetupDtlsSrtp(); + } +} + +void DtlsSrtpTransport::SetWritable(bool writable) { + // Only fire the signal if the writable state changes. + if (writable_ != writable) { + writable_ = writable; + SignalWritableState(writable_); + } +} + +void DtlsSrtpTransport::OnDtlsState(cricket::DtlsTransportInternal* transport, + cricket::DtlsTransportState state) { + RTC_DCHECK(transport == rtp_dtls_transport_ || + transport == rtcp_dtls_transport_); + + if (state != cricket::DTLS_TRANSPORT_CONNECTED) { + srtp_transport_->ResetParams(); + return; + } + + MaybeSetupDtlsSrtp(); +} + +void DtlsSrtpTransport::OnWritableState( + rtc::PacketTransportInternal* transport) { + RTC_DCHECK(transport == srtp_transport_->rtp_packet_transport() || + transport == srtp_transport_->rtcp_packet_transport()); + UpdateWritableStateAndMaybeSetupDtlsSrtp(); +} + +void DtlsSrtpTransport::OnPacketReceived(bool rtcp, + rtc::CopyOnWriteBuffer* packet, + const rtc::PacketTime& packet_time) { + SignalPacketReceived(rtcp, packet, packet_time); +} + +void DtlsSrtpTransport::OnReadyToSend(bool ready) { + SignalReadyToSend(ready); +} + +} // namespace webrtc diff --git a/pc/dtlssrtptransport.h b/pc/dtlssrtptransport.h new file mode 100644 index 0000000000..43f072a8fa --- /dev/null +++ b/pc/dtlssrtptransport.h @@ -0,0 +1,97 @@ +/* + * 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_DTLSSRTPTRANSPORT_H_ +#define PC_DTLSSRTPTRANSPORT_H_ + +#include +#include +#include + +#include "p2p/base/dtlstransportinternal.h" +#include "pc/rtptransportinternaladapter.h" +#include "pc/srtptransport.h" + +namespace webrtc { + +// This class is intended to be used as an RtpTransport and it wraps both an +// SrtpTransport and DtlsTransports(RTP/RTCP). When the DTLS handshake is +// finished, it extracts the keying materials from DtlsTransport and sets them +// to SrtpTransport. +class DtlsSrtpTransport : public RtpTransportInternalAdapter { + public: + explicit DtlsSrtpTransport( + std::unique_ptr srtp_transport); + + // Set P2P layer RTP/RTCP DtlsTransports. When using RTCP-muxing, + // |rtcp_dtls_transport| is null. + void SetDtlsTransports(cricket::DtlsTransportInternal* rtp_dtls_transport, + cricket::DtlsTransportInternal* rtcp_dtls_transport); + + void SetRtcpMuxEnabled(bool enable) override; + + // Set the header extension ids that should be encrypted. + void SetSendEncryptedHeaderExtensionIds( + const std::vector& send_extension_ids); + + void SetRecvEncryptedHeaderExtensionIds( + const std::vector& recv_extension_ids); + + bool IsActive() { return srtp_transport_->IsActive(); } + + // TODO(zhihuang): Remove this when we remove RtpTransportAdapter. + RtpTransportAdapter* GetInternal() override { return nullptr; } + + sigslot::signal2 SignalDtlsSrtpSetupFailure; + + private: + bool IsDtlsActive(); + bool IsDtlsConnected(); + bool IsDtlsWritable(); + bool DtlsHandshakeCompleted(); + void MaybeSetupDtlsSrtp(); + void SetupRtpDtlsSrtp(); + void SetupRtcpDtlsSrtp(); + bool ExtractParams(cricket::DtlsTransportInternal* dtls_transport, + int* selected_crypto_suite, + std::vector* send_key, + std::vector* recv_key); + void SetDtlsTransport(cricket::DtlsTransportInternal* new_dtls_transport, + cricket::DtlsTransportInternal** old_dtls_transport); + void SetRtpDtlsTransport(cricket::DtlsTransportInternal* rtp_dtls_transport); + void SetRtcpDtlsTransport( + cricket::DtlsTransportInternal* rtcp_dtls_transport); + void UpdateWritableStateAndMaybeSetupDtlsSrtp(); + // Set the writability and fire the SignalWritableState if the writability + // changes. + void SetWritable(bool writable); + + void OnDtlsState(cricket::DtlsTransportInternal* dtls_transport, + cricket::DtlsTransportState state); + void OnWritableState(rtc::PacketTransportInternal* transport); + void OnPacketReceived(bool rtcp, + rtc::CopyOnWriteBuffer* packet, + const rtc::PacketTime& packet_time); + void OnReadyToSend(bool ready); + + bool writable_ = false; + std::unique_ptr srtp_transport_; + // Owned by the TransportController. + cricket::DtlsTransportInternal* rtp_dtls_transport_ = nullptr; + cricket::DtlsTransportInternal* rtcp_dtls_transport_ = nullptr; + + // The encrypted header extension IDs. + rtc::Optional> send_extension_ids_; + rtc::Optional> recv_extension_ids_; +}; + +} // namespace webrtc + +#endif // PC_DTLSSRTPTRANSPORT_H_ diff --git a/pc/dtlssrtptransport_unittest.cc b/pc/dtlssrtptransport_unittest.cc new file mode 100644 index 0000000000..7af9870675 --- /dev/null +++ b/pc/dtlssrtptransport_unittest.cc @@ -0,0 +1,486 @@ +/* + * 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. + */ + +#include "pc/dtlssrtptransport.h" + +#include +#include + +#include "media/base/fakertp.h" +#include "p2p/base/dtlstransportinternal.h" +#include "p2p/base/fakedtlstransport.h" +#include "p2p/base/fakepackettransport.h" +#include "p2p/base/p2pconstants.h" +#include "pc/rtptransport.h" +#include "pc/rtptransporttestutil.h" +#include "rtc_base/asyncpacketsocket.h" +#include "rtc_base/gunit.h" +#include "rtc_base/ptr_util.h" +#include "rtc_base/sslstreamadapter.h" + +using cricket::FakeDtlsTransport; +using cricket::FakeIceTransport; +using webrtc::DtlsSrtpTransport; +using webrtc::SrtpTransport; +using webrtc::RtpTransport; + +const int kRtpAuthTagLen = 10; + +class TransportObserver : public sigslot::has_slots<> { + public: + void OnPacketReceived(bool rtcp, + rtc::CopyOnWriteBuffer* packet, + const rtc::PacketTime& packet_time) { + rtcp ? last_recv_rtcp_packet_ = *packet : last_recv_rtp_packet_ = *packet; + } + + void OnReadyToSend(bool ready) { ready_to_send_ = ready; } + + rtc::CopyOnWriteBuffer last_recv_rtp_packet() { + return last_recv_rtp_packet_; + } + + rtc::CopyOnWriteBuffer last_recv_rtcp_packet() { + return last_recv_rtcp_packet_; + } + + bool ready_to_send() { return ready_to_send_; } + + private: + rtc::CopyOnWriteBuffer last_recv_rtp_packet_; + rtc::CopyOnWriteBuffer last_recv_rtcp_packet_; + bool ready_to_send_ = false; +}; + +class DtlsSrtpTransportTest : public testing::Test, + public sigslot::has_slots<> { + protected: + DtlsSrtpTransportTest() {} + + std::unique_ptr MakeDtlsSrtpTransport( + FakeDtlsTransport* rtp_dtls, + FakeDtlsTransport* rtcp_dtls, + bool rtcp_mux_enabled) { + auto rtp_transport = rtc::MakeUnique(rtcp_mux_enabled); + + rtp_transport->SetRtpPacketTransport(rtp_dtls); + rtp_transport->SetRtcpPacketTransport(rtcp_dtls); + rtp_transport->AddHandledPayloadType(0x00); + rtp_transport->AddHandledPayloadType(0xc9); + + auto srtp_transport = + rtc::MakeUnique(std::move(rtp_transport), "content"); + auto dtls_srtp_transport = + rtc::MakeUnique(std::move(srtp_transport)); + + dtls_srtp_transport->SetDtlsTransports(rtp_dtls, rtcp_dtls); + + return dtls_srtp_transport; + } + + void MakeDtlsSrtpTransports(FakeDtlsTransport* rtp_dtls1, + FakeDtlsTransport* rtcp_dtls1, + FakeDtlsTransport* rtp_dtls2, + FakeDtlsTransport* rtcp_dtls2, + bool rtcp_mux_enabled) { + dtls_srtp_transport1_ = + MakeDtlsSrtpTransport(rtp_dtls1, rtcp_dtls1, rtcp_mux_enabled); + dtls_srtp_transport2_ = + MakeDtlsSrtpTransport(rtp_dtls2, rtcp_dtls2, rtcp_mux_enabled); + + dtls_srtp_transport1_->SignalPacketReceived.connect( + &transport_observer1_, &TransportObserver::OnPacketReceived); + dtls_srtp_transport1_->SignalReadyToSend.connect( + &transport_observer1_, &TransportObserver::OnReadyToSend); + + dtls_srtp_transport2_->SignalPacketReceived.connect( + &transport_observer2_, &TransportObserver::OnPacketReceived); + dtls_srtp_transport2_->SignalReadyToSend.connect( + &transport_observer2_, &TransportObserver::OnReadyToSend); + } + + void CompleteDtlsHandshake(FakeDtlsTransport* fake_dtls1, + FakeDtlsTransport* fake_dtls2) { + auto cert1 = rtc::RTCCertificate::Create(std::unique_ptr( + rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT))); + fake_dtls1->SetLocalCertificate(cert1); + auto cert2 = rtc::RTCCertificate::Create(std::unique_ptr( + rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT))); + fake_dtls2->SetLocalCertificate(cert2); + fake_dtls1->SetDestination(fake_dtls2); + } + + void SendRecvRtpPackets() { + ASSERT_TRUE(dtls_srtp_transport1_); + ASSERT_TRUE(dtls_srtp_transport2_); + ASSERT_TRUE(dtls_srtp_transport1_->IsActive()); + ASSERT_TRUE(dtls_srtp_transport2_->IsActive()); + + size_t rtp_len = sizeof(kPcmuFrame); + size_t packet_size = rtp_len + kRtpAuthTagLen; + rtc::Buffer rtp_packet_buffer(packet_size); + char* rtp_packet_data = rtp_packet_buffer.data(); + memcpy(rtp_packet_data, kPcmuFrame, rtp_len); + // In order to be able to run this test function multiple times we can not + // use the same sequence number twice. Increase the sequence number by one. + rtc::SetBE16(reinterpret_cast(rtp_packet_data) + 2, + ++sequence_number_); + rtc::CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len, + packet_size); + rtc::CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len, + packet_size); + + rtc::PacketOptions options; + // Send a packet from |srtp_transport1_| to |srtp_transport2_| and verify + // that the packet can be successfully received and decrypted. + ASSERT_TRUE(dtls_srtp_transport1_->SendRtpPacket(&rtp_packet1to2, options, + cricket::PF_SRTP_BYPASS)); + ASSERT_TRUE(transport_observer2_.last_recv_rtp_packet().data()); + EXPECT_EQ(0, memcmp(transport_observer2_.last_recv_rtp_packet().data(), + kPcmuFrame, rtp_len)); + ASSERT_TRUE(dtls_srtp_transport2_->SendRtpPacket(&rtp_packet2to1, options, + cricket::PF_SRTP_BYPASS)); + ASSERT_TRUE(transport_observer1_.last_recv_rtp_packet().data()); + EXPECT_EQ(0, memcmp(transport_observer1_.last_recv_rtp_packet().data(), + kPcmuFrame, rtp_len)); + } + + void SendRecvRtcpPackets() { + size_t rtcp_len = sizeof(kRtcpReport); + size_t packet_size = rtcp_len + 4 + kRtpAuthTagLen; + rtc::Buffer rtcp_packet_buffer(packet_size); + + // TODO(zhihuang): Remove the extra copy when the SendRtpPacket method + // doesn't take the CopyOnWriteBuffer by pointer. + rtc::CopyOnWriteBuffer rtcp_packet1to2(kRtcpReport, rtcp_len, packet_size); + rtc::CopyOnWriteBuffer rtcp_packet2to1(kRtcpReport, rtcp_len, packet_size); + + rtc::PacketOptions options; + // Send a packet from |srtp_transport1_| to |srtp_transport2_| and verify + // that the packet can be successfully received and decrypted. + ASSERT_TRUE(dtls_srtp_transport1_->SendRtcpPacket(&rtcp_packet1to2, options, + cricket::PF_SRTP_BYPASS)); + ASSERT_TRUE(transport_observer2_.last_recv_rtcp_packet().data()); + EXPECT_EQ(0, memcmp(transport_observer2_.last_recv_rtcp_packet().data(), + kRtcpReport, rtcp_len)); + + // Do the same thing in the opposite direction; + ASSERT_TRUE(dtls_srtp_transport2_->SendRtcpPacket(&rtcp_packet2to1, options, + cricket::PF_SRTP_BYPASS)); + ASSERT_TRUE(transport_observer1_.last_recv_rtcp_packet().data()); + EXPECT_EQ(0, memcmp(transport_observer1_.last_recv_rtcp_packet().data(), + kRtcpReport, rtcp_len)); + } + + void SendRecvRtpPacketsWithHeaderExtension( + const std::vector& encrypted_header_ids) { + ASSERT_TRUE(dtls_srtp_transport1_); + ASSERT_TRUE(dtls_srtp_transport2_); + ASSERT_TRUE(dtls_srtp_transport1_->IsActive()); + ASSERT_TRUE(dtls_srtp_transport2_->IsActive()); + + size_t rtp_len = sizeof(kPcmuFrameWithExtensions); + size_t packet_size = rtp_len + kRtpAuthTagLen; + rtc::Buffer rtp_packet_buffer(packet_size); + char* rtp_packet_data = rtp_packet_buffer.data(); + memcpy(rtp_packet_data, kPcmuFrameWithExtensions, rtp_len); + // In order to be able to run this test function multiple times we can not + // use the same sequence number twice. Increase the sequence number by one. + rtc::SetBE16(reinterpret_cast(rtp_packet_data) + 2, + ++sequence_number_); + rtc::CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len, + packet_size); + rtc::CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len, + packet_size); + + char original_rtp_data[sizeof(kPcmuFrameWithExtensions)]; + memcpy(original_rtp_data, rtp_packet_data, rtp_len); + + rtc::PacketOptions options; + // Send a packet from |srtp_transport1_| to |srtp_transport2_| and verify + // that the packet can be successfully received and decrypted. + ASSERT_TRUE(dtls_srtp_transport1_->SendRtpPacket(&rtp_packet1to2, options, + cricket::PF_SRTP_BYPASS)); + ASSERT_TRUE(transport_observer2_.last_recv_rtp_packet().data()); + EXPECT_EQ(0, memcmp(transport_observer2_.last_recv_rtp_packet().data(), + original_rtp_data, rtp_len)); + // Get the encrypted packet from underneath packet transport and verify the + // data and header extension are actually encrypted. + auto fake_dtls_transport = static_cast( + dtls_srtp_transport1_->rtp_packet_transport()); + auto fake_ice_transport = + static_cast(fake_dtls_transport->ice_transport()); + EXPECT_NE(0, memcmp(fake_ice_transport->last_sent_packet().data(), + original_rtp_data, rtp_len)); + CompareHeaderExtensions(reinterpret_cast( + fake_ice_transport->last_sent_packet().data()), + fake_ice_transport->last_sent_packet().size(), + original_rtp_data, rtp_len, encrypted_header_ids, + false); + + // Do the same thing in the opposite direction. + ASSERT_TRUE(dtls_srtp_transport2_->SendRtpPacket(&rtp_packet2to1, options, + cricket::PF_SRTP_BYPASS)); + ASSERT_TRUE(transport_observer1_.last_recv_rtp_packet().data()); + EXPECT_EQ(0, memcmp(transport_observer1_.last_recv_rtp_packet().data(), + original_rtp_data, rtp_len)); + // Get the encrypted packet from underneath packet transport and verify the + // data and header extension are actually encrypted. + fake_dtls_transport = static_cast( + dtls_srtp_transport2_->rtp_packet_transport()); + fake_ice_transport = + static_cast(fake_dtls_transport->ice_transport()); + EXPECT_NE(0, memcmp(fake_ice_transport->last_sent_packet().data(), + original_rtp_data, rtp_len)); + CompareHeaderExtensions(reinterpret_cast( + fake_ice_transport->last_sent_packet().data()), + fake_ice_transport->last_sent_packet().size(), + original_rtp_data, rtp_len, encrypted_header_ids, + false); + } + + void SendRecvPackets() { + SendRecvRtpPackets(); + SendRecvRtcpPackets(); + } + + std::unique_ptr dtls_srtp_transport1_; + std::unique_ptr dtls_srtp_transport2_; + TransportObserver transport_observer1_; + TransportObserver transport_observer2_; + + int sequence_number_ = 0; +}; + +// Tests that if RTCP muxing is enabled and transports are set after RTP +// transport finished the handshake, SRTP is set up. +TEST_F(DtlsSrtpTransportTest, SetTransportsAfterHandshakeCompleteWithRtcpMux) { + auto rtp_dtls1 = rtc::MakeUnique( + "video", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtp_dtls2 = rtc::MakeUnique( + "video", cricket::ICE_CANDIDATE_COMPONENT_RTP); + + MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr, + /*rtcp_mux_enabled=*/true); + + auto rtp_dtls3 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtp_dtls4 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + + CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get()); + + dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), nullptr); + dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), nullptr); + + SendRecvPackets(); +} + +// Tests that if RTCP muxing is not enabled and transports are set after both +// RTP and RTCP transports finished the handshake, SRTP is set up. +TEST_F(DtlsSrtpTransportTest, + SetTransportsAfterHandshakeCompleteWithoutRtcpMux) { + auto rtp_dtls1 = rtc::MakeUnique( + "video", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtcp_dtls1 = rtc::MakeUnique( + "video", cricket::ICE_CANDIDATE_COMPONENT_RTCP); + auto rtp_dtls2 = rtc::MakeUnique( + "video", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtcp_dtls2 = rtc::MakeUnique( + "video", cricket::ICE_CANDIDATE_COMPONENT_RTCP); + + MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(), + rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false); + + auto rtp_dtls3 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtcp_dtls3 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP); + auto rtp_dtls4 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtcp_dtls4 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP); + CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get()); + CompleteDtlsHandshake(rtcp_dtls3.get(), rtcp_dtls4.get()); + + dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), rtcp_dtls3.get()); + dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), rtcp_dtls4.get()); + + SendRecvPackets(); +} + +// Tests if RTCP muxing is enabled, SRTP is set up as soon as the RTP DTLS +// handshake is finished. +TEST_F(DtlsSrtpTransportTest, SetTransportsBeforeHandshakeCompleteWithRtcpMux) { + auto rtp_dtls1 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtcp_dtls1 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP); + auto rtp_dtls2 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtcp_dtls2 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP); + + MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(), + rtcp_dtls2.get(), + /*rtcp_mux_enabled=*/false); + + dtls_srtp_transport1_->SetRtcpMuxEnabled(true); + dtls_srtp_transport2_->SetRtcpMuxEnabled(true); + CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get()); + SendRecvPackets(); +} + +// Tests if RTCP muxing is not enabled, SRTP is set up when both the RTP and +// RTCP DTLS handshake are finished. +TEST_F(DtlsSrtpTransportTest, + SetTransportsBeforeHandshakeCompleteWithoutRtcpMux) { + auto rtp_dtls1 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtcp_dtls1 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP); + auto rtp_dtls2 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtcp_dtls2 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP); + + MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(), + rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false); + + CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get()); + EXPECT_FALSE(dtls_srtp_transport1_->IsActive()); + EXPECT_FALSE(dtls_srtp_transport2_->IsActive()); + CompleteDtlsHandshake(rtcp_dtls1.get(), rtcp_dtls2.get()); + SendRecvPackets(); +} + +// Tests that if the DtlsTransport underneath is changed, the previous DTLS-SRTP +// context will be reset and will be re-setup once the new transports' handshake +// complete. +TEST_F(DtlsSrtpTransportTest, DtlsSrtpResetAfterDtlsTransportChange) { + auto rtp_dtls1 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtp_dtls2 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + + MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr, + /*rtcp_mux_enabled=*/true); + + CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get()); + EXPECT_TRUE(dtls_srtp_transport1_->IsActive()); + EXPECT_TRUE(dtls_srtp_transport2_->IsActive()); + + auto rtp_dtls3 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtp_dtls4 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + + // The previous context is reset. + dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), nullptr); + dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), nullptr); + EXPECT_FALSE(dtls_srtp_transport1_->IsActive()); + EXPECT_FALSE(dtls_srtp_transport2_->IsActive()); + + // Re-setup. + CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get()); + SendRecvPackets(); +} + +// Tests if only the RTP DTLS handshake complete, and then RTCP muxing is +// enabled, SRTP is set up. +TEST_F(DtlsSrtpTransportTest, + RtcpMuxEnabledAfterRtpTransportHandshakeComplete) { + auto rtp_dtls1 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtcp_dtls1 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP); + auto rtp_dtls2 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtcp_dtls2 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP); + + MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(), + rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false); + + CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get()); + // Inactive because the RTCP transport handshake didn't complete. + EXPECT_FALSE(dtls_srtp_transport1_->IsActive()); + EXPECT_FALSE(dtls_srtp_transport2_->IsActive()); + + dtls_srtp_transport1_->SetRtcpMuxEnabled(true); + dtls_srtp_transport2_->SetRtcpMuxEnabled(true); + // The transports should be active and be able to send packets when the + // RTCP muxing is enabled. + SendRecvPackets(); +} + +// Tests that when SetSend/RecvEncryptedHeaderExtensionIds is called, the SRTP +// sessions are updated with new encryped header extension IDs immediately. +TEST_F(DtlsSrtpTransportTest, EncryptedHeaderExtensionIdUpdated) { + auto rtp_dtls1 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtp_dtls2 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + + MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr, + /*rtcp_mux_enabled=*/true); + CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get()); + + std::vector encrypted_headers; + encrypted_headers.push_back(kHeaderExtensionIDs[0]); + encrypted_headers.push_back(kHeaderExtensionIDs[1]); + RTC_LOG(INFO) << "here"; + + dtls_srtp_transport1_->SetSendEncryptedHeaderExtensionIds(encrypted_headers); + dtls_srtp_transport1_->SetRecvEncryptedHeaderExtensionIds(encrypted_headers); + dtls_srtp_transport2_->SetSendEncryptedHeaderExtensionIds(encrypted_headers); + dtls_srtp_transport2_->SetRecvEncryptedHeaderExtensionIds(encrypted_headers); + SendRecvRtpPacketsWithHeaderExtension(encrypted_headers); +} + +// Tests if RTCP muxing is enabled. DtlsSrtpTransport is ready to send once the +// RTP DtlsTransport is ready. +TEST_F(DtlsSrtpTransportTest, SignalReadyToSendFiredWithRtcpMux) { + auto rtp_dtls1 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtp_dtls2 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + + MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr, + /*rtcp_mux_enabled=*/true); + + rtp_dtls1->SetDestination(rtp_dtls2.get()); + EXPECT_TRUE(transport_observer1_.ready_to_send()); + EXPECT_TRUE(transport_observer2_.ready_to_send()); +} + +// Tests if RTCP muxing is not enabled. DtlsSrtpTransport is ready to send once +// both the RTP and RTCP DtlsTransport are ready. +TEST_F(DtlsSrtpTransportTest, SignalReadyToSendFiredWithoutRtcpMux) { + auto rtp_dtls1 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtcp_dtls1 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP); + auto rtp_dtls2 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + auto rtcp_dtls2 = rtc::MakeUnique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP); + + MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(), + rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false); + + rtp_dtls1->SetDestination(rtp_dtls2.get()); + EXPECT_FALSE(transport_observer1_.ready_to_send()); + EXPECT_FALSE(transport_observer2_.ready_to_send()); + + rtcp_dtls1->SetDestination(rtcp_dtls2.get()); + EXPECT_TRUE(transport_observer1_.ready_to_send()); + EXPECT_TRUE(transport_observer2_.ready_to_send()); +} diff --git a/pc/rtptransport.h b/pc/rtptransport.h index 34ec1b46b9..a42493a563 100644 --- a/pc/rtptransport.h +++ b/pc/rtptransport.h @@ -36,7 +36,7 @@ class RtpTransport : public RtpTransportInternal { explicit RtpTransport(bool rtcp_mux_enabled) : rtcp_mux_enabled_(rtcp_mux_enabled) {} - bool rtcp_mux_enabled() const { return rtcp_mux_enabled_; } + bool rtcp_mux_enabled() const override { return rtcp_mux_enabled_; } void SetRtcpMuxEnabled(bool enable) override; rtc::PacketTransportInternal* rtp_packet_transport() const override { diff --git a/pc/rtptransportinternal.h b/pc/rtptransportinternal.h index 04d2ef39da..afede1ea3a 100644 --- a/pc/rtptransportinternal.h +++ b/pc/rtptransportinternal.h @@ -38,6 +38,7 @@ class RtpTransportInternal : public RtpTransportInterface, // TODO(zstein): Remove PacketTransport setters. Clients should pass these // in to constructors instead and construct a new RtpTransportInternal instead // of updating them. + virtual bool rtcp_mux_enabled() const = 0; virtual rtc::PacketTransportInternal* rtp_packet_transport() const = 0; virtual void SetRtpPacketTransport(rtc::PacketTransportInternal* rtp) = 0; @@ -56,12 +57,18 @@ class RtpTransportInternal : public RtpTransportInterface, sigslot::signal3 SignalPacketReceived; + // Called whenever a transport's writable state might change. The argument is + // true if the transport is writable, otherwise it is false. + sigslot::signal1 SignalWritableState; + // Called whenever the network route of the P2P layer transport changes. // The argument is an optional network route. sigslot::signal1> SignalNetworkRouteChanged; virtual bool IsWritable(bool rtcp) const = 0; + // TODO(zhihuang): Pass the |packet| by copy so that the original data + // wouldn't be modified. virtual bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet, const rtc::PacketOptions& options, int flags) = 0; diff --git a/pc/rtptransportinternaladapter.h b/pc/rtptransportinternaladapter.h new file mode 100644 index 0000000000..402f9cf49b --- /dev/null +++ b/pc/rtptransportinternaladapter.h @@ -0,0 +1,102 @@ +/* + * 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_RTPTRANSPORTINTERNALADAPTER_H_ +#define PC_RTPTRANSPORTINTERNALADAPTER_H_ + +#include +#include + +#include "pc/rtptransportinternal.h" + +namespace webrtc { + +// This class is used by SrtpTransport and DtlsSrtpTransport in order to reduce +// the duplicated code. Using this class, different subclasses can override only +// part of RtpTransportInternal methods without implementing all the common +// methods. +class RtpTransportInternalAdapter : public RtpTransportInternal { + public: + explicit RtpTransportInternalAdapter(RtpTransportInternal* transport) + : transport_(transport) { + RTC_DCHECK(transport_); + } + + void SetRtcpMuxEnabled(bool enable) override { + transport_->SetRtcpMuxEnabled(enable); + } + + bool rtcp_mux_enabled() const override { + return transport_->rtcp_mux_enabled(); + } + + rtc::PacketTransportInternal* rtp_packet_transport() const override { + return transport_->rtp_packet_transport(); + } + void SetRtpPacketTransport(rtc::PacketTransportInternal* rtp) override { + transport_->SetRtpPacketTransport(rtp); + } + + rtc::PacketTransportInternal* rtcp_packet_transport() const override { + return transport_->rtcp_packet_transport(); + } + void SetRtcpPacketTransport(rtc::PacketTransportInternal* rtcp) override { + transport_->SetRtcpPacketTransport(rtcp); + } + + bool IsWritable(bool rtcp) const override { + return transport_->IsWritable(rtcp); + } + + bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet, + const rtc::PacketOptions& options, + int flags) override { + return transport_->SendRtpPacket(packet, options, flags); + } + + bool SendRtcpPacket(rtc::CopyOnWriteBuffer* packet, + const rtc::PacketOptions& options, + int flags) override { + return transport_->SendRtcpPacket(packet, options, flags); + } + + bool HandlesPayloadType(int payload_type) const override { + return transport_->HandlesPayloadType(payload_type); + } + + void AddHandledPayloadType(int payload_type) override { + return transport_->AddHandledPayloadType(payload_type); + } + + // RtpTransportInterface overrides. + PacketTransportInterface* GetRtpPacketTransport() const override { + return transport_->GetRtpPacketTransport(); + } + + PacketTransportInterface* GetRtcpPacketTransport() const override { + return transport_->GetRtcpPacketTransport(); + } + + RTCError SetParameters(const RtpTransportParameters& parameters) override { + return transport_->SetParameters(parameters); + } + + RtpTransportParameters GetParameters() const override { + return transport_->GetParameters(); + } + + protected: + // Owned by the subclasses. + RtpTransportInternal* transport_; +}; + +} // namespace webrtc + +#endif // PC_RTPTRANSPORTINTERNALADAPTER_H_ diff --git a/pc/srtptransport.cc b/pc/srtptransport.cc index bb42ad4d3c..98a3beab30 100644 --- a/pc/srtptransport.cc +++ b/pc/srtptransport.cc @@ -26,14 +26,21 @@ namespace webrtc { SrtpTransport::SrtpTransport(bool rtcp_mux_enabled, const std::string& content_name) - : content_name_(content_name), - rtp_transport_(rtc::MakeUnique(rtcp_mux_enabled)) { + : RtpTransportInternalAdapter(new RtpTransport(rtcp_mux_enabled)), + content_name_(content_name) { + // Own the raw pointer |transport| from the base class. + rtp_transport_.reset(transport_); + RTC_DCHECK(rtp_transport_); ConnectToRtpTransport(); } -SrtpTransport::SrtpTransport(std::unique_ptr transport, - const std::string& content_name) - : content_name_(content_name), rtp_transport_(std::move(transport)) { +SrtpTransport::SrtpTransport( + std::unique_ptr rtp_transport, + const std::string& content_name) + : RtpTransportInternalAdapter(rtp_transport.get()), + content_name_(content_name), + rtp_transport_(std::move(rtp_transport)) { + RTC_DCHECK(rtp_transport_); ConnectToRtpTransport(); } diff --git a/pc/srtptransport.h b/pc/srtptransport.h index 919d4b95d5..8704351a3e 100644 --- a/pc/srtptransport.h +++ b/pc/srtptransport.h @@ -17,7 +17,7 @@ #include #include "p2p/base/icetransportinternal.h" -#include "pc/rtptransportinternal.h" +#include "pc/rtptransportinternaladapter.h" #include "pc/srtpfilter.h" #include "pc/srtpsession.h" #include "rtc_base/checks.h" @@ -26,40 +26,13 @@ namespace webrtc { // This class will eventually be a wrapper around RtpTransportInternal // that protects and unprotects sent and received RTP packets. -class SrtpTransport : public RtpTransportInternal { +class SrtpTransport : public RtpTransportInternalAdapter { public: SrtpTransport(bool rtcp_mux_enabled, const std::string& content_name); - SrtpTransport(std::unique_ptr transport, + SrtpTransport(std::unique_ptr rtp_transport, const std::string& content_name); - void SetRtcpMuxEnabled(bool enable) override { - rtp_transport_->SetRtcpMuxEnabled(enable); - } - - rtc::PacketTransportInternal* rtp_packet_transport() const override { - return rtp_transport_->rtp_packet_transport(); - } - - void SetRtpPacketTransport(rtc::PacketTransportInternal* rtp) override { - rtp_transport_->SetRtpPacketTransport(rtp); - } - - PacketTransportInterface* GetRtpPacketTransport() const override { - return rtp_transport_->GetRtpPacketTransport(); - } - - rtc::PacketTransportInternal* rtcp_packet_transport() const override { - return rtp_transport_->rtcp_packet_transport(); - } - void SetRtcpPacketTransport(rtc::PacketTransportInternal* rtcp) override { - rtp_transport_->SetRtcpPacketTransport(rtcp); - } - - PacketTransportInterface* GetRtcpPacketTransport() const override { - return rtp_transport_->GetRtcpPacketTransport(); - } - bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet, const rtc::PacketOptions& options, int flags) override; @@ -68,30 +41,10 @@ class SrtpTransport : public RtpTransportInternal { const rtc::PacketOptions& options, int flags) override; - bool IsWritable(bool rtcp) const override { - return rtp_transport_->IsWritable(rtcp); - } - // The transport becomes active if the send_session_ and recv_session_ are // created. bool IsActive() const; - bool HandlesPayloadType(int payload_type) const override { - return rtp_transport_->HandlesPayloadType(payload_type); - } - - void AddHandledPayloadType(int payload_type) override { - rtp_transport_->AddHandledPayloadType(payload_type); - } - - RTCError SetParameters(const RtpTransportParameters& parameters) override { - return rtp_transport_->SetParameters(parameters); - } - - RtpTransportParameters GetParameters() const override { - return rtp_transport_->GetParameters(); - } - // TODO(zstein): Remove this when we remove RtpTransportAdapter. RtpTransportAdapter* GetInternal() override { return nullptr; } diff --git a/pc/srtptransport_unittest.cc b/pc/srtptransport_unittest.cc index 3533863852..d93ea131e2 100644 --- a/pc/srtptransport_unittest.cc +++ b/pc/srtptransport_unittest.cc @@ -304,9 +304,9 @@ class SrtpTransportTest : public testing::Test, public sigslot::has_slots<> { int key2_len, const std::string& cs_name) { std::vector encrypted_headers; - encrypted_headers.push_back(1); + encrypted_headers.push_back(kHeaderExtensionIDs[0]); // Don't encrypt header ids 2 and 3. - encrypted_headers.push_back(4); + encrypted_headers.push_back(kHeaderExtensionIDs[1]); EXPECT_EQ(key1_len, key2_len); EXPECT_EQ(cs_name, rtc::SrtpCryptoSuiteToName(cs)); EXPECT_TRUE(srtp_transport1_->SetRtpParams(cs, key1, key1_len,