/* * Copyright 2022 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 "media/sctp/dcsctp_transport.h" #include #include #include #include "api/environment/environment.h" #include "api/environment/environment_factory.h" #include "api/priority.h" #include "api/rtc_error.h" #include "api/transport/data_channel_transport_interface.h" #include "net/dcsctp/public/dcsctp_message.h" #include "net/dcsctp/public/dcsctp_options.h" #include "net/dcsctp/public/dcsctp_socket.h" #include "net/dcsctp/public/mock_dcsctp_socket.h" #include "net/dcsctp/public/mock_dcsctp_socket_factory.h" #include "net/dcsctp/public/types.h" #include "p2p/dtls/fake_dtls_transport.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/thread.h" #include "system_wrappers/include/clock.h" #include "test/gmock.h" #include "test/gtest.h" using ::testing::_; using ::testing::ByMove; using ::testing::ElementsAre; using ::testing::InSequence; using ::testing::Invoke; using ::testing::NiceMock; using ::testing::Return; using ::testing::ReturnPointee; namespace webrtc { namespace { constexpr char kTransportName[] = "transport"; constexpr int kComponent = 77; const PriorityValue kDefaultPriority = PriorityValue(Priority::kLow); class MockDataChannelSink : public DataChannelSink { public: MOCK_METHOD(void, OnConnected, ()); // DataChannelSink MOCK_METHOD(void, OnDataReceived, (int, DataMessageType, const CopyOnWriteBuffer&)); MOCK_METHOD(void, OnChannelClosing, (int)); MOCK_METHOD(void, OnChannelClosed, (int)); MOCK_METHOD(void, OnReadyToSend, ()); MOCK_METHOD(void, OnTransportClosed, (RTCError)); MOCK_METHOD(void, OnBufferedAmountLow, (int channel_id), (override)); }; static_assert(!std::is_abstract_v); class Peer { public: Peer() : fake_dtls_transport_(kTransportName, kComponent), simulated_clock_(1000), env_(CreateEnvironment(&simulated_clock_)) { auto socket_ptr = std::make_unique(); socket_ = socket_ptr.get(); auto mock_dcsctp_socket_factory = std::make_unique(); EXPECT_CALL(*mock_dcsctp_socket_factory, Create) .Times(1) .WillOnce(Return(ByMove(std::move(socket_ptr)))); sctp_transport_ = std::make_unique( env_, Thread::Current(), &fake_dtls_transport_, std::move(mock_dcsctp_socket_factory)); sctp_transport_->SetDataChannelSink(&sink_); sctp_transport_->SetOnConnectedCallback([this]() { sink_.OnConnected(); }); } FakeDtlsTransport fake_dtls_transport_; SimulatedClock simulated_clock_; Environment env_; dcsctp::MockDcSctpSocket* socket_; std::unique_ptr sctp_transport_; NiceMock sink_; }; } // namespace TEST(DcSctpTransportTest, OpenSequence) { AutoThread main_thread; Peer peer_a; peer_a.fake_dtls_transport_.SetWritable(true); EXPECT_CALL(*peer_a.socket_, Connect) .Times(1) .WillOnce(Invoke(peer_a.sctp_transport_.get(), &dcsctp::DcSctpSocketCallbacks::OnConnected)); EXPECT_CALL(peer_a.sink_, OnReadyToSend); EXPECT_CALL(peer_a.sink_, OnConnected); peer_a.sctp_transport_->Start({.local_port = 5000, .remote_port = 5000, .max_message_size = 256 * 1024}); } // Tests that the close sequence invoked from one end results in the stream to // be reset from both ends and all the proper signals are sent. TEST(DcSctpTransportTest, CloseSequence) { AutoThread main_thread; Peer peer_a; Peer peer_b; peer_a.fake_dtls_transport_.SetDestination(&peer_b.fake_dtls_transport_, false); { InSequence sequence; EXPECT_CALL( *peer_a.socket_, SetStreamPriority(dcsctp::StreamID(1), dcsctp::StreamPriority(kDefaultPriority.value()))); EXPECT_CALL(*peer_a.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1)))) .WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed)); EXPECT_CALL(*peer_b.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1)))) .WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed)); EXPECT_CALL(peer_a.sink_, OnChannelClosing(1)).Times(0); EXPECT_CALL(peer_b.sink_, OnChannelClosing(1)); EXPECT_CALL(peer_a.sink_, OnChannelClosed(1)); EXPECT_CALL(peer_b.sink_, OnChannelClosed(1)); } peer_a.sctp_transport_->Start({.local_port = 5000, .remote_port = 5000, .max_message_size = 256 * 1024}); peer_b.sctp_transport_->Start({.local_port = 5000, .remote_port = 5000, .max_message_size = 256 * 1024}); peer_a.sctp_transport_->OpenStream(1, kDefaultPriority); peer_b.sctp_transport_->OpenStream(1, kDefaultPriority); peer_a.sctp_transport_->ResetStream(1); // Simulate the callbacks from the stream resets dcsctp::StreamID streams[1] = {dcsctp::StreamID(1)}; static_cast(peer_a.sctp_transport_.get()) ->OnStreamsResetPerformed(streams); static_cast(peer_b.sctp_transport_.get()) ->OnIncomingStreamsReset(streams); static_cast(peer_a.sctp_transport_.get()) ->OnIncomingStreamsReset(streams); static_cast(peer_b.sctp_transport_.get()) ->OnStreamsResetPerformed(streams); } // Tests that the close sequence initiated from both peers at the same time // terminates properly. Both peers will think they initiated it, so no // OnClosingProcedureStartedRemotely should be called. TEST(DcSctpTransportTest, CloseSequenceSimultaneous) { AutoThread main_thread; Peer peer_a; Peer peer_b; peer_a.fake_dtls_transport_.SetDestination(&peer_b.fake_dtls_transport_, false); { InSequence sequence; EXPECT_CALL(*peer_a.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1)))) .WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed)); EXPECT_CALL(*peer_b.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1)))) .WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed)); EXPECT_CALL(peer_a.sink_, OnChannelClosing(1)).Times(0); EXPECT_CALL(peer_b.sink_, OnChannelClosing(1)).Times(0); EXPECT_CALL(peer_a.sink_, OnChannelClosed(1)); EXPECT_CALL(peer_b.sink_, OnChannelClosed(1)); } peer_a.sctp_transport_->Start({.local_port = 5000, .remote_port = 5000, .max_message_size = 256 * 1024}); peer_b.sctp_transport_->Start({.local_port = 5000, .remote_port = 5000, .max_message_size = 256 * 1024}); peer_a.sctp_transport_->OpenStream(1, kDefaultPriority); peer_b.sctp_transport_->OpenStream(1, kDefaultPriority); peer_a.sctp_transport_->ResetStream(1); peer_b.sctp_transport_->ResetStream(1); // Simulate the callbacks from the stream resets dcsctp::StreamID streams[1] = {dcsctp::StreamID(1)}; static_cast(peer_a.sctp_transport_.get()) ->OnStreamsResetPerformed(streams); static_cast(peer_b.sctp_transport_.get()) ->OnStreamsResetPerformed(streams); static_cast(peer_a.sctp_transport_.get()) ->OnIncomingStreamsReset(streams); static_cast(peer_b.sctp_transport_.get()) ->OnIncomingStreamsReset(streams); } TEST(DcSctpTransportTest, SetStreamPriority) { AutoThread main_thread; Peer peer_a; { InSequence sequence; EXPECT_CALL( *peer_a.socket_, SetStreamPriority(dcsctp::StreamID(1), dcsctp::StreamPriority(1337))); EXPECT_CALL( *peer_a.socket_, SetStreamPriority(dcsctp::StreamID(2), dcsctp::StreamPriority(3141))); } EXPECT_CALL(*peer_a.socket_, Send(_, _)).Times(0); peer_a.sctp_transport_->OpenStream(1, PriorityValue(1337)); peer_a.sctp_transport_->Start({.local_port = 5000, .remote_port = 5000, .max_message_size = 256 * 1024}); peer_a.sctp_transport_->OpenStream(2, PriorityValue(3141)); } TEST(DcSctpTransportTest, DiscardMessageClosedChannel) { AutoThread main_thread; Peer peer_a; EXPECT_CALL(*peer_a.socket_, Send(_, _)).Times(0); peer_a.sctp_transport_->Start({.local_port = 5000, .remote_port = 5000, .max_message_size = 256 * 1024}); SendDataParams params; CopyOnWriteBuffer payload; EXPECT_EQ(peer_a.sctp_transport_->SendData(1, params, payload).type(), RTCErrorType::INVALID_STATE); } TEST(DcSctpTransportTest, DiscardMessageClosingChannel) { AutoThread main_thread; Peer peer_a; EXPECT_CALL(*peer_a.socket_, Send(_, _)).Times(0); peer_a.sctp_transport_->OpenStream(1, kDefaultPriority); peer_a.sctp_transport_->Start({.local_port = 5000, .remote_port = 5000, .max_message_size = 256 * 1024}); peer_a.sctp_transport_->ResetStream(1); SendDataParams params; CopyOnWriteBuffer payload; EXPECT_EQ(peer_a.sctp_transport_->SendData(1, params, payload).type(), RTCErrorType::INVALID_STATE); } TEST(DcSctpTransportTest, SendDataOpenChannel) { AutoThread main_thread; Peer peer_a; dcsctp::DcSctpOptions options; EXPECT_CALL(*peer_a.socket_, Send(_, _)).Times(1); EXPECT_CALL(*peer_a.socket_, options()).WillOnce(ReturnPointee(&options)); peer_a.sctp_transport_->OpenStream(1, kDefaultPriority); peer_a.sctp_transport_->Start({.local_port = 5000, .remote_port = 5000, .max_message_size = 256 * 1024}); SendDataParams params; CopyOnWriteBuffer payload; EXPECT_TRUE(peer_a.sctp_transport_->SendData(1, params, payload).ok()); } TEST(DcSctpTransportTest, DeliversMessage) { AutoThread main_thread; Peer peer_a; EXPECT_CALL(peer_a.sink_, OnDataReceived(1, DataMessageType::kBinary, _)) .Times(1); peer_a.sctp_transport_->OpenStream(1, kDefaultPriority); peer_a.sctp_transport_->Start({.local_port = 5000, .remote_port = 5000, .max_message_size = 256 * 1024}); static_cast(peer_a.sctp_transport_.get()) ->OnMessageReceived( dcsctp::DcSctpMessage(dcsctp::StreamID(1), dcsctp::PPID(53), {0})); } TEST(DcSctpTransportTest, DropMessageWithUnknownPpid) { AutoThread main_thread; Peer peer_a; EXPECT_CALL(peer_a.sink_, OnDataReceived(_, _, _)).Times(0); peer_a.sctp_transport_->OpenStream(1, kDefaultPriority); peer_a.sctp_transport_->Start({.local_port = 5000, .remote_port = 5000, .max_message_size = 256 * 1024}); static_cast(peer_a.sctp_transport_.get()) ->OnMessageReceived( dcsctp::DcSctpMessage(dcsctp::StreamID(1), dcsctp::PPID(1337), {0})); } } // namespace webrtc