/* * Copyright 2011 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 "webrtc/base/fakesslidentity.h" #include "webrtc/base/gunit.h" #include "webrtc/base/network.h" #include "webrtc/p2p/base/faketransportcontroller.h" #include "webrtc/p2p/base/p2ptransport.h" using cricket::Transport; using cricket::FakeTransport; using cricket::TransportChannel; using cricket::FakeTransportChannel; using cricket::IceRole; using cricket::TransportDescription; using rtc::SocketAddress; static const char kIceUfrag1[] = "TESTICEUFRAG0001"; static const char kIcePwd1[] = "TESTICEPWD00000000000001"; static const char kIceUfrag2[] = "TESTICEUFRAG0002"; static const char kIcePwd2[] = "TESTICEPWD00000000000002"; class TransportTest : public testing::Test, public sigslot::has_slots<> { public: TransportTest() : transport_(new FakeTransport("test content name")), channel_(NULL) {} ~TransportTest() { transport_->DestroyAllChannels(); } bool SetupChannel() { channel_ = CreateChannel(1); return (channel_ != NULL); } FakeTransportChannel* CreateChannel(int component) { return static_cast( transport_->CreateChannel(component)); } void DestroyChannel() { transport_->DestroyChannel(1); channel_ = NULL; } protected: std::unique_ptr transport_; FakeTransportChannel* channel_; }; // This test verifies channels are created with proper ICE // role, tiebreaker and remote ice mode and credentials after offer and // answer negotiations. TEST_F(TransportTest, TestChannelIceParameters) { transport_->SetIceRole(cricket::ICEROLE_CONTROLLING); transport_->SetIceTiebreaker(99U); cricket::TransportDescription local_desc(kIceUfrag1, kIcePwd1); ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc, cricket::CA_OFFER, NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role()); EXPECT_TRUE(SetupChannel()); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole()); EXPECT_EQ(cricket::ICEMODE_FULL, channel_->remote_ice_mode()); EXPECT_EQ(kIceUfrag1, channel_->ice_ufrag()); EXPECT_EQ(kIcePwd1, channel_->ice_pwd()); cricket::TransportDescription remote_desc(kIceUfrag1, kIcePwd1); ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc, cricket::CA_ANSWER, NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole()); EXPECT_EQ(99U, channel_->IceTiebreaker()); EXPECT_EQ(cricket::ICEMODE_FULL, channel_->remote_ice_mode()); // Changing the transport role from CONTROLLING to CONTROLLED. transport_->SetIceRole(cricket::ICEROLE_CONTROLLED); EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel_->GetIceRole()); EXPECT_EQ(cricket::ICEMODE_FULL, channel_->remote_ice_mode()); EXPECT_EQ(kIceUfrag1, channel_->remote_ice_ufrag()); EXPECT_EQ(kIcePwd1, channel_->remote_ice_pwd()); } // Verifies that IceCredentialsChanged returns true when either ufrag or pwd // changed, and false in other cases. TEST_F(TransportTest, TestIceCredentialsChanged) { EXPECT_TRUE(cricket::IceCredentialsChanged("u1", "p1", "u2", "p2")); EXPECT_TRUE(cricket::IceCredentialsChanged("u1", "p1", "u2", "p1")); EXPECT_TRUE(cricket::IceCredentialsChanged("u1", "p1", "u1", "p2")); EXPECT_FALSE(cricket::IceCredentialsChanged("u1", "p1", "u1", "p1")); } // This test verifies that the callee's ICE role remains the same when the // callee triggers an ICE restart. // // RFC5245 currently says that the role *should* change on an ICE restart, // but this rule was intended for an ICE restart that occurs when an endpoint // is changing to ICE lite (which we already handle). See discussion here: // https://mailarchive.ietf.org/arch/msg/ice/C0_QRCTNcwtvUF12y28jQicPR10 TEST_F(TransportTest, TestIceControlledToControllingOnIceRestart) { EXPECT_TRUE(SetupChannel()); transport_->SetIceRole(cricket::ICEROLE_CONTROLLED); cricket::TransportDescription desc(kIceUfrag1, kIcePwd1); ASSERT_TRUE(transport_->SetRemoteTransportDescription(desc, cricket::CA_OFFER, NULL)); ASSERT_TRUE(transport_->SetLocalTransportDescription(desc, cricket::CA_ANSWER, NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLED, transport_->ice_role()); cricket::TransportDescription new_local_desc(kIceUfrag2, kIcePwd2); ASSERT_TRUE(transport_->SetLocalTransportDescription(new_local_desc, cricket::CA_OFFER, NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLED, transport_->ice_role()); EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel_->GetIceRole()); } // This test verifies that the caller's ICE role remains the same when the // callee triggers an ICE restart. // // RFC5245 currently says that the role *should* change on an ICE restart, // but this rule was intended for an ICE restart that occurs when an endpoint // is changing to ICE lite (which we already handle). See discussion here: // https://mailarchive.ietf.org/arch/msg/ice/C0_QRCTNcwtvUF12y28jQicPR10 TEST_F(TransportTest, TestIceControllingToControlledOnIceRestart) { EXPECT_TRUE(SetupChannel()); transport_->SetIceRole(cricket::ICEROLE_CONTROLLING); cricket::TransportDescription desc(kIceUfrag1, kIcePwd1); ASSERT_TRUE(transport_->SetLocalTransportDescription(desc, cricket::CA_OFFER, NULL)); ASSERT_TRUE(transport_->SetRemoteTransportDescription(desc, cricket::CA_ANSWER, NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role()); cricket::TransportDescription new_local_desc(kIceUfrag2, kIcePwd2); ASSERT_TRUE(transport_->SetLocalTransportDescription(new_local_desc, cricket::CA_ANSWER, NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role()); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole()); } // This test verifies that the caller's ICE role is still controlling after the // callee triggers ICE restart if the callee's ICE mode is LITE. TEST_F(TransportTest, TestIceControllingOnIceRestartIfRemoteIsIceLite) { EXPECT_TRUE(SetupChannel()); transport_->SetIceRole(cricket::ICEROLE_CONTROLLING); cricket::TransportDescription desc(kIceUfrag1, kIcePwd1); ASSERT_TRUE(transport_->SetLocalTransportDescription(desc, cricket::CA_OFFER, NULL)); cricket::TransportDescription remote_desc( std::vector(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_NONE, NULL); ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc, cricket::CA_ANSWER, NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role()); cricket::TransportDescription new_local_desc(kIceUfrag2, kIcePwd2); ASSERT_TRUE(transport_->SetLocalTransportDescription(new_local_desc, cricket::CA_ANSWER, NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role()); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole()); } // Tests channel role is reversed after receiving ice-lite from remote. TEST_F(TransportTest, TestSetRemoteIceLiteInOffer) { transport_->SetIceRole(cricket::ICEROLE_CONTROLLED); cricket::TransportDescription remote_desc( std::vector(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS, NULL); ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc, cricket::CA_OFFER, NULL)); cricket::TransportDescription local_desc(kIceUfrag1, kIcePwd1); ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc, cricket::CA_ANSWER, NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role()); EXPECT_TRUE(SetupChannel()); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole()); EXPECT_EQ(cricket::ICEMODE_LITE, channel_->remote_ice_mode()); } // Tests ice-lite in remote answer. TEST_F(TransportTest, TestSetRemoteIceLiteInAnswer) { transport_->SetIceRole(cricket::ICEROLE_CONTROLLING); cricket::TransportDescription local_desc(kIceUfrag1, kIcePwd1); ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc, cricket::CA_OFFER, NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role()); EXPECT_TRUE(SetupChannel()); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole()); // Channels will be created in ICEFULL_MODE. EXPECT_EQ(cricket::ICEMODE_FULL, channel_->remote_ice_mode()); cricket::TransportDescription remote_desc( std::vector(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_NONE, NULL); ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc, cricket::CA_ANSWER, NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole()); // After receiving remote description with ICEMODE_LITE, channel should // have mode set to ICEMODE_LITE. EXPECT_EQ(cricket::ICEMODE_LITE, channel_->remote_ice_mode()); } TEST_F(TransportTest, TestGetStats) { EXPECT_TRUE(SetupChannel()); cricket::TransportStats stats; EXPECT_TRUE(transport_->GetStats(&stats)); // Note that this tests the behavior of a FakeTransportChannel. ASSERT_EQ(1U, stats.channel_stats.size()); EXPECT_EQ(1, stats.channel_stats[0].component); // Set local transport description for FakeTransport before connecting. TransportDescription faketransport_desc( std::vector(), rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH), cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE, nullptr); transport_->SetLocalTransportDescription(faketransport_desc, cricket::CA_OFFER, nullptr); transport_->ConnectChannels(); EXPECT_TRUE(transport_->GetStats(&stats)); ASSERT_EQ(1U, stats.channel_stats.size()); EXPECT_EQ(1, stats.channel_stats[0].component); } // Tests that VerifyCertificateFingerprint only returns true when the // certificate matches the fingerprint. TEST_F(TransportTest, TestVerifyCertificateFingerprint) { std::string error_desc; EXPECT_FALSE( transport_->VerifyCertificateFingerprint(nullptr, nullptr, &error_desc)); rtc::KeyType key_types[] = {rtc::KT_RSA, rtc::KT_ECDSA}; for (auto& key_type : key_types) { rtc::scoped_refptr certificate = rtc::RTCCertificate::Create(std::unique_ptr( rtc::SSLIdentity::Generate("testing", key_type))); ASSERT_NE(nullptr, certificate); std::string digest_algorithm; ASSERT_TRUE(certificate->ssl_certificate().GetSignatureDigestAlgorithm( &digest_algorithm)); ASSERT_FALSE(digest_algorithm.empty()); std::unique_ptr good_fingerprint( rtc::SSLFingerprint::Create(digest_algorithm, certificate->identity())); ASSERT_NE(nullptr, good_fingerprint); EXPECT_TRUE(transport_->VerifyCertificateFingerprint( certificate.get(), good_fingerprint.get(), &error_desc)); EXPECT_FALSE(transport_->VerifyCertificateFingerprint( certificate.get(), nullptr, &error_desc)); EXPECT_FALSE(transport_->VerifyCertificateFingerprint( nullptr, good_fingerprint.get(), &error_desc)); rtc::SSLFingerprint bad_fingerprint = *good_fingerprint; bad_fingerprint.digest.AppendData("0", 1); EXPECT_FALSE(transport_->VerifyCertificateFingerprint( certificate.get(), &bad_fingerprint, &error_desc)); } } // Tests that NegotiateRole sets the SSL role correctly. TEST_F(TransportTest, TestNegotiateRole) { TransportDescription local_desc(kIceUfrag1, kIcePwd1); TransportDescription remote_desc(kIceUfrag2, kIcePwd2); struct NegotiateRoleParams { cricket::ConnectionRole local_role; cricket::ConnectionRole remote_role; cricket::ContentAction local_action; cricket::ContentAction remote_action; }; rtc::SSLRole ssl_role; std::string error_desc; // Parameters which set the SSL role to SSL_CLIENT. NegotiateRoleParams valid_client_params[] = { {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTPASS, cricket::CA_ANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTPASS, cricket::CA_PRANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_PASSIVE, cricket::CA_OFFER, cricket::CA_ANSWER}, {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_PASSIVE, cricket::CA_OFFER, cricket::CA_PRANSWER}}; for (auto& param : valid_client_params) { local_desc.connection_role = param.local_role; remote_desc.connection_role = param.remote_role; ASSERT_TRUE(transport_->SetRemoteTransportDescription( remote_desc, param.remote_action, nullptr)); ASSERT_TRUE(transport_->SetLocalTransportDescription( local_desc, param.local_action, nullptr)); EXPECT_TRUE( transport_->NegotiateRole(param.local_action, &ssl_role, &error_desc)); EXPECT_EQ(rtc::SSL_CLIENT, ssl_role); } // Parameters which set the SSL role to SSL_SERVER. NegotiateRoleParams valid_server_params[] = { {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTPASS, cricket::CA_ANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTPASS, cricket::CA_PRANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTIVE, cricket::CA_OFFER, cricket::CA_ANSWER}, {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTIVE, cricket::CA_OFFER, cricket::CA_PRANSWER}}; for (auto& param : valid_server_params) { local_desc.connection_role = param.local_role; remote_desc.connection_role = param.remote_role; ASSERT_TRUE(transport_->SetRemoteTransportDescription( remote_desc, param.remote_action, nullptr)); ASSERT_TRUE(transport_->SetLocalTransportDescription( local_desc, param.local_action, nullptr)); EXPECT_TRUE( transport_->NegotiateRole(param.local_action, &ssl_role, &error_desc)); EXPECT_EQ(rtc::SSL_SERVER, ssl_role); } // Invalid parameters due to both peers having a duplicate role. NegotiateRoleParams duplicate_params[] = { {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTIVE, cricket::CA_ANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTPASS, cricket::CA_ANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_PASSIVE, cricket::CA_ANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTIVE, cricket::CA_PRANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTPASS, cricket::CA_PRANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_PASSIVE, cricket::CA_PRANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTIVE, cricket::CA_OFFER, cricket::CA_ANSWER}, {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTPASS, cricket::CA_OFFER, cricket::CA_ANSWER}, {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_PASSIVE, cricket::CA_OFFER, cricket::CA_ANSWER}, {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTIVE, cricket::CA_OFFER, cricket::CA_PRANSWER}, {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTPASS, cricket::CA_OFFER, cricket::CA_PRANSWER}, {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_PASSIVE, cricket::CA_OFFER, cricket::CA_PRANSWER}}; for (auto& param : duplicate_params) { local_desc.connection_role = param.local_role; remote_desc.connection_role = param.remote_role; ASSERT_TRUE(transport_->SetRemoteTransportDescription( remote_desc, param.remote_action, nullptr)); ASSERT_TRUE(transport_->SetLocalTransportDescription( local_desc, param.local_action, nullptr)); EXPECT_FALSE( transport_->NegotiateRole(param.local_action, &ssl_role, &error_desc)); } // Invalid parameters due to the offerer not using ACTPASS. NegotiateRoleParams offerer_without_actpass_params[] = { {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_PASSIVE, cricket::CA_ANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTIVE, cricket::CA_ANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_PASSIVE, cricket::CA_ANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_PASSIVE, cricket::CA_PRANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTIVE, cricket::CA_PRANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_PASSIVE, cricket::CA_PRANSWER, cricket::CA_OFFER}, {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_PASSIVE, cricket::CA_OFFER, cricket::CA_ANSWER}, {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTIVE, cricket::CA_OFFER, cricket::CA_ANSWER}, {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTPASS, cricket::CA_OFFER, cricket::CA_ANSWER}, {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_PASSIVE, cricket::CA_OFFER, cricket::CA_PRANSWER}, {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTIVE, cricket::CA_OFFER, cricket::CA_PRANSWER}, {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTPASS, cricket::CA_OFFER, cricket::CA_PRANSWER}}; for (auto& param : offerer_without_actpass_params) { local_desc.connection_role = param.local_role; remote_desc.connection_role = param.remote_role; ASSERT_TRUE(transport_->SetRemoteTransportDescription( remote_desc, param.remote_action, nullptr)); ASSERT_TRUE(transport_->SetLocalTransportDescription( local_desc, param.local_action, nullptr)); EXPECT_FALSE( transport_->NegotiateRole(param.local_action, &ssl_role, &error_desc)); } }