Index: webrtc/p2p/base/dtlstransportchannel_unittest.cc |
diff --git a/webrtc/p2p/base/dtlstransportchannel_unittest.cc b/webrtc/p2p/base/dtlstransportchannel_unittest.cc |
index 119d3249f0ba636d113b02a29bb69d7a784aff97..6eb0f0e3f1aa857461a5e3feecf1451956cc0ef6 100644 |
--- a/webrtc/p2p/base/dtlstransportchannel_unittest.cc |
+++ b/webrtc/p2p/base/dtlstransportchannel_unittest.cc |
@@ -81,11 +81,10 @@ |
ASSERT(!transport_); |
ssl_max_version_ = version; |
} |
- void SetupChannels(int count, cricket::IceRole role, int async_delay_ms = 0) { |
+ void SetupChannels(int count, cricket::IceRole role) { |
transport_.reset(new cricket::DtlsTransport<cricket::FakeTransport>( |
"dtls content name", nullptr, certificate_)); |
transport_->SetAsync(true); |
- transport_->SetAsyncDelay(async_delay_ms); |
transport_->SetIceRole(role); |
transport_->SetIceTiebreaker( |
(role == cricket::ICEROLE_CONTROLLING) ? 1 : 2); |
@@ -120,11 +119,6 @@ |
static_cast<cricket::FakeTransportChannel*>(wrapper->channel()) : NULL; |
} |
- cricket::DtlsTransportChannelWrapper* GetDtlsChannel(int component) { |
- cricket::TransportChannelImpl* ch = transport_->GetChannel(component); |
- return static_cast<cricket::DtlsTransportChannelWrapper*>(ch); |
- } |
- |
// Offer DTLS if we have an identity; pass in a remote fingerprint only if |
// both sides support DTLS. |
void Negotiate(DtlsTestClient* peer, cricket::ContentAction action, |
@@ -158,6 +152,7 @@ |
EXPECT_EQ(expect_success, |
transport_->SetLocalTransportDescription( |
MakeTransportDescription(cert, role), action, nullptr)); |
+ set_local_cert_ = (cert != nullptr); |
} |
void SetRemoteTransportDescription( |
@@ -172,6 +167,7 @@ |
EXPECT_EQ(expect_success, |
transport_->SetRemoteTransportDescription( |
MakeTransportDescription(cert, role), action, nullptr)); |
+ set_remote_cert_ = (cert != nullptr); |
} |
// Allow any DTLS configuration to be specified (including invalid ones). |
@@ -233,16 +229,7 @@ |
return received_dtls_client_hellos_; |
} |
- int received_dtls_server_hellos() const { |
- return received_dtls_server_hellos_; |
- } |
- |
- bool negotiated_dtls() const { |
- return transport_->local_description() && |
- transport_->local_description()->identity_fingerprint && |
- transport_->remote_description() && |
- transport_->remote_description()->identity_fingerprint; |
- } |
+ bool negotiated_dtls() const { return set_local_cert_ && set_remote_cert_; } |
void CheckRole(rtc::SSLRole role) { |
if (role == rtc::SSL_CLIENT) { |
@@ -424,19 +411,18 @@ |
std::set<int> received_; |
bool use_dtls_srtp_ = false; |
rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12; |
+ bool set_local_cert_ = false; |
+ bool set_remote_cert_ = false; |
int received_dtls_client_hellos_ = 0; |
int received_dtls_server_hellos_ = 0; |
rtc::SentPacket sent_packet_; |
}; |
-// Base class for DtlsTransportChannelTest and DtlsEventOrderingTest, which |
-// inherit from different variants of testing::Test. |
-// |
// Note that this test always uses a FakeClock, due to the |fake_clock_| member |
// variable. |
-class DtlsTransportChannelTestBase { |
+class DtlsTransportChannelTest : public testing::Test { |
public: |
- DtlsTransportChannelTestBase() |
+ DtlsTransportChannelTest() |
: client1_("P1"), |
client2_("P2"), |
channel_ct_(1), |
@@ -509,9 +495,9 @@ |
if (!rv) |
return false; |
- EXPECT_TRUE_SIMULATED_WAIT( |
+ EXPECT_TRUE_WAIT( |
client1_.all_channels_writable() && client2_.all_channels_writable(), |
- kTimeout, fake_clock_); |
+ kTimeout); |
if (!client1_.all_channels_writable() || !client2_.all_channels_writable()) |
return false; |
@@ -602,8 +588,7 @@ |
LOG(LS_INFO) << "Expect packets, size=" << size; |
client2_.ExpectPackets(channel, size); |
client1_.SendPackets(channel, size, count, srtp); |
- EXPECT_EQ_SIMULATED_WAIT(count, client2_.NumPacketsReceived(), kTimeout, |
- fake_clock_); |
+ EXPECT_EQ_WAIT(count, client2_.NumPacketsReceived(), kTimeout); |
} |
protected: |
@@ -615,9 +600,6 @@ |
bool use_dtls_srtp_; |
rtc::SSLProtocolVersion ssl_expected_version_; |
}; |
- |
-class DtlsTransportChannelTest : public DtlsTransportChannelTestBase, |
- public ::testing::Test {}; |
// Test that transport negotiation of ICE, no DTLS works properly. |
TEST_F(DtlsTransportChannelTest, TestChannelSetupIce) { |
@@ -902,9 +884,9 @@ |
cricket::CONNECTIONROLE_ACTIVE, NF_REOFFER); |
bool rv = client1_.Connect(&client2_, false); |
EXPECT_TRUE(rv); |
- EXPECT_TRUE_SIMULATED_WAIT( |
+ EXPECT_TRUE_WAIT( |
client1_.all_channels_writable() && client2_.all_channels_writable(), |
- kTimeout, fake_clock_); |
+ kTimeout); |
TestTransfer(0, 1000, 100, true); |
TestTransfer(1, 1000, 100, true); |
@@ -959,6 +941,72 @@ |
certificate1->ssl_certificate().ToPEMString()); |
} |
+// Test that DTLS completes promptly if a ClientHello is received before the |
+// transport channel is writable (allowing a ServerHello to be sent). |
+TEST_F(DtlsTransportChannelTest, TestReceiveClientHelloBeforeWritable) { |
+ MAYBE_SKIP_TEST(HaveDtls); |
+ PrepareDtls(true, true, rtc::KT_DEFAULT); |
+ // Exchange transport descriptions. |
+ Negotiate(cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTIVE); |
+ |
+ // Make client2_ writable, but not client1_. |
+ EXPECT_TRUE(client2_.Connect(&client1_, true)); |
+ EXPECT_TRUE_WAIT(client2_.all_raw_channels_writable(), kTimeout); |
+ |
+ // Expect a DTLS ClientHello to be sent even while client1_ isn't writable. |
+ EXPECT_EQ_WAIT(1, client1_.received_dtls_client_hellos(), kTimeout); |
+ EXPECT_FALSE(client1_.all_raw_channels_writable()); |
+ |
+ // Now make client1_ writable and expect the handshake to complete |
+ // without client2_ needing to retransmit the ClientHello. |
+ EXPECT_TRUE(client1_.Connect(&client2_, true)); |
+ EXPECT_TRUE_WAIT( |
+ client1_.all_channels_writable() && client2_.all_channels_writable(), |
+ kTimeout); |
+ EXPECT_EQ(1, client1_.received_dtls_client_hellos()); |
+} |
+ |
+// Test that DTLS completes promptly if a ClientHello is received before the |
+// transport channel has a remote fingerprint (allowing a ServerHello to be |
+// sent). |
+TEST_F(DtlsTransportChannelTest, |
+ TestReceiveClientHelloBeforeRemoteFingerprint) { |
+ MAYBE_SKIP_TEST(HaveDtls); |
+ PrepareDtls(true, true, rtc::KT_DEFAULT); |
+ client1_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLING); |
+ client2_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLED); |
+ |
+ // Make client2_ writable and give it local/remote certs, but don't yet give |
+ // client1_ a remote fingerprint. |
+ client1_.transport()->SetLocalTransportDescription( |
+ MakeTransportDescription(client1_.certificate(), |
+ cricket::CONNECTIONROLE_ACTPASS), |
+ cricket::CA_OFFER, nullptr); |
+ client2_.Negotiate(&client1_, cricket::CA_ANSWER, |
+ cricket::CONNECTIONROLE_ACTIVE, |
+ cricket::CONNECTIONROLE_ACTPASS, 0); |
+ EXPECT_TRUE(client2_.Connect(&client1_, true)); |
+ EXPECT_TRUE_WAIT(client2_.all_raw_channels_writable(), kTimeout); |
+ |
+ // Expect a DTLS ClientHello to be sent even while client1_ doesn't have a |
+ // remote fingerprint. |
+ EXPECT_EQ_WAIT(1, client1_.received_dtls_client_hellos(), kTimeout); |
+ EXPECT_FALSE(client1_.all_raw_channels_writable()); |
+ |
+ // Now make give client1_ its remote fingerprint and make it writable, and |
+ // expect the handshake to complete without client2_ needing to retransmit |
+ // the ClientHello. |
+ client1_.transport()->SetRemoteTransportDescription( |
+ MakeTransportDescription(client2_.certificate(), |
+ cricket::CONNECTIONROLE_ACTIVE), |
+ cricket::CA_ANSWER, nullptr); |
+ EXPECT_TRUE(client1_.Connect(&client2_, true)); |
+ EXPECT_TRUE_WAIT( |
+ client1_.all_channels_writable() && client2_.all_channels_writable(), |
+ kTimeout); |
+ EXPECT_EQ(1, client1_.received_dtls_client_hellos()); |
+} |
+ |
// Test that packets are retransmitted according to the expected schedule. |
// Each time a timeout occurs, the retransmission timer should be doubled up to |
// 60 seconds. The timer defaults to 1 second, but for WebRTC we should be |
@@ -976,8 +1024,7 @@ |
// Make client2_ writable, but not client1_. |
// This means client1_ will send DTLS client hellos but get no response. |
EXPECT_TRUE(client2_.Connect(&client1_, true)); |
- EXPECT_TRUE_SIMULATED_WAIT(client2_.all_raw_channels_writable(), kTimeout, |
- fake_clock_); |
+ EXPECT_TRUE_WAIT(client2_.all_raw_channels_writable(), kTimeout); |
// Wait for the first client hello to be sent. |
EXPECT_EQ_WAIT(1, client1_.received_dtls_client_hellos(), kTimeout); |
@@ -1012,164 +1059,3 @@ |
CONNECT_BEFORE_NEGOTIATE)); |
TestTransfer(0, 1000, 100, false); |
} |
- |
-// The following events can occur in many different orders: |
-// 1. Caller receives remote fingerprint. |
-// 2. Caller is writable. |
-// 3. Caller receives ClientHello. |
-// 4. DTLS handshake finishes. |
-// |
-// The tests below cover all causally consistent permutations of these events; |
-// the caller must be writable and receive a ClientHello before the handshake |
-// finishes, but otherwise any ordering is possible. |
-// |
-// For each permutation, the test verifies that a connection is established and |
-// fingerprint verified without any DTLS packet needing to be retransmitted. |
-// |
-// Each permutation is also tested with a valid and invalid fingerprint, |
-// ensuring that the handshake fails with an invalid fingerprint. |
-enum DtlsTransportEvent { |
- CALLER_RECEIVES_FINGERPRINT, |
- CALLER_WRITABLE, |
- CALLER_RECEIVES_CLIENTHELLO, |
- HANDSHAKE_FINISHES |
-}; |
- |
-class DtlsEventOrderingTest |
- : public DtlsTransportChannelTestBase, |
- public ::testing::TestWithParam< |
- ::testing::tuple<std::vector<DtlsTransportEvent>, bool>> { |
- protected: |
- // If |valid_fingerprint| is false, the caller will receive a fingerprint |
- // that doesn't match the callee's certificate, so the handshake should fail. |
- void TestEventOrdering(const std::vector<DtlsTransportEvent>& events, |
- bool valid_fingerprint) { |
- // Pre-setup: Set local certificate on both caller and callee, and |
- // remote fingerprint on callee, but neither is writable and the caller |
- // doesn't have the callee's fingerprint. |
- PrepareDtls(true, true, rtc::KT_DEFAULT); |
- // Simulate packets being sent and arriving asynchronously. |
- // Otherwise the entire DTLS handshake would occur in one clock tick, and |
- // we couldn't inject method calls in the middle of it. |
- int simulated_delay_ms = 10; |
- client1_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLING, |
- simulated_delay_ms); |
- client2_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLED, |
- simulated_delay_ms); |
- client1_.SetLocalTransportDescription(client1_.certificate(), |
- cricket::CA_OFFER, |
- cricket::CONNECTIONROLE_ACTPASS, 0); |
- client2_.Negotiate(&client1_, cricket::CA_ANSWER, |
- cricket::CONNECTIONROLE_ACTIVE, |
- cricket::CONNECTIONROLE_ACTPASS, 0); |
- |
- for (DtlsTransportEvent e : events) { |
- switch (e) { |
- case CALLER_RECEIVES_FINGERPRINT: |
- if (valid_fingerprint) { |
- client1_.SetRemoteTransportDescription( |
- client2_.certificate(), cricket::CA_ANSWER, |
- cricket::CONNECTIONROLE_ACTIVE, 0); |
- } else { |
- // Create a fingerprint with a correct algorithm but an invalid |
- // digest. |
- cricket::TransportDescription remote_desc = |
- MakeTransportDescription(client2_.certificate(), |
- cricket::CONNECTIONROLE_ACTIVE); |
- ++(remote_desc.identity_fingerprint->digest[0]); |
- // Even if certificate verification fails inside this method, |
- // it should return true as long as the fingerprint was formatted |
- // correctly. |
- EXPECT_TRUE(client1_.transport()->SetRemoteTransportDescription( |
- remote_desc, cricket::CA_ANSWER, nullptr)); |
- } |
- break; |
- case CALLER_WRITABLE: |
- EXPECT_TRUE(client1_.Connect(&client2_, true)); |
- EXPECT_TRUE_SIMULATED_WAIT(client1_.all_raw_channels_writable(), |
- kTimeout, fake_clock_); |
- break; |
- case CALLER_RECEIVES_CLIENTHELLO: |
- // Sanity check that a ClientHello hasn't already been received. |
- EXPECT_EQ(0, client1_.received_dtls_client_hellos()); |
- // Making client2_ writable will cause it to send the ClientHello. |
- EXPECT_TRUE(client2_.Connect(&client1_, true)); |
- EXPECT_TRUE_SIMULATED_WAIT(client2_.all_raw_channels_writable(), |
- kTimeout, fake_clock_); |
- EXPECT_EQ_SIMULATED_WAIT(1, client1_.received_dtls_client_hellos(), |
- kTimeout, fake_clock_); |
- break; |
- case HANDSHAKE_FINISHES: |
- // Sanity check that the handshake hasn't already finished. |
- EXPECT_FALSE(client1_.GetDtlsChannel(0)->IsDtlsConnected() || |
- client1_.GetDtlsChannel(0)->dtls_state() == |
- cricket::DTLS_TRANSPORT_FAILED); |
- EXPECT_TRUE_SIMULATED_WAIT( |
- client1_.GetDtlsChannel(0)->IsDtlsConnected() || |
- client1_.GetDtlsChannel(0)->dtls_state() == |
- cricket::DTLS_TRANSPORT_FAILED, |
- kTimeout, fake_clock_); |
- break; |
- } |
- } |
- |
- cricket::DtlsTransportState expected_final_state = |
- valid_fingerprint ? cricket::DTLS_TRANSPORT_CONNECTED |
- : cricket::DTLS_TRANSPORT_FAILED; |
- EXPECT_EQ_SIMULATED_WAIT(expected_final_state, |
- client1_.GetDtlsChannel(0)->dtls_state(), kTimeout, |
- fake_clock_); |
- EXPECT_EQ_SIMULATED_WAIT(expected_final_state, |
- client2_.GetDtlsChannel(0)->dtls_state(), kTimeout, |
- fake_clock_); |
- |
- // Channel should be writable iff there was a valid fingerprint. |
- EXPECT_EQ(valid_fingerprint, client1_.GetDtlsChannel(0)->writable()); |
- EXPECT_EQ(valid_fingerprint, client2_.GetDtlsChannel(0)->writable()); |
- |
- // Check that no hello needed to be retransmitted. |
- EXPECT_EQ(1, client1_.received_dtls_client_hellos()); |
- EXPECT_EQ(1, client2_.received_dtls_server_hellos()); |
- |
- if (valid_fingerprint) { |
- TestTransfer(0, 1000, 100, false); |
- } |
- } |
-}; |
- |
-TEST_P(DtlsEventOrderingTest, TestEventOrdering) { |
- MAYBE_SKIP_TEST(HaveDtls); |
- TestEventOrdering(::testing::get<0>(GetParam()), |
- ::testing::get<1>(GetParam())); |
-} |
- |
-INSTANTIATE_TEST_CASE_P( |
- TestEventOrdering, |
- DtlsEventOrderingTest, |
- ::testing::Combine( |
- ::testing::Values( |
- std::vector<DtlsTransportEvent>{ |
- CALLER_RECEIVES_FINGERPRINT, CALLER_WRITABLE, |
- CALLER_RECEIVES_CLIENTHELLO, HANDSHAKE_FINISHES}, |
- std::vector<DtlsTransportEvent>{ |
- CALLER_WRITABLE, CALLER_RECEIVES_FINGERPRINT, |
- CALLER_RECEIVES_CLIENTHELLO, HANDSHAKE_FINISHES}, |
- std::vector<DtlsTransportEvent>{ |
- CALLER_WRITABLE, CALLER_RECEIVES_CLIENTHELLO, |
- CALLER_RECEIVES_FINGERPRINT, HANDSHAKE_FINISHES}, |
- std::vector<DtlsTransportEvent>{ |
- CALLER_WRITABLE, CALLER_RECEIVES_CLIENTHELLO, |
- HANDSHAKE_FINISHES, CALLER_RECEIVES_FINGERPRINT}, |
- std::vector<DtlsTransportEvent>{ |
- CALLER_RECEIVES_FINGERPRINT, CALLER_RECEIVES_CLIENTHELLO, |
- CALLER_WRITABLE, HANDSHAKE_FINISHES}, |
- std::vector<DtlsTransportEvent>{ |
- CALLER_RECEIVES_CLIENTHELLO, CALLER_RECEIVES_FINGERPRINT, |
- CALLER_WRITABLE, HANDSHAKE_FINISHES}, |
- std::vector<DtlsTransportEvent>{ |
- CALLER_RECEIVES_CLIENTHELLO, CALLER_WRITABLE, |
- CALLER_RECEIVES_FINGERPRINT, HANDSHAKE_FINISHES}, |
- std::vector<DtlsTransportEvent>{CALLER_RECEIVES_CLIENTHELLO, |
- CALLER_WRITABLE, HANDSHAKE_FINISHES, |
- CALLER_RECEIVES_FINGERPRINT}), |
- ::testing::Bool())); |