Allow remote SDP offers to be "active" or "passive"
Bug: webrtc:12933 Change-Id: I75f148a1700143571e0ef8bce8a99123bae9c918 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/229181 Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org> Commit-Queue: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/master@{#34812}
This commit is contained in:
parent
34cc986b4e
commit
efece42aa5
@ -91,11 +91,26 @@ std::unique_ptr<TransportDescription> TransportDescriptionFactory::CreateAnswer(
|
||||
if (offer && offer->identity_fingerprint.get()) {
|
||||
// The offer supports DTLS, so answer with DTLS, as long as we support it.
|
||||
if (secure_ == SEC_ENABLED || secure_ == SEC_REQUIRED) {
|
||||
// Fail if we can't create the fingerprint.
|
||||
// Setting DTLS role to active.
|
||||
ConnectionRole role = (options.prefer_passive_role)
|
||||
? CONNECTIONROLE_PASSIVE
|
||||
: CONNECTIONROLE_ACTIVE;
|
||||
ConnectionRole role = CONNECTIONROLE_NONE;
|
||||
// If the offer does not constrain the role, go with preference.
|
||||
if (offer->connection_role == CONNECTIONROLE_ACTPASS) {
|
||||
role = (options.prefer_passive_role) ? CONNECTIONROLE_PASSIVE
|
||||
: CONNECTIONROLE_ACTIVE;
|
||||
} else if (offer->connection_role == CONNECTIONROLE_ACTIVE) {
|
||||
role = CONNECTIONROLE_PASSIVE;
|
||||
} else if (offer->connection_role == CONNECTIONROLE_PASSIVE) {
|
||||
role = CONNECTIONROLE_ACTIVE;
|
||||
} else if (offer->connection_role == CONNECTIONROLE_NONE) {
|
||||
// This case may be reached if a=setup is not present in the SDP.
|
||||
RTC_LOG(LS_WARNING) << "Remote offer connection role is NONE, which is "
|
||||
"a protocol violation";
|
||||
role = (options.prefer_passive_role) ? CONNECTIONROLE_PASSIVE
|
||||
: CONNECTIONROLE_ACTIVE;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Remote offer connection role is " << role
|
||||
<< " which is a protocol violation";
|
||||
RTC_NOTREACHED();
|
||||
}
|
||||
|
||||
if (!SetSecurityInfo(desc.get(), role)) {
|
||||
return NULL;
|
||||
|
||||
@ -352,3 +352,50 @@ TEST_F(TransportDescriptionFactoryTest, CreateAnswerIceCredentialsIterator) {
|
||||
EXPECT_EQ(answer->GetIceParameters().ufrag, credentials[0].ufrag);
|
||||
EXPECT_EQ(answer->GetIceParameters().pwd, credentials[0].pwd);
|
||||
}
|
||||
|
||||
TEST_F(TransportDescriptionFactoryTest, CreateAnswerToDtlsActpassOffer) {
|
||||
f1_.set_secure(cricket::SEC_ENABLED);
|
||||
f1_.set_certificate(cert1_);
|
||||
|
||||
f2_.set_secure(cricket::SEC_ENABLED);
|
||||
f2_.set_certificate(cert2_);
|
||||
cricket::TransportOptions options;
|
||||
std::unique_ptr<TransportDescription> offer =
|
||||
f1_.CreateOffer(options, nullptr, &ice_credentials_);
|
||||
|
||||
std::unique_ptr<TransportDescription> answer =
|
||||
f2_.CreateAnswer(offer.get(), options, false, nullptr, &ice_credentials_);
|
||||
EXPECT_EQ(answer->connection_role, cricket::CONNECTIONROLE_ACTIVE);
|
||||
}
|
||||
|
||||
TEST_F(TransportDescriptionFactoryTest, CreateAnswerToDtlsActiveOffer) {
|
||||
f1_.set_secure(cricket::SEC_ENABLED);
|
||||
f1_.set_certificate(cert1_);
|
||||
|
||||
f2_.set_secure(cricket::SEC_ENABLED);
|
||||
f2_.set_certificate(cert2_);
|
||||
cricket::TransportOptions options;
|
||||
std::unique_ptr<TransportDescription> offer =
|
||||
f1_.CreateOffer(options, nullptr, &ice_credentials_);
|
||||
offer->connection_role = cricket::CONNECTIONROLE_ACTIVE;
|
||||
|
||||
std::unique_ptr<TransportDescription> answer =
|
||||
f2_.CreateAnswer(offer.get(), options, false, nullptr, &ice_credentials_);
|
||||
EXPECT_EQ(answer->connection_role, cricket::CONNECTIONROLE_PASSIVE);
|
||||
}
|
||||
|
||||
TEST_F(TransportDescriptionFactoryTest, CreateAnswerToDtlsPassiveOffer) {
|
||||
f1_.set_secure(cricket::SEC_ENABLED);
|
||||
f1_.set_certificate(cert1_);
|
||||
|
||||
f2_.set_secure(cricket::SEC_ENABLED);
|
||||
f2_.set_certificate(cert2_);
|
||||
cricket::TransportOptions options;
|
||||
std::unique_ptr<TransportDescription> offer =
|
||||
f1_.CreateOffer(options, nullptr, &ice_credentials_);
|
||||
offer->connection_role = cricket::CONNECTIONROLE_PASSIVE;
|
||||
|
||||
std::unique_ptr<TransportDescription> answer =
|
||||
f2_.CreateAnswer(offer.get(), options, false, nullptr, &ice_credentials_);
|
||||
EXPECT_EQ(answer->connection_role, cricket::CONNECTIONROLE_ACTIVE);
|
||||
}
|
||||
|
||||
@ -619,6 +619,9 @@ webrtc::RTCError JsepTransport::NegotiateDtlsRole(
|
||||
// ClientHello over each flow (host/port quartet).
|
||||
// IOW - actpass and passive modes should be treated as server and
|
||||
// active as client.
|
||||
// RFC 8842 section 5.3 updates this text, so that it is mandated
|
||||
// for the responder to handle offers with "active" and "passive"
|
||||
// as well as "actpass"
|
||||
bool is_remote_server = false;
|
||||
if (local_description_type == SdpType::kOffer) {
|
||||
if (local_connection_role != CONNECTIONROLE_ACTPASS) {
|
||||
@ -649,15 +652,37 @@ webrtc::RTCError JsepTransport::NegotiateDtlsRole(
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-dtls-sdp,
|
||||
// section 5.5.
|
||||
auto current_dtls_role = GetDtlsRole();
|
||||
if (!current_dtls_role ||
|
||||
(*current_dtls_role == rtc::SSL_CLIENT &&
|
||||
remote_connection_role == CONNECTIONROLE_ACTIVE) ||
|
||||
(*current_dtls_role == rtc::SSL_SERVER &&
|
||||
remote_connection_role == CONNECTIONROLE_PASSIVE)) {
|
||||
return webrtc::RTCError(
|
||||
webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Offerer must use actpass value or current negotiated role for "
|
||||
"setup attribute.");
|
||||
if (!current_dtls_role) {
|
||||
// Role not assigned yet. Verify that local role fits with remote role.
|
||||
switch (remote_connection_role) {
|
||||
case CONNECTIONROLE_ACTIVE:
|
||||
if (local_connection_role != CONNECTIONROLE_PASSIVE) {
|
||||
return webrtc::RTCError(
|
||||
webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Answerer must be passive when offerer is active");
|
||||
}
|
||||
break;
|
||||
case CONNECTIONROLE_PASSIVE:
|
||||
if (local_connection_role != CONNECTIONROLE_ACTIVE) {
|
||||
return webrtc::RTCError(
|
||||
webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Answerer must be active when offerer is passive");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
RTC_NOTREACHED();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ((*current_dtls_role == rtc::SSL_CLIENT &&
|
||||
remote_connection_role == CONNECTIONROLE_ACTIVE) ||
|
||||
(*current_dtls_role == rtc::SSL_SERVER &&
|
||||
remote_connection_role == CONNECTIONROLE_PASSIVE)) {
|
||||
return webrtc::RTCError(
|
||||
webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Offerer must use current negotiated role for "
|
||||
"setup attribute.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -42,6 +42,20 @@ struct NegotiateRoleParams {
|
||||
SdpType remote_type;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const ConnectionRole& role) {
|
||||
std::string str = "invalid";
|
||||
ConnectionRoleToString(role, &str);
|
||||
os << str;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const NegotiateRoleParams& param) {
|
||||
os << "[Local role " << param.local_role << " Remote role "
|
||||
<< param.remote_role << " LocalType " << SdpTypeToString(param.local_type)
|
||||
<< " RemoteType " << SdpTypeToString(param.remote_type) << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<webrtc::IceTransportInterface> CreateIceTransport(
|
||||
std::unique_ptr<FakeIceTransport> internal) {
|
||||
if (!internal) {
|
||||
@ -445,7 +459,13 @@ TEST_P(JsepTransport2WithRtcpMux, ValidDtlsRoleNegotiation) {
|
||||
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_PASSIVE, SdpType::kOffer,
|
||||
SdpType::kAnswer},
|
||||
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_PASSIVE, SdpType::kOffer,
|
||||
SdpType::kPrAnswer}};
|
||||
SdpType::kPrAnswer},
|
||||
// Combinations permitted by RFC 8842 section 5.3
|
||||
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_PASSIVE, SdpType::kAnswer,
|
||||
SdpType::kOffer},
|
||||
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_PASSIVE, SdpType::kPrAnswer,
|
||||
SdpType::kOffer},
|
||||
};
|
||||
|
||||
for (auto& param : valid_client_params) {
|
||||
jsep_transport_ =
|
||||
@ -487,7 +507,11 @@ TEST_P(JsepTransport2WithRtcpMux, ValidDtlsRoleNegotiation) {
|
||||
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_ACTIVE, SdpType::kOffer,
|
||||
SdpType::kAnswer},
|
||||
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_ACTIVE, SdpType::kOffer,
|
||||
SdpType::kPrAnswer}};
|
||||
SdpType::kPrAnswer},
|
||||
// Combinations permitted by RFC 8842 section 5.3
|
||||
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_ACTIVE, SdpType::kPrAnswer,
|
||||
SdpType::kOffer},
|
||||
};
|
||||
|
||||
for (auto& param : valid_server_params) {
|
||||
jsep_transport_ =
|
||||
@ -590,20 +614,15 @@ TEST_P(JsepTransport2WithRtcpMux, InvalidDtlsRoleNegotiation) {
|
||||
}
|
||||
}
|
||||
|
||||
// Invalid parameters due to the offerer not using ACTPASS.
|
||||
// Invalid parameters due to the offerer not using a role consistent with the
|
||||
// state
|
||||
NegotiateRoleParams offerer_without_actpass_params[] = {
|
||||
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_PASSIVE, SdpType::kAnswer,
|
||||
SdpType::kOffer},
|
||||
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_ACTIVE, SdpType::kAnswer,
|
||||
SdpType::kOffer},
|
||||
// Cannot use ACTPASS in an answer
|
||||
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_PASSIVE, SdpType::kAnswer,
|
||||
SdpType::kOffer},
|
||||
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_PASSIVE, SdpType::kPrAnswer,
|
||||
SdpType::kOffer},
|
||||
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_ACTIVE, SdpType::kPrAnswer,
|
||||
SdpType::kOffer},
|
||||
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_PASSIVE, SdpType::kPrAnswer,
|
||||
SdpType::kOffer},
|
||||
// Cannot send ACTIVE or PASSIVE in an offer (must handle, must not send)
|
||||
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_PASSIVE, SdpType::kOffer,
|
||||
SdpType::kAnswer},
|
||||
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_ACTIVE, SdpType::kOffer,
|
||||
@ -629,20 +648,24 @@ TEST_P(JsepTransport2WithRtcpMux, InvalidDtlsRoleNegotiation) {
|
||||
EXPECT_TRUE(jsep_transport_
|
||||
->SetLocalJsepTransportDescription(local_description,
|
||||
param.local_type)
|
||||
.ok());
|
||||
.ok())
|
||||
<< param;
|
||||
EXPECT_FALSE(jsep_transport_
|
||||
->SetRemoteJsepTransportDescription(remote_description,
|
||||
param.remote_type)
|
||||
.ok());
|
||||
.ok())
|
||||
<< param;
|
||||
} else {
|
||||
EXPECT_TRUE(jsep_transport_
|
||||
->SetRemoteJsepTransportDescription(remote_description,
|
||||
param.remote_type)
|
||||
.ok());
|
||||
.ok())
|
||||
<< param;
|
||||
EXPECT_FALSE(jsep_transport_
|
||||
->SetLocalJsepTransportDescription(local_description,
|
||||
param.local_type)
|
||||
.ok());
|
||||
.ok())
|
||||
<< param;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2264,7 +2264,6 @@ bool ParseSessionDescription(const std::string& message,
|
||||
session_extmaps->push_back(extmap);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -3675,7 +3675,7 @@ TEST_F(WebRtcSdpTest, SerializeDtlsSetupAttribute) {
|
||||
EXPECT_EQ(sdp_with_dtlssetup, message);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, DeserializeDtlsSetupAttribute) {
|
||||
TEST_F(WebRtcSdpTest, DeserializeDtlsSetupAttributeActpass) {
|
||||
JsepSessionDescription jdesc_with_dtlssetup(kDummyType);
|
||||
std::string sdp_with_dtlssetup = kSdpFullString;
|
||||
InjectAfter(kSessionTime, "a=setup:actpass\r\n", &sdp_with_dtlssetup);
|
||||
@ -3691,6 +3691,37 @@ TEST_F(WebRtcSdpTest, DeserializeDtlsSetupAttribute) {
|
||||
vtinfo->description.connection_role);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, DeserializeDtlsSetupAttributeActive) {
|
||||
JsepSessionDescription jdesc_with_dtlssetup(kDummyType);
|
||||
std::string sdp_with_dtlssetup = kSdpFullString;
|
||||
InjectAfter(kSessionTime, "a=setup:active\r\n", &sdp_with_dtlssetup);
|
||||
EXPECT_TRUE(SdpDeserialize(sdp_with_dtlssetup, &jdesc_with_dtlssetup));
|
||||
cricket::SessionDescription* desc = jdesc_with_dtlssetup.description();
|
||||
const cricket::TransportInfo* atinfo =
|
||||
desc->GetTransportInfoByName("audio_content_name");
|
||||
EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
|
||||
atinfo->description.connection_role);
|
||||
const cricket::TransportInfo* vtinfo =
|
||||
desc->GetTransportInfoByName("video_content_name");
|
||||
EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
|
||||
vtinfo->description.connection_role);
|
||||
}
|
||||
TEST_F(WebRtcSdpTest, DeserializeDtlsSetupAttributePassive) {
|
||||
JsepSessionDescription jdesc_with_dtlssetup(kDummyType);
|
||||
std::string sdp_with_dtlssetup = kSdpFullString;
|
||||
InjectAfter(kSessionTime, "a=setup:passive\r\n", &sdp_with_dtlssetup);
|
||||
EXPECT_TRUE(SdpDeserialize(sdp_with_dtlssetup, &jdesc_with_dtlssetup));
|
||||
cricket::SessionDescription* desc = jdesc_with_dtlssetup.description();
|
||||
const cricket::TransportInfo* atinfo =
|
||||
desc->GetTransportInfoByName("audio_content_name");
|
||||
EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
|
||||
atinfo->description.connection_role);
|
||||
const cricket::TransportInfo* vtinfo =
|
||||
desc->GetTransportInfoByName("video_content_name");
|
||||
EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
|
||||
vtinfo->description.connection_role);
|
||||
}
|
||||
|
||||
// Verifies that the order of the serialized m-lines follows the order of the
|
||||
// ContentInfo in SessionDescription, and vise versa for deserialization.
|
||||
TEST_F(WebRtcSdpTest, MediaContentOrderMaintainedRoundTrip) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user