diff --git a/talk/session/media/mediasession.cc b/talk/session/media/mediasession.cc index 8219ec8844..006975f931 100644 --- a/talk/session/media/mediasession.cc +++ b/talk/session/media/mediasession.cc @@ -62,6 +62,10 @@ using talk_base::scoped_ptr; // RFC4585 const char kMediaProtocolAvpf[] = "RTP/AVPF"; // RFC5124 +const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF"; + +// This should be replaced by "UDP/TLS/RTP/SAVPF", but we need to support it for +// now to be compatible with previous Chrome versions. const char kMediaProtocolSavpf[] = "RTP/SAVPF"; const char kMediaProtocolRtpPrefix[] = "RTP/"; @@ -987,17 +991,20 @@ static bool CreateMediaContentAnswer( } static bool IsMediaProtocolSupported(MediaType type, - const std::string& protocol) { + const std::string& protocol, + bool secure_transport) { // Data channels can have a protocol of SCTP or SCTP/DTLS. if (type == MEDIA_TYPE_DATA && - (protocol == kMediaProtocolSctp || - protocol == kMediaProtocolDtlsSctp)) { + ((protocol == kMediaProtocolSctp && !secure_transport)|| + (protocol == kMediaProtocolDtlsSctp && secure_transport))) { return true; } + // Since not all applications serialize and deserialize the media protocol, // we will have to accept |protocol| to be empty. - return protocol == kMediaProtocolAvpf || protocol == kMediaProtocolSavpf || - protocol.empty(); + return protocol == kMediaProtocolAvpf || protocol.empty() || + protocol == kMediaProtocolSavpf || + (protocol == kMediaProtocolDtlsSavpf && secure_transport); } static void SetMediaProtocol(bool secure_transport, @@ -1337,8 +1344,9 @@ SessionDescription* MediaSessionDescriptionFactory::CreateAnswer( } bool rejected = !options.has_audio || audio_content->rejected || - !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO, - audio_answer->protocol()); + !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO, + audio_answer->protocol(), + audio_transport->secure()); if (!rejected) { AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer.get()); @@ -1385,7 +1393,9 @@ SessionDescription* MediaSessionDescriptionFactory::CreateAnswer( return NULL; } bool rejected = !options.has_video || video_content->rejected || - !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO, video_answer->protocol()); + !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO, + video_answer->protocol(), + video_transport->secure()); if (!rejected) { if (!AddTransportAnswer(video_content->name, *(video_transport.get()), answer.get())) { @@ -1438,7 +1448,9 @@ SessionDescription* MediaSessionDescriptionFactory::CreateAnswer( } bool rejected = !options.has_data() || data_content->rejected || - !IsMediaProtocolSupported(MEDIA_TYPE_DATA, data_answer->protocol()); + !IsMediaProtocolSupported(MEDIA_TYPE_DATA, + data_answer->protocol(), + data_transport->secure()); if (!rejected) { data_answer->set_bandwidth(options.data_bandwidth); if (!AddTransportAnswer(data_content->name, *(data_transport.get()), diff --git a/talk/session/media/mediasession.h b/talk/session/media/mediasession.h index a130b151ff..5041de0a97 100644 --- a/talk/session/media/mediasession.h +++ b/talk/session/media/mediasession.h @@ -80,6 +80,8 @@ extern const char kMediaProtocolAvpf[]; // RFC5124 RTP/SAVPF extern const char kMediaProtocolSavpf[]; +extern const char kMediaProtocolDtlsSavpf[]; + extern const char kMediaProtocolRtpPrefix[]; extern const char kMediaProtocolSctp[]; diff --git a/talk/session/media/mediasession_unittest.cc b/talk/session/media/mediasession_unittest.cc index ad7cc132e7..cf492a65ee 100644 --- a/talk/session/media/mediasession_unittest.cc +++ b/talk/session/media/mediasession_unittest.cc @@ -1788,6 +1788,64 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoWithAnswerBundle) { TestCryptoWithBundle(false); } +// Verifies that creating answer fails if the offer has UDP/TLS/RTP/SAVPF but +// DTLS is not enabled locally. +TEST_F(MediaSessionDescriptionFactoryTest, + TestOfferDtlsSavpfWithoutDtlsFailed) { + f1_.set_secure(SEC_ENABLED); + f2_.set_secure(SEC_ENABLED); + tdf1_.set_secure(SEC_DISABLED); + tdf2_.set_secure(SEC_DISABLED); + + talk_base::scoped_ptr offer( + f1_.CreateOffer(MediaSessionOptions(), NULL)); + ASSERT_TRUE(offer.get() != NULL); + ContentInfo* offer_content = offer->GetContentByName("audio"); + ASSERT_TRUE(offer_content != NULL); + AudioContentDescription* offer_audio_desc = + static_cast(offer_content->description); + offer_audio_desc->set_protocol(cricket::kMediaProtocolDtlsSavpf); + + talk_base::scoped_ptr answer( + f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL)); + ASSERT_TRUE(answer != NULL); + ContentInfo* answer_content = answer->GetContentByName("audio"); + ASSERT_TRUE(answer_content != NULL); + + ASSERT_TRUE(answer_content->rejected); +} + +// Offers UDP/TLS/RTP/SAVPF and verifies the answer can be created and contains +// UDP/TLS/RTP/SAVPF. +TEST_F(MediaSessionDescriptionFactoryTest, TestOfferDtlsSavpfCreateAnswer) { + f1_.set_secure(SEC_ENABLED); + f2_.set_secure(SEC_ENABLED); + tdf1_.set_secure(SEC_ENABLED); + tdf2_.set_secure(SEC_ENABLED); + + talk_base::scoped_ptr offer( + f1_.CreateOffer(MediaSessionOptions(), NULL)); + ASSERT_TRUE(offer.get() != NULL); + ContentInfo* offer_content = offer->GetContentByName("audio"); + ASSERT_TRUE(offer_content != NULL); + AudioContentDescription* offer_audio_desc = + static_cast(offer_content->description); + offer_audio_desc->set_protocol(cricket::kMediaProtocolDtlsSavpf); + + talk_base::scoped_ptr answer( + f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL)); + ASSERT_TRUE(answer != NULL); + + const ContentInfo* answer_content = answer->GetContentByName("audio"); + ASSERT_TRUE(answer_content != NULL); + ASSERT_FALSE(answer_content->rejected); + + const AudioContentDescription* answer_audio_desc = + static_cast(answer_content->description); + EXPECT_EQ(std::string(cricket::kMediaProtocolDtlsSavpf), + answer_audio_desc->protocol()); +} + // Test that we include both SDES and DTLS in the offer, but only include SDES // in the answer if DTLS isn't negotiated. TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoDtls) {