Index: webrtc/p2p/base/dtlstransportchannel_unittest.cc |
diff --git a/webrtc/p2p/base/dtlstransportchannel_unittest.cc b/webrtc/p2p/base/dtlstransportchannel_unittest.cc |
index 7643016b6a522abee5815386f9566a1a53cfb6ce..e14ab7646d0b9f9e7a802c2d3c3fd61b9abb0bbc 100644 |
--- a/webrtc/p2p/base/dtlstransportchannel_unittest.cc |
+++ b/webrtc/p2p/base/dtlstransportchannel_unittest.cc |
@@ -33,25 +33,34 @@ static const char kIcePwd1[] = "TESTICEPWD00000000000001"; |
static const size_t kPacketNumOffset = 8; |
static const size_t kPacketHeaderLen = 12; |
static const int kFakePacketId = 0x1234; |
+static const int kTimeout = 10000; |
static bool IsRtpLeadByte(uint8_t b) { |
return ((b & 0xC0) == 0x80); |
} |
+cricket::TransportDescription MakeTransportDescription( |
+ const rtc::scoped_refptr<rtc::RTCCertificate>& cert, |
+ cricket::ConnectionRole role) { |
+ rtc::scoped_ptr<rtc::SSLFingerprint> fingerprint; |
+ if (cert) { |
+ std::string digest_algorithm; |
+ cert->ssl_certificate().GetSignatureDigestAlgorithm(&digest_algorithm); |
+ fingerprint.reset( |
+ rtc::SSLFingerprint::Create(digest_algorithm, cert->identity())); |
+ } |
+ return cricket::TransportDescription(std::vector<std::string>(), kIceUfrag1, |
+ kIcePwd1, cricket::ICEMODE_FULL, role, |
+ fingerprint.get()); |
+} |
+ |
using cricket::ConnectionRole; |
enum Flags { NF_REOFFER = 0x1, NF_EXPECT_FAILURE = 0x2 }; |
class DtlsTestClient : public sigslot::has_slots<> { |
public: |
- DtlsTestClient(const std::string& name) |
- : name_(name), |
- packet_size_(0), |
- use_dtls_srtp_(false), |
- ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12), |
- negotiated_dtls_(false), |
- received_dtls_client_hello_(false), |
- received_dtls_server_hello_(false) {} |
+ DtlsTestClient(const std::string& name) : name_(name) {} |
void CreateCertificate(rtc::KeyType key_type) { |
certificate_ = |
rtc::RTCCertificate::Create(rtc::scoped_ptr<rtc::SSLIdentity>( |
@@ -185,9 +194,8 @@ class DtlsTestClient : public sigslot::has_slots<> { |
negotiated_dtls_ = (local_cert && remote_cert); |
} |
- bool Connect(DtlsTestClient* peer) { |
- transport_->ConnectChannels(); |
- transport_->SetDestination(peer->transport_.get()); |
+ bool Connect(DtlsTestClient* peer, bool asymmetric) { |
+ transport_->SetDestination(peer->transport_.get(), asymmetric); |
return true; |
} |
@@ -203,13 +211,29 @@ class DtlsTestClient : public sigslot::has_slots<> { |
return true; |
} |
+ bool all_raw_channels_writable() const { |
+ if (channels_.empty()) { |
+ return false; |
+ } |
+ for (cricket::DtlsTransportChannelWrapper* channel : channels_) { |
+ if (!channel->channel()->writable()) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ } |
+ |
+ int received_dtls_client_hellos() const { |
+ return received_dtls_client_hellos_; |
+ } |
+ |
void CheckRole(rtc::SSLRole role) { |
if (role == rtc::SSL_CLIENT) { |
- ASSERT_FALSE(received_dtls_client_hello_); |
- ASSERT_TRUE(received_dtls_server_hello_); |
+ ASSERT_EQ(0, received_dtls_client_hellos_); |
+ ASSERT_GT(received_dtls_server_hellos_, 0); |
} else { |
- ASSERT_TRUE(received_dtls_client_hello_); |
- ASSERT_FALSE(received_dtls_server_hello_); |
+ ASSERT_GT(received_dtls_client_hellos_, 0); |
+ ASSERT_EQ(0, received_dtls_server_hellos_); |
} |
} |
@@ -358,20 +382,18 @@ class DtlsTestClient : public sigslot::has_slots<> { |
// Look at the handshake packets to see what role we played. |
// Check that non-handshake packets are DTLS data or SRTP bypass. |
- if (negotiated_dtls_) { |
- if (data[0] == 22 && size > 17) { |
- if (data[13] == 1) { |
- received_dtls_client_hello_ = true; |
- } else if (data[13] == 2) { |
- received_dtls_server_hello_ = true; |
- } |
- } else if (!(data[0] >= 20 && data[0] <= 22)) { |
- ASSERT_TRUE(data[0] == 23 || IsRtpLeadByte(data[0])); |
- if (data[0] == 23) { |
- ASSERT_TRUE(VerifyEncryptedPacket(data, size)); |
- } else if (IsRtpLeadByte(data[0])) { |
- ASSERT_TRUE(VerifyPacket(data, size, NULL)); |
- } |
+ if (data[0] == 22 && size > 17) { |
+ if (data[13] == 1) { |
+ ++received_dtls_client_hellos_; |
+ } else if (data[13] == 2) { |
+ ++received_dtls_server_hellos_; |
+ } |
+ } else if (negotiated_dtls_ && !(data[0] >= 20 && data[0] <= 22)) { |
+ ASSERT_TRUE(data[0] == 23 || IsRtpLeadByte(data[0])); |
+ if (data[0] == 23) { |
+ ASSERT_TRUE(VerifyEncryptedPacket(data, size)); |
+ } else if (IsRtpLeadByte(data[0])) { |
+ ASSERT_TRUE(VerifyPacket(data, size, NULL)); |
} |
} |
} |
@@ -381,13 +403,13 @@ class DtlsTestClient : public sigslot::has_slots<> { |
rtc::scoped_refptr<rtc::RTCCertificate> certificate_; |
rtc::scoped_ptr<cricket::FakeTransport> transport_; |
std::vector<cricket::DtlsTransportChannelWrapper*> channels_; |
- size_t packet_size_; |
+ size_t packet_size_ = 0u; |
std::set<int> received_; |
- bool use_dtls_srtp_; |
- rtc::SSLProtocolVersion ssl_max_version_; |
- bool negotiated_dtls_; |
- bool received_dtls_client_hello_; |
- bool received_dtls_server_hello_; |
+ bool use_dtls_srtp_ = false; |
+ rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12; |
+ bool negotiated_dtls_ = false; |
+ int received_dtls_client_hellos_ = 0; |
+ int received_dtls_server_hellos_ = 0; |
rtc::SentPacket sent_packet_; |
}; |
@@ -437,14 +459,14 @@ class DtlsTransportChannelTest : public testing::Test { |
bool Connect(ConnectionRole client1_role, ConnectionRole client2_role) { |
Negotiate(client1_role, client2_role); |
- bool rv = client1_.Connect(&client2_); |
+ bool rv = client1_.Connect(&client2_, false); |
EXPECT_TRUE(rv); |
if (!rv) |
return false; |
EXPECT_TRUE_WAIT( |
client1_.all_channels_writable() && client2_.all_channels_writable(), |
- 10000); |
+ kTimeout); |
if (!client1_.all_channels_writable() || !client2_.all_channels_writable()) |
return false; |
@@ -535,7 +557,7 @@ class DtlsTransportChannelTest : public testing::Test { |
LOG(LS_INFO) << "Expect packets, size=" << size; |
client2_.ExpectPackets(channel, size); |
client1_.SendPackets(channel, size, count, srtp); |
- EXPECT_EQ_WAIT(count, client2_.NumPacketsReceived(), 10000); |
+ EXPECT_EQ_WAIT(count, client2_.NumPacketsReceived(), kTimeout); |
} |
protected: |
@@ -828,11 +850,11 @@ TEST_F(DtlsTransportChannelTest, TestRenegotiateBeforeConnect) { |
Renegotiate(&client1_, cricket::CONNECTIONROLE_ACTPASS, |
cricket::CONNECTIONROLE_ACTIVE, NF_REOFFER); |
- bool rv = client1_.Connect(&client2_); |
+ bool rv = client1_.Connect(&client2_, false); |
EXPECT_TRUE(rv); |
EXPECT_TRUE_WAIT( |
client1_.all_channels_writable() && client2_.all_channels_writable(), |
- 10000); |
+ kTimeout); |
TestTransfer(0, 1000, 100, true); |
TestTransfer(1, 1000, 100, true); |
@@ -886,3 +908,69 @@ TEST_F(DtlsTransportChannelTest, TestCertificatesAfterConnect) { |
ASSERT_EQ(remote_cert2->ToPEMString(), |
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); |
+ |
+ // Epect a DTLS ClientHello to be sent even while client1_ isn't writable. |
pthatcher1
2016/04/29 22:19:01
Expect
|
+ 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); |
+ |
+ // Epect a DTLS ClientHello to be sent even while client1_ doesn't have a |
pthatcher1
2016/04/29 22:19:01
Expect
|
+ // 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()); |
+} |