/* * 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. */ // This file is intended for PeerConnection integration tests that are // slow to execute (currently defined as more than 5 seconds per test). #include #include #include #include #include #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/dtmf_sender_interface.h" #include "api/peer_connection_interface.h" #include "api/rtp_receiver_interface.h" #include "api/scoped_refptr.h" #include "api/units/time_delta.h" #include "p2p/base/port_allocator.h" #include "p2p/base/port_interface.h" #include "p2p/base/stun_server.h" #include "p2p/base/test_stun_server.h" #include "pc/test/integration_test_helpers.h" #include "pc/test/mock_peer_connection_observers.h" #include "rtc_base/fake_clock.h" #include "rtc_base/fake_network.h" #include "rtc_base/firewall_socket_server.h" #include "rtc_base/gunit.h" #include "rtc_base/logging.h" #include "rtc_base/socket_address.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/test_certificate_verifier.h" #include "test/gmock.h" #include "test/gtest.h" namespace webrtc { namespace { class PeerConnectionIntegrationTest : public PeerConnectionIntegrationBaseTest, public ::testing::WithParamInterface { protected: PeerConnectionIntegrationTest() : PeerConnectionIntegrationBaseTest(GetParam()) {} }; // Fake clock must be set before threads are started to prevent race on // Set/GetClockForTesting(). // To achieve that, multiple inheritance is used as a mixin pattern // where order of construction is finely controlled. // This also ensures peerconnection is closed before switching back to non-fake // clock, avoiding other races and DCHECK failures such as in rtp_sender.cc. class FakeClockForTest : public rtc::ScopedFakeClock { protected: FakeClockForTest() { // Some things use a time of "0" as a special value, so we need to start out // the fake clock at a nonzero time. // TODO(deadbeef): Fix this. AdvanceTime(TimeDelta::Seconds(1000)); } // Explicit handle. ScopedFakeClock& FakeClock() { return *this; } }; // Ensure FakeClockForTest is constructed first (see class for rationale). class PeerConnectionIntegrationTestWithFakeClock : public FakeClockForTest, public PeerConnectionIntegrationTest {}; class PeerConnectionIntegrationTestPlanB : public PeerConnectionIntegrationBaseTest { protected: PeerConnectionIntegrationTestPlanB() : PeerConnectionIntegrationBaseTest(SdpSemantics::kPlanB_DEPRECATED) {} }; class PeerConnectionIntegrationTestUnifiedPlan : public PeerConnectionIntegrationBaseTest { protected: PeerConnectionIntegrationTestUnifiedPlan() : PeerConnectionIntegrationBaseTest(SdpSemantics::kUnifiedPlan) {} }; // Test the OnFirstPacketReceived callback from audio/video RtpReceivers. This // includes testing that the callback is invoked if an observer is connected // after the first packet has already been received. TEST_P(PeerConnectionIntegrationTest, RtpReceiverObserverOnFirstPacketReceived) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); callee()->AddAudioVideoTracks(); // Start offer/answer exchange and wait for it to complete. caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Should be one receiver each for audio/video. EXPECT_EQ(2U, caller()->rtp_receiver_observers().size()); EXPECT_EQ(2U, callee()->rtp_receiver_observers().size()); // Wait for all "first packet received" callbacks to be fired. EXPECT_TRUE_WAIT( absl::c_all_of(caller()->rtp_receiver_observers(), [](const std::unique_ptr& o) { return o->first_packet_received(); }), kMaxWaitForFramesMs); EXPECT_TRUE_WAIT( absl::c_all_of(callee()->rtp_receiver_observers(), [](const std::unique_ptr& o) { return o->first_packet_received(); }), kMaxWaitForFramesMs); // If new observers are set after the first packet was already received, the // callback should still be invoked. caller()->ResetRtpReceiverObservers(); callee()->ResetRtpReceiverObservers(); EXPECT_EQ(2U, caller()->rtp_receiver_observers().size()); EXPECT_EQ(2U, callee()->rtp_receiver_observers().size()); EXPECT_TRUE( absl::c_all_of(caller()->rtp_receiver_observers(), [](const std::unique_ptr& o) { return o->first_packet_received(); })); EXPECT_TRUE( absl::c_all_of(callee()->rtp_receiver_observers(), [](const std::unique_ptr& o) { return o->first_packet_received(); })); } class DummyDtmfObserver : public DtmfSenderObserverInterface { public: DummyDtmfObserver() : completed_(false) {} // Implements DtmfSenderObserverInterface. void OnToneChange(const std::string& tone) override { tones_.push_back(tone); if (tone.empty()) { completed_ = true; } } const std::vector& tones() const { return tones_; } bool completed() const { return completed_; } private: bool completed_; std::vector tones_; }; TEST_P(PeerConnectionIntegrationTest, SSLCertificateVerifierFailureUsedForTurnConnectionsFailsConnection) { static const rtc::SocketAddress turn_server_internal_address{"88.88.88.0", 3478}; static const rtc::SocketAddress turn_server_external_address{"88.88.88.1", 0}; // Enable TCP-TLS for the fake turn server. We need to pass in 88.88.88.0 so // that host name verification passes on the fake certificate. CreateTurnServer(turn_server_internal_address, turn_server_external_address, cricket::PROTO_TLS, "88.88.88.0"); PeerConnectionInterface::IceServer ice_server; ice_server.urls.push_back("turns:88.88.88.0:3478?transport=tcp"); ice_server.username = "test"; ice_server.password = "test"; PeerConnectionInterface::RTCConfiguration client_1_config; client_1_config.servers.push_back(ice_server); client_1_config.type = PeerConnectionInterface::kRelay; PeerConnectionInterface::RTCConfiguration client_2_config; client_2_config.servers.push_back(ice_server); // Setting the type to kRelay forces the connection to go through a TURN // server. client_2_config.type = PeerConnectionInterface::kRelay; // Get a copy to the pointer so we can verify calls later. rtc::TestCertificateVerifier* client_1_cert_verifier = new rtc::TestCertificateVerifier(); client_1_cert_verifier->verify_certificate_ = false; rtc::TestCertificateVerifier* client_2_cert_verifier = new rtc::TestCertificateVerifier(); client_2_cert_verifier->verify_certificate_ = false; // Create the dependencies with the test certificate verifier. PeerConnectionDependencies client_1_deps(nullptr); client_1_deps.tls_cert_verifier = std::unique_ptr(client_1_cert_verifier); PeerConnectionDependencies client_2_deps(nullptr); client_2_deps.tls_cert_verifier = std::unique_ptr(client_2_cert_verifier); ASSERT_TRUE(CreatePeerConnectionWrappersWithConfigAndDeps( client_1_config, std::move(client_1_deps), client_2_config, std::move(client_2_deps))); ConnectFakeSignaling(); // Set "offer to receive audio/video" without adding any tracks, so we just // set up ICE/DTLS with no media. PeerConnectionInterface::RTCOfferAnswerOptions options; options.offer_to_receive_audio = 1; options.offer_to_receive_video = 1; caller()->SetOfferAnswerOptions(options); caller()->CreateAndSetAndSignalOffer(); bool wait_res = true; // TODO(bugs.webrtc.org/9219): When IceConnectionState is implemented // properly, should be able to just wait for a state of "failed" instead of // waiting a fixed 10 seconds. WAIT_(DtlsConnected(), kDefaultTimeout, wait_res); ASSERT_FALSE(wait_res); EXPECT_GT(client_1_cert_verifier->call_count_, 0u); EXPECT_GT(client_2_cert_verifier->call_count_, 0u); } // Test that we can get capture start ntp time. TEST_P(PeerConnectionIntegrationTest, GetCaptureStartNtpTimeWithOldStatsApi) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioTrack(); callee()->AddAudioTrack(); // Do offer/answer, wait for the callee to receive some frames. caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Get the remote audio track created on the receiver, so they can be used as // GetStats filters. auto receivers = callee()->pc()->GetReceivers(); ASSERT_EQ(1u, receivers.size()); auto remote_audio_track = receivers[0]->track(); // Get the audio output level stats. Note that the level is not available // until an RTCP packet has been received. EXPECT_TRUE_WAIT(callee()->OldGetStatsForTrack(remote_audio_track.get()) ->CaptureStartNtpTime() > 0, 2 * kMaxWaitForFramesMs); } // Test that firewalling the ICE connection causes the clients to identify the // disconnected state and then removing the firewall causes them to reconnect. class PeerConnectionIntegrationIceStatesTest : public PeerConnectionIntegrationBaseTest, public ::testing::WithParamInterface< std::tuple>> { protected: PeerConnectionIntegrationIceStatesTest() : PeerConnectionIntegrationBaseTest(std::get<0>(GetParam())) { port_allocator_flags_ = std::get<1>(std::get<1>(GetParam())); } void StartStunServer(const SocketAddress& server_address) { stun_server_ = cricket::TestStunServer::Create(firewall(), server_address, *network_thread()); } bool TestIPv6() { return (port_allocator_flags_ & cricket::PORTALLOCATOR_ENABLE_IPV6); } void SetPortAllocatorFlags() { PeerConnectionIntegrationBaseTest::SetPortAllocatorFlags( port_allocator_flags_, port_allocator_flags_); } std::vector CallerAddresses() { std::vector addresses; addresses.push_back(SocketAddress("1.1.1.1", 0)); if (TestIPv6()) { addresses.push_back(SocketAddress("1111:0:a:b:c:d:e:f", 0)); } return addresses; } std::vector CalleeAddresses() { std::vector addresses; addresses.push_back(SocketAddress("2.2.2.2", 0)); if (TestIPv6()) { addresses.push_back(SocketAddress("2222:0:a:b:c:d:e:f", 0)); } return addresses; } void SetUpNetworkInterfaces() { // Remove the default interfaces added by the test infrastructure. caller()->network_manager()->RemoveInterface(kDefaultLocalAddress); callee()->network_manager()->RemoveInterface(kDefaultLocalAddress); // Add network addresses for test. for (const auto& caller_address : CallerAddresses()) { caller()->network_manager()->AddInterface(caller_address); } for (const auto& callee_address : CalleeAddresses()) { callee()->network_manager()->AddInterface(callee_address); } } private: uint32_t port_allocator_flags_; cricket::TestStunServer::StunServerPtr stun_server_; }; // Ensure FakeClockForTest is constructed first (see class for rationale). class PeerConnectionIntegrationIceStatesTestWithFakeClock : public FakeClockForTest, public PeerConnectionIntegrationIceStatesTest {}; #if !defined(THREAD_SANITIZER) // This test provokes TSAN errors. bugs.webrtc.org/11282 // Tests that the PeerConnection goes through all the ICE gathering/connection // states over the duration of the call. This includes Disconnected and Failed // states, induced by putting a firewall between the peers and waiting for them // to time out. TEST_P(PeerConnectionIntegrationIceStatesTestWithFakeClock, VerifyIceStates) { const SocketAddress kStunServerAddress = SocketAddress("99.99.99.1", cricket::STUN_SERVER_PORT); StartStunServer(kStunServerAddress); PeerConnectionInterface::RTCConfiguration config; PeerConnectionInterface::IceServer ice_stun_server; ice_stun_server.urls.push_back( "stun:" + kStunServerAddress.HostAsURIString() + ":" + kStunServerAddress.PortAsString()); config.servers.push_back(ice_stun_server); ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config)); ConnectFakeSignaling(); SetPortAllocatorFlags(); SetUpNetworkInterfaces(); caller()->AddAudioVideoTracks(); callee()->AddAudioVideoTracks(); // Initial state before anything happens. ASSERT_EQ(PeerConnectionInterface::kIceGatheringNew, caller()->ice_gathering_state()); ASSERT_EQ(PeerConnectionInterface::kIceConnectionNew, caller()->ice_connection_state()); ASSERT_EQ(PeerConnectionInterface::kIceConnectionNew, caller()->standardized_ice_connection_state()); // Start the call by creating the offer, setting it as the local description, // then sending it to the peer who will respond with an answer. This happens // asynchronously so that we can watch the states as it runs in the // background. caller()->CreateAndSetAndSignalOffer(); ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionCompleted, caller()->ice_connection_state(), kDefaultTimeout, FakeClock()); ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionCompleted, caller()->standardized_ice_connection_state(), kDefaultTimeout, FakeClock()); // Verify that the observer was notified of the intermediate transitions. EXPECT_THAT(caller()->ice_connection_state_history(), ElementsAre(PeerConnectionInterface::kIceConnectionChecking, PeerConnectionInterface::kIceConnectionConnected, PeerConnectionInterface::kIceConnectionCompleted)); EXPECT_THAT(caller()->standardized_ice_connection_state_history(), ElementsAre(PeerConnectionInterface::kIceConnectionChecking, PeerConnectionInterface::kIceConnectionConnected, PeerConnectionInterface::kIceConnectionCompleted)); EXPECT_THAT( caller()->peer_connection_state_history(), ElementsAre(PeerConnectionInterface::PeerConnectionState::kConnecting, PeerConnectionInterface::PeerConnectionState::kConnected)); EXPECT_THAT(caller()->ice_gathering_state_history(), ElementsAre(PeerConnectionInterface::kIceGatheringGathering, PeerConnectionInterface::kIceGatheringComplete)); // Block connections to/from the caller and wait for ICE to become // disconnected. for (const auto& caller_address : CallerAddresses()) { firewall()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, caller_address); } RTC_LOG(LS_INFO) << "Firewall rules applied"; ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionDisconnected, caller()->ice_connection_state(), kDefaultTimeout, FakeClock()); ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionDisconnected, caller()->standardized_ice_connection_state(), kDefaultTimeout, FakeClock()); // Let ICE re-establish by removing the firewall rules. firewall()->ClearRules(); RTC_LOG(LS_INFO) << "Firewall rules cleared"; ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionCompleted, caller()->ice_connection_state(), kDefaultTimeout, FakeClock()); ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionCompleted, caller()->standardized_ice_connection_state(), kDefaultTimeout, FakeClock()); // According to RFC7675, if there is no response within 30 seconds then the // peer should consider the other side to have rejected the connection. This // is signaled by the state transitioning to "failed". constexpr int kConsentTimeout = 30000; for (const auto& caller_address : CallerAddresses()) { firewall()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, caller_address); } RTC_LOG(LS_INFO) << "Firewall rules applied again"; ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionFailed, caller()->ice_connection_state(), kConsentTimeout, FakeClock()); ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionFailed, caller()->standardized_ice_connection_state(), kConsentTimeout, FakeClock()); } #endif // This test sets up a call that's transferred to a new caller with a different // DTLS fingerprint. TEST_P(PeerConnectionIntegrationTest, CallTransferredForCallee) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); callee()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Keep the original peer around which will still send packets to the // receiving client. These SRTP packets will be dropped. std::unique_ptr original_peer( SetCallerPcWrapperAndReturnCurrent( CreatePeerConnectionWrapperWithAlternateKey().release())); // TODO(deadbeef): Why do we call Close here? That goes against the comment // directly above. original_peer->pc()->Close(); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Wait for some additional frames to be transmitted end-to-end. MediaExpectations media_expectations; media_expectations.ExpectBidirectionalAudioAndVideo(); ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // This test sets up a call that's transferred to a new callee with a different // DTLS fingerprint. TEST_P(PeerConnectionIntegrationTest, CallTransferredForCaller) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); callee()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Keep the original peer around which will still send packets to the // receiving client. These SRTP packets will be dropped. std::unique_ptr original_peer( SetCalleePcWrapperAndReturnCurrent( CreatePeerConnectionWrapperWithAlternateKey().release())); // TODO(deadbeef): Why do we call Close here? That goes against the comment // directly above. original_peer->pc()->Close(); ConnectFakeSignaling(); callee()->AddAudioVideoTracks(); caller()->SetOfferAnswerOptions(IceRestartOfferAnswerOptions()); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Wait for some additional frames to be transmitted end-to-end. MediaExpectations media_expectations; media_expectations.ExpectBidirectionalAudioAndVideo(); ASSERT_TRUE(ExpectNewFrames(media_expectations)); } INSTANTIATE_TEST_SUITE_P(PeerConnectionIntegrationTest, PeerConnectionIntegrationTest, Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan)); constexpr uint32_t kFlagsIPv4NoStun = cricket::PORTALLOCATOR_DISABLE_TCP | cricket::PORTALLOCATOR_DISABLE_STUN | cricket::PORTALLOCATOR_DISABLE_RELAY; constexpr uint32_t kFlagsIPv6NoStun = cricket::PORTALLOCATOR_DISABLE_TCP | cricket::PORTALLOCATOR_DISABLE_STUN | cricket::PORTALLOCATOR_ENABLE_IPV6 | cricket::PORTALLOCATOR_DISABLE_RELAY; constexpr uint32_t kFlagsIPv4Stun = cricket::PORTALLOCATOR_DISABLE_TCP | cricket::PORTALLOCATOR_DISABLE_RELAY; INSTANTIATE_TEST_SUITE_P( PeerConnectionIntegrationTest, PeerConnectionIntegrationIceStatesTestWithFakeClock, Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), Values(std::make_pair("IPv4 no STUN", kFlagsIPv4NoStun), std::make_pair("IPv6 no STUN", kFlagsIPv6NoStun), std::make_pair("IPv4 with STUN", kFlagsIPv4Stun)))); } // namespace } // namespace webrtc