Update stable to r4610.
git-svn-id: http://webrtc.googlecode.com/svn/stable/talk@4610 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/app/webrtc/jsepsessiondescription_unittest.cc b/app/webrtc/jsepsessiondescription_unittest.cc
index 83f67cb..e2b59fb 100644
--- a/app/webrtc/jsepsessiondescription_unittest.cc
+++ b/app/webrtc/jsepsessiondescription_unittest.cc
@@ -79,16 +79,18 @@
cricket::NS_GINGLE_P2P,
std::vector<std::string>(),
kCandidateUfragVoice, kCandidatePwdVoice,
- cricket::ICEMODE_FULL, NULL,
- cricket::Candidates()))));
+ cricket::ICEMODE_FULL,
+ cricket::CONNECTIONROLE_NONE,
+ NULL, cricket::Candidates()))));
EXPECT_TRUE(desc->AddTransportInfo(
cricket::TransportInfo(cricket::CN_VIDEO,
cricket::TransportDescription(
cricket::NS_GINGLE_P2P,
std::vector<std::string>(),
kCandidateUfragVideo, kCandidatePwdVideo,
- cricket::ICEMODE_FULL, NULL,
- cricket::Candidates()))));
+ cricket::ICEMODE_FULL,
+ cricket::CONNECTIONROLE_NONE,
+ NULL, cricket::Candidates()))));
return desc;
}
diff --git a/app/webrtc/peerconnection_unittest.cc b/app/webrtc/peerconnection_unittest.cc
index 250e60f..52fa4cf 100644
--- a/app/webrtc/peerconnection_unittest.cc
+++ b/app/webrtc/peerconnection_unittest.cc
@@ -1095,7 +1095,7 @@
// This test sets up a Jsep call between two parties, and the callee only
// accept to receive audio.
-TEST_F(JsepPeerConnectionP2PTestClient, LocalP2PTestAnswerAudio) {
+TEST_F(JsepPeerConnectionP2PTestClient, DISABLED_LocalP2PTestAnswerAudio) {
ASSERT_TRUE(CreateTestClients());
receiving_client()->SetReceiveAudioVideo(true, false);
LocalP2PTest();
diff --git a/app/webrtc/webrtcsdp.cc b/app/webrtc/webrtcsdp.cc
index 9910a51..a3bfa71 100644
--- a/app/webrtc/webrtcsdp.cc
+++ b/app/webrtc/webrtcsdp.cc
@@ -141,6 +141,7 @@
static const char kAttributeCandidatePassword[] = "password";
static const char kAttributeCandidateGeneration[] = "generation";
static const char kAttributeFingerprint[] = "fingerprint";
+static const char kAttributeSetup[] = "setup";
static const char kAttributeFmtp[] = "fmtp";
static const char kAttributeRtpmap[] = "rtpmap";
static const char kAttributeRtcp[] = "rtcp";
@@ -318,6 +319,9 @@
static bool ParseFingerprintAttribute(const std::string& line,
talk_base::SSLFingerprint** fingerprint,
SdpParseError* error);
+static bool ParseDtlsSetup(const std::string& line,
+ cricket::ConnectionRole* role,
+ SdpParseError* error);
// Helper functions
@@ -902,7 +906,8 @@
SdpParseError* error) {
std::string session_id;
std::string session_version;
- TransportDescription session_td(NS_JINGLE_ICE_UDP, Candidates());
+ TransportDescription session_td(NS_JINGLE_ICE_UDP,
+ std::string(), std::string());
RtpHeaderExtensions session_extmaps;
cricket::SessionDescription* desc = new cricket::SessionDescription();
std::vector<JsepIceCandidate*> candidates;
@@ -1226,8 +1231,23 @@
os << kSdpDelimiterColon
<< fp->algorithm << kSdpDelimiterSpace
<< fp->GetRfc4572Fingerprint();
-
AddLine(os.str(), message);
+
+ // Inserting setup attribute.
+ if (transport_info->description.connection_role !=
+ cricket::CONNECTIONROLE_NONE) {
+ // Making sure we are not using "passive" mode.
+ cricket::ConnectionRole role =
+ transport_info->description.connection_role;
+ ASSERT(role == cricket::CONNECTIONROLE_ACTIVE ||
+ role == cricket::CONNECTIONROLE_ACTPASS);
+ InitAttrLine(kAttributeSetup, &os);
+ std::string dtls_role_str = role == cricket::CONNECTIONROLE_ACTPASS ?
+ cricket::CONNECTIONROLE_ACTPASS_STR :
+ cricket::CONNECTIONROLE_ACTIVE_STR;
+ os << kSdpDelimiterColon << dtls_role_str;
+ AddLine(os.str(), message);
+ }
}
}
@@ -1796,6 +1816,10 @@
return false;
}
session_td->identity_fingerprint.reset(fingerprint);
+ } else if (HasAttribute(line, kAttributeSetup)) {
+ if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
+ return false;
+ }
} else if (HasAttribute(line, kAttributeMsidSemantics)) {
std::string semantics;
if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
@@ -1876,6 +1900,24 @@
return true;
}
+static bool ParseDtlsSetup(const std::string& line,
+ cricket::ConnectionRole* role,
+ SdpParseError* error) {
+ // setup-attr = "a=setup:" role
+ // role = "active" / "passive" / "actpass" / "holdconn"
+ std::vector<std::string> fields;
+ talk_base::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
+ const size_t expected_fields = 2;
+ if (fields.size() != expected_fields) {
+ return ParseFailedExpectFieldNum(line, expected_fields, error);
+ }
+ std::string role_str = fields[1];
+ if (!cricket::StringToConnectionRole(role_str, role)) {
+ return ParseFailed(line, "Invalid attribute value.", error);
+ }
+ return true;
+}
+
// RFC 3551
// PT encoding media type clock rate channels
// name (Hz)
@@ -2039,6 +2081,7 @@
session_td.ice_ufrag,
session_td.ice_pwd,
session_td.ice_mode,
+ session_td.connection_role,
session_td.identity_fingerprint.get(),
Candidates());
@@ -2378,6 +2421,10 @@
return false;
}
transport->identity_fingerprint.reset(fingerprint);
+ } else if (HasAttribute(line, kAttributeSetup)) {
+ if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
+ return false;
+ }
} else if (is_rtp) {
//
// RTP specific attrubtes
diff --git a/app/webrtc/webrtcsdp_unittest.cc b/app/webrtc/webrtcsdp_unittest.cc
index 9e4c660..5fa8b0c 100644
--- a/app/webrtc/webrtcsdp_unittest.cc
+++ b/app/webrtc/webrtcsdp_unittest.cc
@@ -485,19 +485,13 @@
EXPECT_TRUE(desc_.AddTransportInfo(
TransportInfo(kAudioContentName,
TransportDescription(NS_JINGLE_ICE_UDP,
- std::vector<std::string>(),
kCandidateUfragVoice,
- kCandidatePwdVoice,
- cricket::ICEMODE_FULL,
- NULL, Candidates()))));
+ kCandidatePwdVoice))));
EXPECT_TRUE(desc_.AddTransportInfo(
TransportInfo(kVideoContentName,
TransportDescription(NS_JINGLE_ICE_UDP,
- std::vector<std::string>(),
kCandidateUfragVideo,
- kCandidatePwdVideo,
- cricket::ICEMODE_FULL,
- NULL, Candidates()))));
+ kCandidatePwdVideo))));
// v4 host
int port = 1234;
@@ -860,9 +854,7 @@
}
TransportInfo transport_info(
content_name, TransportDescription(NS_JINGLE_ICE_UDP,
- std::vector<std::string>(),
- ufrag, pwd, cricket::ICEMODE_FULL,
- NULL, Candidates()));
+ ufrag, pwd));
SessionDescription* desc =
const_cast<SessionDescription*>(jdesc->description());
desc->RemoveTransportInfoByName(content_name);
@@ -903,16 +895,18 @@
std::vector<std::string>(),
kCandidateUfragVoice,
kCandidatePwdVoice,
- cricket::ICEMODE_FULL, &fingerprint,
- Candidates()))));
+ cricket::ICEMODE_FULL,
+ cricket::CONNECTIONROLE_NONE,
+ &fingerprint, Candidates()))));
EXPECT_TRUE(desc_.AddTransportInfo(
TransportInfo(kVideoContentName,
TransportDescription(NS_JINGLE_ICE_UDP,
std::vector<std::string>(),
kCandidateUfragVideo,
kCandidatePwdVideo,
- cricket::ICEMODE_FULL, &fingerprint,
- Candidates()))));
+ cricket::ICEMODE_FULL,
+ cricket::CONNECTIONROLE_NONE,
+ &fingerprint, Candidates()))));
}
void AddExtmap() {
@@ -984,11 +978,8 @@
EXPECT_TRUE(desc_.AddTransportInfo(
TransportInfo(kDataContentName,
TransportDescription(NS_JINGLE_ICE_UDP,
- std::vector<std::string>(),
kCandidateUfragData,
- kCandidatePwdData,
- cricket::ICEMODE_FULL,
- NULL, Candidates()))));
+ kCandidatePwdData))));
}
void AddRtpDataChannel() {
@@ -1011,11 +1002,8 @@
EXPECT_TRUE(desc_.AddTransportInfo(
TransportInfo(kDataContentName,
TransportDescription(NS_JINGLE_ICE_UDP,
- std::vector<std::string>(),
kCandidateUfragData,
- kCandidatePwdData,
- cricket::ICEMODE_FULL,
- NULL, Candidates()))));
+ kCandidatePwdData))));
}
bool TestDeserializeDirection(cricket::MediaContentDirection direction) {
@@ -1966,3 +1954,60 @@
EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
EXPECT_EQ(sdp_with_data, webrtc::SdpSerialize(jdesc_output));
}
+
+TEST_F(WebRtcSdpTest, SerializeDtlsSetupAttribute) {
+ AddFingerprint();
+ TransportInfo audio_transport_info =
+ *(desc_.GetTransportInfoByName(kAudioContentName));
+ EXPECT_EQ(cricket::CONNECTIONROLE_NONE,
+ audio_transport_info.description.connection_role);
+ audio_transport_info.description.connection_role =
+ cricket::CONNECTIONROLE_ACTIVE;
+
+ TransportInfo video_transport_info =
+ *(desc_.GetTransportInfoByName(kVideoContentName));
+ EXPECT_EQ(cricket::CONNECTIONROLE_NONE,
+ video_transport_info.description.connection_role);
+ video_transport_info.description.connection_role =
+ cricket::CONNECTIONROLE_ACTIVE;
+
+ desc_.RemoveTransportInfoByName(kAudioContentName);
+ desc_.RemoveTransportInfoByName(kVideoContentName);
+
+ desc_.AddTransportInfo(audio_transport_info);
+ desc_.AddTransportInfo(video_transport_info);
+
+ ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
+ jdesc_.session_id(),
+ jdesc_.session_version()));
+ std::string message = webrtc::SdpSerialize(jdesc_);
+ std::string sdp_with_dtlssetup = kSdpFullString;
+
+ // Fingerprint attribute is necessary to add DTLS setup attribute.
+ InjectAfter(kAttributeIcePwdVoice,
+ kFingerprint, &sdp_with_dtlssetup);
+ InjectAfter(kAttributeIcePwdVideo,
+ kFingerprint, &sdp_with_dtlssetup);
+ // Now adding |setup| attribute.
+ InjectAfter(kFingerprint,
+ "a=setup:active\r\n", &sdp_with_dtlssetup);
+ EXPECT_EQ(sdp_with_dtlssetup, message);
+}
+
+TEST_F(WebRtcSdpTest, DeserializeDtlsSetupAttribute) {
+ JsepSessionDescription jdesc_with_dtlssetup(kDummyString);
+ std::string sdp_with_dtlssetup = kSdpFullString;
+ InjectAfter(kSessionTime,
+ "a=setup:actpass\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_ACTPASS,
+ atinfo->description.connection_role);
+ const cricket::TransportInfo* vtinfo =
+ desc->GetTransportInfoByName("video_content_name");
+ EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS,
+ vtinfo->description.connection_role);
+}
diff --git a/app/webrtc/webrtcsession.cc b/app/webrtc/webrtcsession.cc
index b056757..7016e2a 100644
--- a/app/webrtc/webrtcsession.cc
+++ b/app/webrtc/webrtcsession.cc
@@ -505,6 +505,26 @@
return webrtc_session_desc_factory_->secure();
}
+bool WebRtcSession::GetSslRole(talk_base::SSLRole* role) {
+ if (local_description() == NULL || remote_description() == NULL) {
+ LOG(LS_INFO) << "Local and Remote descriptions must be applied to get "
+ << "SSL Role of the session.";
+ return false;
+ }
+
+ // TODO(mallinath) - Return role of each transport, as role may differ from
+ // one another.
+ // In current implementaion we just return the role of first transport in the
+ // transport map.
+ for (cricket::TransportMap::const_iterator iter = transport_proxies().begin();
+ iter != transport_proxies().end(); ++iter) {
+ if (iter->second->impl()) {
+ return iter->second->impl()->GetSslRole(role);
+ }
+ }
+ return false;
+}
+
void WebRtcSession::CreateOffer(CreateSessionDescriptionObserver* observer,
const MediaConstraintsInterface* constraints) {
webrtc_session_desc_factory_->CreateOffer(observer, constraints);
@@ -517,42 +537,22 @@
bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
std::string* err_desc) {
- cricket::SecureMediaPolicy secure_policy =
- webrtc_session_desc_factory_->secure();
// Takes the ownership of |desc| regardless of the result.
talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
- if (error() != cricket::BaseSession::ERROR_NONE) {
- return BadLocalSdp(SessionErrorMsg(error()), err_desc);
- }
-
- if (!desc || !desc->description()) {
- return BadLocalSdp(kInvalidSdp, err_desc);
- }
-
- if (!VerifyBundleSettings(desc->description())) {
- return BadLocalSdp(kBundleWithoutRtcpMux, err_desc);
- }
-
- Action action = GetAction(desc->type());
- if (!ExpectSetLocalDescription(action)) {
- std::string type = desc->type();
- return BadLocalSdp(BadStateErrMsg(type, state()), err_desc);
- }
- if (secure_policy == cricket::SEC_REQUIRED &&
- !VerifyCrypto(desc->description())) {
- return BadLocalSdp(kSdpWithoutCrypto, err_desc);
- }
- if (action == kAnswer && !VerifyMediaDescriptions(
- desc->description(), remote_description()->description())) {
- return BadLocalSdp(kMlineMismatch, err_desc);
+ // Validate SDP.
+ if (!ValidateSessionDescription(desc, cricket::CS_LOCAL, err_desc)) {
+ return false;
}
// Update the initiator flag if this session is the initiator.
+ Action action = GetAction(desc->type());
if (state() == STATE_INIT && action == kOffer) {
set_initiator(true);
}
+ cricket::SecureMediaPolicy secure_policy =
+ webrtc_session_desc_factory_->secure();
// Update the MediaContentDescription crypto settings as per the policy set.
UpdateSessionDescriptionSecurePolicy(secure_policy, desc->description());
@@ -589,40 +589,16 @@
bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
std::string* err_desc) {
- cricket::SecureMediaPolicy secure_policy =
- webrtc_session_desc_factory_->secure();
// Takes the ownership of |desc| regardless of the result.
talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
- if (error() != cricket::BaseSession::ERROR_NONE) {
- return BadRemoteSdp(SessionErrorMsg(error()), err_desc);
- }
-
- if (!desc || !desc->description()) {
- return BadRemoteSdp(kInvalidSdp, err_desc);
- }
-
- if (!VerifyBundleSettings(desc->description())) {
- return BadRemoteSdp(kBundleWithoutRtcpMux, err_desc);
- }
-
- Action action = GetAction(desc->type());
- if (!ExpectSetRemoteDescription(action)) {
- std::string type = desc->type();
- return BadRemoteSdp(BadStateErrMsg(type, state()), err_desc);
- }
-
- if (action == kAnswer && !VerifyMediaDescriptions(
- desc->description(), local_description()->description())) {
- return BadRemoteSdp(kMlineMismatch, err_desc);
- }
-
- if (secure_policy == cricket::SEC_REQUIRED &&
- !VerifyCrypto(desc->description())) {
- return BadRemoteSdp(kSdpWithoutCrypto, err_desc);
+ // Validate SDP.
+ if (!ValidateSessionDescription(desc, cricket::CS_REMOTE, err_desc)) {
+ return false;
}
// Transport and Media channels will be created only when offer is set.
+ Action action = GetAction(desc->type());
if (action == kOffer && !CreateChannels(desc->description())) {
// TODO(mallinath) - Handle CreateChannel failure, as new local description
// is applied. Restore back to old description.
@@ -1094,36 +1070,6 @@
ProcessNewLocalCandidate(proxy->content_name(), candidates);
}
-bool WebRtcSession::ExpectSetLocalDescription(Action action) {
- return ((action == kOffer && state() == STATE_INIT) ||
- // update local offer
- (action == kOffer && state() == STATE_SENTINITIATE) ||
- // update the current ongoing session.
- (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
- (action == kOffer && state() == STATE_SENTACCEPT) ||
- (action == kOffer && state() == STATE_INPROGRESS) ||
- // accept remote offer
- (action == kAnswer && state() == STATE_RECEIVEDINITIATE) ||
- (action == kAnswer && state() == STATE_SENTPRACCEPT) ||
- (action == kPrAnswer && state() == STATE_RECEIVEDINITIATE) ||
- (action == kPrAnswer && state() == STATE_SENTPRACCEPT));
-}
-
-bool WebRtcSession::ExpectSetRemoteDescription(Action action) {
- return ((action == kOffer && state() == STATE_INIT) ||
- // update remote offer
- (action == kOffer && state() == STATE_RECEIVEDINITIATE) ||
- // update the current ongoing session
- (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
- (action == kOffer && state() == STATE_SENTACCEPT) ||
- (action == kOffer && state() == STATE_INPROGRESS) ||
- // accept local offer
- (action == kAnswer && state() == STATE_SENTINITIATE) ||
- (action == kAnswer && state() == STATE_RECEIVEDPRACCEPT) ||
- (action == kPrAnswer && state() == STATE_SENTINITIATE) ||
- (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT));
-}
-
void WebRtcSession::OnCandidatesAllocationDone() {
ASSERT(signaling_thread()->IsCurrent());
if (ice_observer_) {
@@ -1378,7 +1324,7 @@
}
// Returns false if bundle is enabled and rtcp_mux is disabled.
-bool WebRtcSession::VerifyBundleSettings(const SessionDescription* desc) {
+bool WebRtcSession::ValidateBundleSettings(const SessionDescription* desc) {
bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
if (!bundle_enabled)
return true;
@@ -1409,4 +1355,79 @@
return description->rtcp_mux();
}
+bool WebRtcSession::ValidateSessionDescription(
+ const SessionDescriptionInterface* sdesc,
+ cricket::ContentSource source, std::string* error_desc) {
+
+ if (error() != cricket::BaseSession::ERROR_NONE) {
+ return BadSdp(source, SessionErrorMsg(error()), error_desc);
+ }
+
+ if (!sdesc || !sdesc->description()) {
+ return BadSdp(source, kInvalidSdp, error_desc);
+ }
+
+ std::string type = sdesc->type();
+ Action action = GetAction(sdesc->type());
+ if (source == cricket::CS_LOCAL) {
+ if (!ExpectSetLocalDescription(action))
+ return BadSdp(source, BadStateErrMsg(type, state()), error_desc);
+ } else {
+ if (!ExpectSetRemoteDescription(action))
+ return BadSdp(source, BadStateErrMsg(type, state()), error_desc);
+ }
+
+ // Verify crypto settings.
+ if (webrtc_session_desc_factory_->secure() == cricket::SEC_REQUIRED &&
+ !VerifyCrypto(sdesc->description())) {
+ return BadSdp(source, kSdpWithoutCrypto, error_desc);
+ }
+
+ if (!ValidateBundleSettings(sdesc->description())) {
+ return BadSdp(source, kBundleWithoutRtcpMux, error_desc);
+ }
+
+ // Verify m-lines in Answer when compared against Offer.
+ if (action == kAnswer) {
+ const cricket::SessionDescription* offer_desc =
+ (source == cricket::CS_LOCAL) ? remote_description()->description() :
+ local_description()->description();
+ if (!VerifyMediaDescriptions(sdesc->description(), offer_desc)) {
+ return BadSdp(source, kMlineMismatch, error_desc);
+ }
+ }
+
+ return true;
+}
+
+bool WebRtcSession::ExpectSetLocalDescription(Action action) {
+ return ((action == kOffer && state() == STATE_INIT) ||
+ // update local offer
+ (action == kOffer && state() == STATE_SENTINITIATE) ||
+ // update the current ongoing session.
+ (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
+ (action == kOffer && state() == STATE_SENTACCEPT) ||
+ (action == kOffer && state() == STATE_INPROGRESS) ||
+ // accept remote offer
+ (action == kAnswer && state() == STATE_RECEIVEDINITIATE) ||
+ (action == kAnswer && state() == STATE_SENTPRACCEPT) ||
+ (action == kPrAnswer && state() == STATE_RECEIVEDINITIATE) ||
+ (action == kPrAnswer && state() == STATE_SENTPRACCEPT));
+}
+
+bool WebRtcSession::ExpectSetRemoteDescription(Action action) {
+ return ((action == kOffer && state() == STATE_INIT) ||
+ // update remote offer
+ (action == kOffer && state() == STATE_RECEIVEDINITIATE) ||
+ // update the current ongoing session
+ (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
+ (action == kOffer && state() == STATE_SENTACCEPT) ||
+ (action == kOffer && state() == STATE_INPROGRESS) ||
+ // accept local offer
+ (action == kAnswer && state() == STATE_SENTINITIATE) ||
+ (action == kAnswer && state() == STATE_RECEIVEDPRACCEPT) ||
+ (action == kPrAnswer && state() == STATE_SENTINITIATE) ||
+ (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT));
+}
+
} // namespace webrtc
diff --git a/app/webrtc/webrtcsession.h b/app/webrtc/webrtcsession.h
index 202ca66..0cb049f 100644
--- a/app/webrtc/webrtcsession.h
+++ b/app/webrtc/webrtcsession.h
@@ -130,6 +130,9 @@
void set_secure_policy(cricket::SecureMediaPolicy secure_policy);
cricket::SecureMediaPolicy secure_policy() const;
+ // Get current ssl role from transport.
+ bool GetSslRole(talk_base::SSLRole* role);
+
// Generic error message callback from WebRtcSession.
// TODO - It may be necessary to supply error code as well.
sigslot::signal0<> SignalError;
@@ -152,9 +155,6 @@
return remote_desc_.get();
}
- void set_secure(cricket::SecureMediaPolicy secure_policy);
- cricket::SecureMediaPolicy secure();
-
// Get the id used as a media stream track's "id" field from ssrc.
virtual bool GetTrackIdBySsrc(uint32 ssrc, std::string* id);
@@ -223,10 +223,6 @@
const cricket::Candidates& candidates);
virtual void OnCandidatesAllocationDone();
- // Check if a call to SetLocalDescription is acceptable with |action|.
- bool ExpectSetLocalDescription(Action action);
- // Check if a call to SetRemoteDescription is acceptable with |action|.
- bool ExpectSetRemoteDescription(Action action);
// Creates local session description with audio and video contents.
bool CreateDefaultLocalDescription();
// Enables media channels to allow sending of media.
@@ -275,8 +271,20 @@
std::string BadStateErrMsg(const std::string& type, State state);
void SetIceConnectionState(PeerConnectionInterface::IceConnectionState state);
- bool VerifyBundleSettings(const cricket::SessionDescription* desc);
+ bool ValidateBundleSettings(const cricket::SessionDescription* desc);
bool HasRtcpMuxEnabled(const cricket::ContentInfo* content);
+ // Below methods are helper methods which verifies SDP.
+ bool ValidateSessionDescription(const SessionDescriptionInterface* sdesc,
+ cricket::ContentSource source,
+ std::string* error_desc);
+
+ // Check if a call to SetLocalDescription is acceptable with |action|.
+ bool ExpectSetLocalDescription(Action action);
+ // Check if a call to SetRemoteDescription is acceptable with |action|.
+ bool ExpectSetRemoteDescription(Action action);
+ // Verifies a=setup attribute as per RFC 5763.
+ bool ValidateDtlsSetupAttribute(const cricket::SessionDescription* desc,
+ Action action);
talk_base::scoped_ptr<cricket::VoiceChannel> voice_channel_;
talk_base::scoped_ptr<cricket::VideoChannel> video_channel_;
diff --git a/app/webrtc/webrtcsessiondescriptionfactory.cc b/app/webrtc/webrtcsessiondescriptionfactory.cc
index 2021085..13f54a7 100644
--- a/app/webrtc/webrtcsessiondescriptionfactory.cc
+++ b/app/webrtc/webrtcsessiondescriptionfactory.cc
@@ -343,6 +343,13 @@
// an answer should also contain new ice ufrag and password if an offer has
// been received with new ufrag and password.
request.options.transport_options.ice_restart = session_->IceRestartPending();
+ // We should pass current ssl role to the transport description factory, if
+ // there is already an existing ongoing session.
+ talk_base::SSLRole ssl_role;
+ if (session_->GetSslRole(&ssl_role)) {
+ request.options.transport_options.prefer_passive_role =
+ (talk_base::SSL_SERVER == ssl_role);
+ }
cricket::SessionDescription* desc(session_desc_factory_.CreateAnswer(
static_cast<cricket::BaseSession*>(session_)->remote_description(),
diff --git a/base/macsocketserver_unittest.cc b/base/macsocketserver_unittest.cc
index 07cce26..ba407c1 100644
--- a/base/macsocketserver_unittest.cc
+++ b/base/macsocketserver_unittest.cc
@@ -132,7 +132,7 @@
SocketTest::TestConnectWithDnsLookupIPv6();
}
-TEST_F(MacAsyncSocketTest, TestConnectFailIPv4) {
+TEST_F(MacAsyncSocketTest, DISABLED_TestConnectFailIPv4) {
SocketTest::TestConnectFailIPv4();
}
diff --git a/base/messagehandler.cc b/base/messagehandler.cc
index 5b3585b..16f9a21 100644
--- a/base/messagehandler.cc
+++ b/base/messagehandler.cc
@@ -2,26 +2,26 @@
* libjingle
* Copyright 2004--2005, Google Inc.
*
- * Redistribution and use in source and binary forms, with or without
+ * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
- * 1. Redistributions of source code must retain the above copyright notice,
+ * 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
+ * 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
@@ -31,7 +31,7 @@
namespace talk_base {
MessageHandler::~MessageHandler() {
- MessageQueueManager::Instance()->Clear(this);
+ MessageQueueManager::Clear(this);
}
} // namespace talk_base
diff --git a/base/messagequeue.cc b/base/messagequeue.cc
index 5c40622..15b700f 100644
--- a/base/messagequeue.cc
+++ b/base/messagequeue.cc
@@ -42,7 +42,7 @@
//------------------------------------------------------------------
// MessageQueueManager
-MessageQueueManager* MessageQueueManager::instance_;
+MessageQueueManager* MessageQueueManager::instance_ = NULL;
MessageQueueManager* MessageQueueManager::Instance() {
// Note: This is not thread safe, but it is first called before threads are
@@ -52,6 +52,10 @@
return instance_;
}
+bool MessageQueueManager::IsInitialized() {
+ return instance_ != NULL;
+}
+
MessageQueueManager::MessageQueueManager() {
}
@@ -59,6 +63,9 @@
}
void MessageQueueManager::Add(MessageQueue *message_queue) {
+ return Instance()->AddInternal(message_queue);
+}
+void MessageQueueManager::AddInternal(MessageQueue *message_queue) {
// MessageQueueManager methods should be non-reentrant, so we
// ASSERT that is the case. If any of these ASSERT, please
// contact bpm or jbeda.
@@ -68,6 +75,12 @@
}
void MessageQueueManager::Remove(MessageQueue *message_queue) {
+ // If there isn't a message queue manager instance, then there isn't a queue
+ // to remove.
+ if (!instance_) return;
+ return Instance()->RemoveInternal(message_queue);
+}
+void MessageQueueManager::RemoveInternal(MessageQueue *message_queue) {
ASSERT(!crit_.CurrentThreadIsOwner()); // See note above.
// If this is the last MessageQueue, destroy the manager as well so that
// we don't leak this object at program shutdown. As mentioned above, this is
@@ -91,6 +104,12 @@
}
void MessageQueueManager::Clear(MessageHandler *handler) {
+ // If there isn't a message queue manager instance, then there aren't any
+ // queues to remove this handler from.
+ if (!instance_) return;
+ return Instance()->ClearInternal(handler);
+}
+void MessageQueueManager::ClearInternal(MessageHandler *handler) {
ASSERT(!crit_.CurrentThreadIsOwner()); // See note above.
CritScope cs(&crit_);
std::vector<MessageQueue *>::iterator iter;
@@ -122,7 +141,7 @@
// is going away.
SignalQueueDestroyed();
if (active_) {
- MessageQueueManager::Instance()->Remove(this);
+ MessageQueueManager::Remove(this);
Clear(NULL);
}
if (ss_) {
@@ -381,7 +400,7 @@
ASSERT(crit_.CurrentThreadIsOwner());
if (!active_) {
active_ = true;
- MessageQueueManager::Instance()->Add(this);
+ MessageQueueManager::Add(this);
}
}
diff --git a/base/messagequeue.h b/base/messagequeue.h
index 331f207..7b38ba0 100644
--- a/base/messagequeue.h
+++ b/base/messagequeue.h
@@ -53,16 +53,26 @@
class MessageQueueManager {
public:
- static MessageQueueManager* Instance();
+ static void Add(MessageQueue *message_queue);
+ static void Remove(MessageQueue *message_queue);
+ static void Clear(MessageHandler *handler);
- void Add(MessageQueue *message_queue);
- void Remove(MessageQueue *message_queue);
- void Clear(MessageHandler *handler);
+ // For testing purposes, we expose whether or not the MessageQueueManager
+ // instance has been initialized. It has no other use relative to the rest of
+ // the functions of this class, which auto-initialize the underlying
+ // MessageQueueManager instance when necessary.
+ static bool IsInitialized();
private:
+ static MessageQueueManager* Instance();
+
MessageQueueManager();
~MessageQueueManager();
+ void AddInternal(MessageQueue *message_queue);
+ void RemoveInternal(MessageQueue *message_queue);
+ void ClearInternal(MessageHandler *handler);
+
static MessageQueueManager* instance_;
// This list contains 'active' MessageQueues.
std::vector<MessageQueue *> message_queues_;
diff --git a/base/messagequeue_unittest.cc b/base/messagequeue_unittest.cc
index 8e55548..55c0166 100644
--- a/base/messagequeue_unittest.cc
+++ b/base/messagequeue_unittest.cc
@@ -130,3 +130,10 @@
EXPECT_TRUE(deleted);
}
+TEST(MessageQueueManager, DISABLED_Clear) {
+ bool deleted = false;
+ DeletedMessageHandler* handler = new DeletedMessageHandler(&deleted);
+ delete handler;
+ EXPECT_TRUE(deleted);
+ EXPECT_FALSE(MessageQueueManager::IsInitialized());
+}
diff --git a/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java b/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java
index 43f5f55..936cb2b 100644
--- a/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java
+++ b/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java
@@ -327,11 +327,13 @@
@Override public void onAddStream(final MediaStream stream){
runOnUiThread(new Runnable() {
public void run() {
- abortUnless(stream.audioTracks.size() == 1 &&
- stream.videoTracks.size() == 1,
+ abortUnless(stream.audioTracks.size() <= 1 &&
+ stream.videoTracks.size() <= 1,
"Weird-looking stream: " + stream);
- stream.videoTracks.get(0).addRenderer(new VideoRenderer(
- new VideoCallbacks(vsv, VideoStreamsView.Endpoint.REMOTE)));
+ if (stream.videoTracks.size() == 1) {
+ stream.videoTracks.get(0).addRenderer(new VideoRenderer(
+ new VideoCallbacks(vsv, VideoStreamsView.Endpoint.REMOTE)));
+ }
}
});
}
diff --git a/libjingle.gyp b/libjingle.gyp
index 15d92d1..aa61360 100755
--- a/libjingle.gyp
+++ b/libjingle.gyp
@@ -1032,6 +1032,7 @@
'p2p/base/transportchannelimpl.h',
'p2p/base/transportchannelproxy.cc',
'p2p/base/transportchannelproxy.h',
+ 'p2p/base/transportdescription.cc',
'p2p/base/transportdescription.h',
'p2p/base/transportdescriptionfactory.cc',
'p2p/base/transportdescriptionfactory.h',
diff --git a/media/base/videocapturer.cc b/media/base/videocapturer.cc
index 3bc2373..0cef81b 100644
--- a/media/base/videocapturer.cc
+++ b/media/base/videocapturer.cc
@@ -338,7 +338,7 @@
scaled_height_ = scaled_height;
}
if (FOURCC_ARGB == captured_frame->fourcc &&
- (scaled_width != captured_frame->height ||
+ (scaled_width != captured_frame->width ||
scaled_height != captured_frame->height)) {
CapturedFrame* scaled_frame = const_cast<CapturedFrame*>(captured_frame);
// Compute new width such that width * height is less than maximum but
diff --git a/media/base/videocapturer_unittest.cc b/media/base/videocapturer_unittest.cc
index a6ce3ba..ae461a9 100644
--- a/media/base/videocapturer_unittest.cc
+++ b/media/base/videocapturer_unittest.cc
@@ -681,3 +681,29 @@
capturer_.ConstrainSupportedFormats(vga_format);
EXPECT_TRUE(HdFormatInList(*capturer_.GetSupportedFormats()));
}
+
+TEST_F(VideoCapturerTest, BlacklistAllFormats) {
+ cricket::VideoFormat vga_format(640, 480,
+ cricket::VideoFormat::FpsToInterval(30),
+ cricket::FOURCC_I420);
+ std::vector<cricket::VideoFormat> supported_formats;
+ // Mock a device that only supports HD formats.
+ supported_formats.push_back(cricket::VideoFormat(1280, 720,
+ cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420));
+ supported_formats.push_back(cricket::VideoFormat(1920, 1080,
+ cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420));
+ capturer_.ResetSupportedFormats(supported_formats);
+ EXPECT_EQ(2u, capturer_.GetSupportedFormats()->size());
+ // Now, enable the list, which would exclude both formats. However, since
+ // only HD formats are available, we refuse to filter at all, so we don't
+ // break this camera.
+ capturer_.set_enable_camera_list(true);
+ capturer_.ConstrainSupportedFormats(vga_format);
+ EXPECT_EQ(2u, capturer_.GetSupportedFormats()->size());
+ // To make sure it's not just the camera list being broken, add in VGA and
+ // try again. This time, only the VGA format should be there.
+ supported_formats.push_back(vga_format);
+ capturer_.ResetSupportedFormats(supported_formats);
+ ASSERT_EQ(1u, capturer_.GetSupportedFormats()->size());
+ EXPECT_EQ(vga_format.height, capturer_.GetSupportedFormats()->at(0).height);
+}
diff --git a/p2p/base/constants.cc b/p2p/base/constants.cc
index 12336d4..27d4309 100644
--- a/p2p/base/constants.cc
+++ b/p2p/base/constants.cc
@@ -262,4 +262,10 @@
const buzz::StaticQName QN_VOICEMAIL_REGARDING = { NS_VOICEMAIL, "regarding" };
#endif
+// From RFC 4145, SDP setup attribute values.
+const char CONNECTIONROLE_ACTIVE_STR[] = "active";
+const char CONNECTIONROLE_PASSIVE_STR[] = "passive";
+const char CONNECTIONROLE_ACTPASS_STR[] = "actpass";
+const char CONNECTIONROLE_HOLDCONN_STR[] = "holdconn";
+
} // namespace cricket
diff --git a/p2p/base/constants.h b/p2p/base/constants.h
index f7e5671..99e006a 100644
--- a/p2p/base/constants.h
+++ b/p2p/base/constants.h
@@ -261,6 +261,12 @@
extern const buzz::StaticQName QN_VOICEMAIL_REGARDING;
#endif
+// RFC 4145, SDP setup attribute values.
+extern const char CONNECTIONROLE_ACTIVE_STR[];
+extern const char CONNECTIONROLE_PASSIVE_STR[];
+extern const char CONNECTIONROLE_ACTPASS_STR[];
+extern const char CONNECTIONROLE_HOLDCONN_STR[];
+
} // namespace cricket
#endif // TALK_P2P_BASE_CONSTANTS_H_
diff --git a/p2p/base/dtlstransport.h b/p2p/base/dtlstransport.h
index a6e3b82..61e78f7 100644
--- a/p2p/base/dtlstransport.h
+++ b/p2p/base/dtlstransport.h
@@ -55,7 +55,6 @@
~DtlsTransport() {
Base::DestroyAllChannels();
}
-
virtual void SetIdentity_w(talk_base::SSLIdentity* identity) {
identity_ = identity;
}
@@ -100,6 +99,74 @@
if (remote_fp && local_fp) {
remote_fingerprint_.reset(new talk_base::SSLFingerprint(*remote_fp));
+
+ // From RFC 4145, section-4.1, The following are the values that the
+ // 'setup' attribute can take in an offer/answer exchange:
+ // Offer Answer
+ // ________________
+ // active passive / holdconn
+ // passive active / holdconn
+ // actpass active / passive / holdconn
+ // holdconn holdconn
+ //
+ // Set the role that is most conformant with RFC 5763, Section 5, bullet 1
+ // The endpoint MUST use the setup attribute defined in [RFC4145].
+ // The endpoint that is the offerer MUST use the setup attribute
+ // value of setup:actpass and be prepared to receive a client_hello
+ // before it receives the answer. The answerer MUST use either a
+ // setup attribute value of setup:active or setup:passive. Note that
+ // if the answerer uses setup:passive, then the DTLS handshake will
+ // not begin until the answerer is received, which adds additional
+ // latency. setup:active allows the answer and the DTLS handshake to
+ // occur in parallel. Thus, setup:active is RECOMMENDED. Whichever
+ // party is active MUST initiate a DTLS handshake by sending a
+ // ClientHello over each flow (host/port quartet).
+ // IOW - actpass and passive modes should be treated as server and
+ // active as client.
+ ConnectionRole local_connection_role =
+ Base::local_description()->connection_role;
+ ConnectionRole remote_connection_role =
+ Base::remote_description()->connection_role;
+
+ bool is_remote_server = false;
+ if (local_role == CA_OFFER) {
+ if (local_connection_role != CONNECTIONROLE_ACTPASS) {
+ LOG(LS_ERROR) << "Offerer must use actpass value for setup attribute";
+ return false;
+ }
+
+ if (remote_connection_role == CONNECTIONROLE_ACTIVE ||
+ remote_connection_role == CONNECTIONROLE_PASSIVE ||
+ remote_connection_role == CONNECTIONROLE_NONE) {
+ is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE);
+ } else {
+ LOG(LS_ERROR) << "Answerer must use either active or passive value "
+ << "for setup attribute";
+ return false;
+ }
+ // If remote is NONE or ACTIVE it will act as client.
+ } else {
+ if (remote_connection_role != CONNECTIONROLE_ACTPASS &&
+ remote_connection_role != CONNECTIONROLE_NONE) {
+ LOG(LS_ERROR) << "Offerer must use actpass value for setup attribute";
+ return false;
+ }
+
+ if (local_connection_role == CONNECTIONROLE_ACTIVE ||
+ local_connection_role == CONNECTIONROLE_PASSIVE) {
+ is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE);
+ } else {
+ LOG(LS_ERROR) << "Answerer must use either active or passive value "
+ << "for setup attribute";
+ return false;
+ }
+
+ // If local is passive, local will act as server.
+ }
+
+ secure_role_ = is_remote_server ? talk_base::SSL_CLIENT :
+ talk_base::SSL_SERVER;
+
} else if (local_fp && (local_role == CA_ANSWER)) {
LOG(LS_ERROR)
<< "Local fingerprint supplied when caller didn't offer DTLS";
@@ -128,18 +195,34 @@
Base::DestroyTransportChannel(base_channel);
}
+ virtual bool GetSslRole_w(talk_base::SSLRole* ssl_role) const {
+ ASSERT(ssl_role != NULL);
+ *ssl_role = secure_role_;
+ return true;
+ }
+
private:
- virtual void ApplyNegotiatedTransportDescription_w(
+ virtual bool ApplyNegotiatedTransportDescription_w(
TransportChannelImpl* channel) {
- channel->SetRemoteFingerprint(
+ // Set ssl role. Role must be set before fingerprint is applied, which
+ // initiates DTLS setup.
+ if (!channel->SetSslRole(secure_role_)) {
+ LOG(LS_INFO) << "Failed to set ssl role for the channel.";
+ return false;
+ }
+ // Apply remote fingerprint.
+ if (!channel->SetRemoteFingerprint(
remote_fingerprint_->algorithm,
reinterpret_cast<const uint8 *>(remote_fingerprint_->
digest.data()),
- remote_fingerprint_->digest.length());
- Base::ApplyNegotiatedTransportDescription_w(channel);
+ remote_fingerprint_->digest.length())) {
+ return false;
+ }
+ return Base::ApplyNegotiatedTransportDescription_w(channel);
}
talk_base::SSLIdentity* identity_;
+ talk_base::SSLRole secure_role_;
talk_base::scoped_ptr<talk_base::SSLFingerprint> remote_fingerprint_;
};
diff --git a/p2p/base/dtlstransportchannel.cc b/p2p/base/dtlstransportchannel.cc
index 6cf400c..40b4e31 100644
--- a/p2p/base/dtlstransportchannel.cc
+++ b/p2p/base/dtlstransportchannel.cc
@@ -102,7 +102,7 @@
downward_(NULL),
dtls_state_(STATE_NONE),
local_identity_(NULL),
- dtls_role_(talk_base::SSL_CLIENT) {
+ ssl_role_(talk_base::SSL_CLIENT) {
channel_->SignalReadableState.connect(this,
&DtlsTransportChannelWrapper::OnReadableState);
channel_->SignalWritableState.connect(this,
@@ -171,18 +171,22 @@
return true;
}
-void DtlsTransportChannelWrapper::SetIceRole(IceRole role) {
- // TODO(ekr@rtfm.com): Forbid this if Connect() has been called.
- ASSERT(dtls_state_ < STATE_ACCEPTED);
+bool DtlsTransportChannelWrapper::SetSslRole(talk_base::SSLRole role) {
+ if (dtls_state_ == STATE_OPEN) {
+ if (ssl_role_ != role) {
+ LOG(LS_ERROR) << "SSL Role can't be reversed after the session is setup.";
+ return false;
+ }
+ return true;
+ }
- // Set the role that is most conformant with RFC 5763, Section 5, bullet 1:
- // The endpoint that is the offerer MUST [...] be prepared to receive
- // a client_hello before it receives the answer.
- // (IOW, the offerer is the server, and the answerer is the client).
- dtls_role_ = (role == ICEROLE_CONTROLLING) ?
- talk_base::SSL_SERVER : talk_base::SSL_CLIENT;
+ ssl_role_ = role;
+ return true;
+}
- channel_->SetIceRole(role);
+bool DtlsTransportChannelWrapper::GetSslRole(talk_base::SSLRole* role) const {
+ *role = ssl_role_;
+ return true;
}
bool DtlsTransportChannelWrapper::SetRemoteFingerprint(
@@ -201,12 +205,12 @@
// hasn't been called.
if (dtls_state_ > STATE_OFFERED ||
(dtls_state_ == STATE_NONE && !digest_alg.empty())) {
- LOG_J(LS_ERROR, this) << "Can't set DTLS remote settings in this state";
+ LOG_J(LS_ERROR, this) << "Can't set DTLS remote settings in this state.";
return false;
}
if (digest_alg.empty()) {
- LOG_J(LS_INFO, this) << "Other side didn't support DTLS";
+ LOG_J(LS_INFO, this) << "Other side didn't support DTLS.";
dtls_state_ = STATE_NONE;
return true;
}
@@ -230,7 +234,7 @@
dtls_.reset(talk_base::SSLStreamAdapter::Create(downward));
if (!dtls_) {
- LOG_J(LS_ERROR, this) << "Failed to create DTLS adapter";
+ LOG_J(LS_ERROR, this) << "Failed to create DTLS adapter.";
delete downward;
return false;
}
@@ -239,27 +243,27 @@
dtls_->SetIdentity(local_identity_->GetReference());
dtls_->SetMode(talk_base::SSL_MODE_DTLS);
- dtls_->SetServerRole(dtls_role_);
+ dtls_->SetServerRole(ssl_role_);
dtls_->SignalEvent.connect(this, &DtlsTransportChannelWrapper::OnDtlsEvent);
if (!dtls_->SetPeerCertificateDigest(
remote_fingerprint_algorithm_,
reinterpret_cast<unsigned char *>(remote_fingerprint_value_.data()),
remote_fingerprint_value_.length())) {
- LOG_J(LS_ERROR, this) << "Couldn't set DTLS certificate digest";
+ LOG_J(LS_ERROR, this) << "Couldn't set DTLS certificate digest.";
return false;
}
// Set up DTLS-SRTP, if it's been enabled.
if (!srtp_ciphers_.empty()) {
if (!dtls_->SetDtlsSrtpCiphers(srtp_ciphers_)) {
- LOG_J(LS_ERROR, this) << "Couldn't set DTLS-SRTP ciphers";
+ LOG_J(LS_ERROR, this) << "Couldn't set DTLS-SRTP ciphers.";
return false;
}
} else {
- LOG_J(LS_INFO, this) << "Not using DTLS";
+ LOG_J(LS_INFO, this) << "Not using DTLS.";
}
- LOG_J(LS_INFO, this) << "DTLS setup complete";
+ LOG_J(LS_INFO, this) << "DTLS setup complete.";
return true;
}
@@ -349,7 +353,7 @@
ASSERT(talk_base::Thread::Current() == worker_thread_);
ASSERT(channel == channel_);
LOG_J(LS_VERBOSE, this)
- << "DTLSTransportChannelWrapper: channel readable state changed";
+ << "DTLSTransportChannelWrapper: channel readable state changed.";
if (dtls_state_ == STATE_NONE || dtls_state_ == STATE_OPEN) {
set_readable(channel_->readable());
@@ -361,7 +365,7 @@
ASSERT(talk_base::Thread::Current() == worker_thread_);
ASSERT(channel == channel_);
LOG_J(LS_VERBOSE, this)
- << "DTLSTransportChannelWrapper: channel writable state changed";
+ << "DTLSTransportChannelWrapper: channel writable state changed.";
switch (dtls_state_) {
case STATE_NONE:
@@ -416,13 +420,13 @@
// decide to take this as evidence that the other
// side is ready to do DTLS and start the handshake
// on our end
- LOG_J(LS_WARNING, this) << "Received packet before we know if we are doing "
- << "DTLS or not; dropping";
+ LOG_J(LS_WARNING, this) << "Received packet before we know if we are "
+ << "doing DTLS or not; dropping.";
break;
case STATE_ACCEPTED:
// Drop packets received before DTLS has actually started
- LOG_J(LS_INFO, this) << "Dropping packet received before DTLS started";
+ LOG_J(LS_INFO, this) << "Dropping packet received before DTLS started.";
break;
case STATE_STARTED:
@@ -431,19 +435,20 @@
// Is this potentially a DTLS packet?
if (IsDtlsPacket(data, size)) {
if (!HandleDtlsPacket(data, size)) {
- LOG_J(LS_ERROR, this) << "Failed to handle DTLS packet";
+ LOG_J(LS_ERROR, this) << "Failed to handle DTLS packet.";
return;
}
} else {
// Not a DTLS packet; our handshake should be complete by now.
if (dtls_state_ != STATE_OPEN) {
- LOG_J(LS_ERROR, this) << "Received non-DTLS packet before DTLS complete";
+ LOG_J(LS_ERROR, this) << "Received non-DTLS packet before DTLS "
+ << "complete.";
return;
}
// And it had better be a SRTP packet.
if (!IsRtpPacket(data, size)) {
- LOG_J(LS_ERROR, this) << "Received unexpected non-DTLS packet";
+ LOG_J(LS_ERROR, this) << "Received unexpected non-DTLS packet.";
return;
}
@@ -472,7 +477,7 @@
ASSERT(dtls == dtls_.get());
if (sig & talk_base::SE_OPEN) {
// This is the first time.
- LOG_J(LS_INFO, this) << "DTLS handshake complete";
+ LOG_J(LS_INFO, this) << "DTLS handshake complete.";
if (dtls_->GetState() == talk_base::SS_OPEN) {
// The check for OPEN shouldn't be necessary but let's make
// sure we don't accidentally frob the state if it's closed.
diff --git a/p2p/base/dtlstransportchannel.h b/p2p/base/dtlstransportchannel.h
index 8321024..ed0db68 100644
--- a/p2p/base/dtlstransportchannel.h
+++ b/p2p/base/dtlstransportchannel.h
@@ -121,8 +121,9 @@
TransportChannelImpl* channel);
virtual ~DtlsTransportChannelWrapper();
- virtual void SetIceRole(IceRole ice_role);
- // Returns current transport role of the channel.
+ virtual void SetIceRole(IceRole role) {
+ channel_->SetIceRole(role);
+ }
virtual IceRole GetIceRole() const {
return channel_->GetIceRole();
}
@@ -158,6 +159,9 @@
// Find out which DTLS-SRTP cipher was negotiated
virtual bool GetSrtpCipher(std::string* cipher);
+ virtual bool GetSslRole(talk_base::SSLRole* role) const;
+ virtual bool SetSslRole(talk_base::SSLRole role);
+
// Once DTLS has established (i.e., this channel is writable), this method
// extracts the keys negotiated during the DTLS handshake, for use in external
// encryption. DTLS-SRTP uses this to extract the needed SRTP keys.
@@ -234,7 +238,7 @@
std::vector<std::string> srtp_ciphers_; // SRTP ciphers to use with DTLS.
State dtls_state_;
talk_base::SSLIdentity* local_identity_;
- talk_base::SSLRole dtls_role_;
+ talk_base::SSLRole ssl_role_;
talk_base::Buffer remote_fingerprint_value_;
std::string remote_fingerprint_algorithm_;
diff --git a/p2p/base/dtlstransportchannel_unittest.cc b/p2p/base/dtlstransportchannel_unittest.cc
index c46839f..7507101 100644
--- a/p2p/base/dtlstransportchannel_unittest.cc
+++ b/p2p/base/dtlstransportchannel_unittest.cc
@@ -56,6 +56,10 @@
return ((b & 0xC0) == 0x80);
}
+using cricket::ConnectionRole;
+
+enum Flags { NF_REOFFER = 0x1, NF_EXPECT_FAILURE = 0x2 };
+
class DtlsTestClient : public sigslot::has_slots<> {
public:
DtlsTestClient(const std::string& name,
@@ -77,6 +81,7 @@
void CreateIdentity() {
identity_.reset(talk_base::SSLIdentity::Generate(name_));
}
+ talk_base::SSLIdentity* identity() { return identity_.get(); }
void SetupSrtp() {
ASSERT(identity_.get() != NULL);
use_dtls_srtp_ = true;
@@ -108,6 +113,9 @@
this, &DtlsTestClient::OnFakeTransportChannelReadPacket);
}
}
+
+ cricket::Transport* transport() { return transport_.get(); }
+
cricket::FakeTransportChannel* GetFakeChannel(int component) {
cricket::TransportChannelImpl* ch = transport_->GetChannel(component);
cricket::DtlsTransportChannelWrapper* wrapper =
@@ -118,13 +126,20 @@
// Offer DTLS if we have an identity; pass in a remote fingerprint only if
// both sides support DTLS.
- void Negotiate(DtlsTestClient* peer) {
- Negotiate(identity_.get(), (identity_) ? peer->identity_.get() : NULL);
+ void Negotiate(DtlsTestClient* peer, cricket::ContentAction action,
+ ConnectionRole local_role, ConnectionRole remote_role,
+ int flags) {
+ Negotiate(identity_.get(), (identity_) ? peer->identity_.get() : NULL,
+ action, local_role, remote_role, flags);
}
// Allow any DTLS configuration to be specified (including invalid ones).
void Negotiate(talk_base::SSLIdentity* local_identity,
- talk_base::SSLIdentity* remote_identity) {
+ talk_base::SSLIdentity* remote_identity,
+ cricket::ContentAction action,
+ ConnectionRole local_role,
+ ConnectionRole remote_role,
+ int flags) {
talk_base::scoped_ptr<talk_base::SSLFingerprint> local_fingerprint;
talk_base::scoped_ptr<talk_base::SSLFingerprint> remote_fingerprint;
if (local_identity) {
@@ -137,7 +152,9 @@
talk_base::DIGEST_SHA_1, remote_identity));
ASSERT_TRUE(remote_fingerprint.get() != NULL);
}
- if (use_dtls_srtp_) {
+
+ if (use_dtls_srtp_ && !(flags & NF_REOFFER)) {
+ // SRTP ciphers will be set only in the beginning.
for (std::vector<cricket::DtlsTransportChannelWrapper*>::iterator it =
channels_.begin(); it != channels_.end(); ++it) {
std::vector<std::string> ciphers;
@@ -150,17 +167,32 @@
cricket::NS_GINGLE_P2P : cricket::NS_JINGLE_ICE_UDP;
cricket::TransportDescription local_desc(
transport_type, std::vector<std::string>(), kIceUfrag1, kIcePwd1,
- cricket::ICEMODE_FULL, local_fingerprint.get(),
+ cricket::ICEMODE_FULL, local_role,
+ // If remote if the offerer and has no DTLS support, answer will be
+ // without any fingerprint.
+ (action == cricket::CA_ANSWER && !remote_identity) ?
+ NULL : local_fingerprint.get(),
cricket::Candidates());
- ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc,
- cricket::CA_OFFER));
+
cricket::TransportDescription remote_desc(
transport_type, std::vector<std::string>(), kIceUfrag1, kIcePwd1,
- cricket::ICEMODE_FULL, remote_fingerprint.get(),
+ cricket::ICEMODE_FULL, remote_role, remote_fingerprint.get(),
cricket::Candidates());
- ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc,
- cricket::CA_ANSWER));
+ bool expect_success = (flags & NF_EXPECT_FAILURE) ? false : true;
+ // If |expect_success| is false, expect SRTD or SLTD to fail when
+ // content action is CA_ANSWER.
+ if (action == cricket::CA_OFFER) {
+ ASSERT_TRUE(transport_->SetLocalTransportDescription(
+ local_desc, cricket::CA_OFFER));
+ ASSERT_EQ(expect_success, transport_->SetRemoteTransportDescription(
+ remote_desc, cricket::CA_ANSWER));
+ } else {
+ ASSERT_TRUE(transport_->SetRemoteTransportDescription(
+ remote_desc, cricket::CA_OFFER));
+ ASSERT_EQ(expect_success, transport_->SetLocalTransportDescription(
+ local_desc, cricket::CA_ANSWER));
+ }
negotiated_dtls_ = (local_identity && remote_identity);
}
@@ -373,8 +405,8 @@
use_dtls_srtp_ = true;
}
- bool Connect() {
- Negotiate();
+ bool Connect(ConnectionRole client1_role, ConnectionRole client2_role) {
+ Negotiate(client1_role, client2_role);
bool rv = client1_.Connect(&client2_);
EXPECT_TRUE(rv);
@@ -387,8 +419,20 @@
// Check that we used the right roles.
if (use_dtls_) {
- client1_.CheckRole(talk_base::SSL_SERVER);
- client2_.CheckRole(talk_base::SSL_CLIENT);
+ talk_base::SSLRole client1_ssl_role =
+ (client1_role == cricket::CONNECTIONROLE_ACTIVE ||
+ (client2_role == cricket::CONNECTIONROLE_PASSIVE &&
+ client1_role == cricket::CONNECTIONROLE_ACTPASS)) ?
+ talk_base::SSL_CLIENT : talk_base::SSL_SERVER;
+
+ talk_base::SSLRole client2_ssl_role =
+ (client2_role == cricket::CONNECTIONROLE_ACTIVE ||
+ (client1_role == cricket::CONNECTIONROLE_PASSIVE &&
+ client2_role == cricket::CONNECTIONROLE_ACTPASS)) ?
+ talk_base::SSL_CLIENT : talk_base::SSL_SERVER;
+
+ client1_.CheckRole(client1_ssl_role);
+ client2_.CheckRole(client2_ssl_role);
}
// Check that we negotiated the right ciphers.
@@ -402,11 +446,55 @@
return true;
}
+
+ bool Connect() {
+ // By default, Client1 will be Server and Client2 will be Client.
+ return Connect(cricket::CONNECTIONROLE_ACTPASS,
+ cricket::CONNECTIONROLE_ACTIVE);
+ }
+
void Negotiate() {
+ Negotiate(cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTIVE);
+ }
+
+ void Negotiate(ConnectionRole client1_role, ConnectionRole client2_role) {
client1_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLING);
client2_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLED);
- client2_.Negotiate(&client1_);
- client1_.Negotiate(&client2_);
+ // Expect success from SLTD and SRTD.
+ client1_.Negotiate(&client2_, cricket::CA_OFFER,
+ client1_role, client2_role, 0);
+ client2_.Negotiate(&client1_, cricket::CA_ANSWER,
+ client2_role, client1_role, 0);
+ }
+
+ // Negotiate with legacy client |client2|. Legacy client doesn't use setup
+ // attributes, except NONE.
+ void NegotiateWithLegacy() {
+ client1_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLING);
+ client2_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLED);
+ // Expect success from SLTD and SRTD.
+ client1_.Negotiate(&client2_, cricket::CA_OFFER,
+ cricket::CONNECTIONROLE_ACTPASS,
+ cricket::CONNECTIONROLE_NONE, 0);
+ client2_.Negotiate(&client1_, cricket::CA_ANSWER,
+ cricket::CONNECTIONROLE_ACTIVE,
+ cricket::CONNECTIONROLE_NONE, 0);
+ }
+
+ void Renegotiate(DtlsTestClient* reoffer_initiator,
+ ConnectionRole client1_role, ConnectionRole client2_role,
+ int flags) {
+ if (reoffer_initiator == &client1_) {
+ client1_.Negotiate(&client2_, cricket::CA_OFFER,
+ client1_role, client2_role, flags);
+ client2_.Negotiate(&client1_, cricket::CA_ANSWER,
+ client2_role, client1_role, flags);
+ } else {
+ client2_.Negotiate(&client1_, cricket::CA_OFFER,
+ client2_role, client1_role, flags);
+ client1_.Negotiate(&client2_, cricket::CA_ANSWER,
+ client1_role, client2_role, flags);
+ }
}
void TestTransfer(size_t channel, size_t size, size_t count, bool srtp) {
@@ -568,3 +656,96 @@
TestTransfer(0, 1000, 100, false);
TestTransfer(0, 1000, 100, true);
}
+
+// Testing when the remote is passive.
+TEST_F(DtlsTransportChannelTest, TestTransferDtlsAnswererIsPassive) {
+ MAYBE_SKIP_TEST(HaveDtlsSrtp);
+ SetChannelCount(2);
+ PrepareDtls(true, true);
+ PrepareDtlsSrtp(true, true);
+ ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS,
+ cricket::CONNECTIONROLE_PASSIVE));
+ TestTransfer(0, 1000, 100, true);
+ TestTransfer(1, 1000, 100, true);
+}
+
+// Testing with the legacy DTLS client which doesn't use setup attribute.
+// In this case legacy is the answerer.
+TEST_F(DtlsTransportChannelTest, TestDtlsSetupWithLegacyAsAnswerer) {
+ MAYBE_SKIP_TEST(HaveDtlsSrtp);
+ PrepareDtls(true, true);
+ NegotiateWithLegacy();
+ talk_base::SSLRole channel1_role;
+ talk_base::SSLRole channel2_role;
+ EXPECT_TRUE(client1_.transport()->GetSslRole(&channel1_role));
+ EXPECT_TRUE(client2_.transport()->GetSslRole(&channel2_role));
+ EXPECT_EQ(talk_base::SSL_SERVER, channel1_role);
+ EXPECT_EQ(talk_base::SSL_CLIENT, channel2_role);
+}
+
+// Testing re offer/answer after the session is estbalished. Roles will be
+// kept same as of the previous negotiation.
+TEST_F(DtlsTransportChannelTest, TestDtlsReOfferFromOfferer) {
+ MAYBE_SKIP_TEST(HaveDtlsSrtp);
+ SetChannelCount(2);
+ PrepareDtls(true, true);
+ PrepareDtlsSrtp(true, true);
+ // Initial role for client1 is ACTPASS and client2 is ACTIVE.
+ ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS,
+ cricket::CONNECTIONROLE_ACTIVE));
+ TestTransfer(0, 1000, 100, true);
+ TestTransfer(1, 1000, 100, true);
+ // Using input roles for the re-offer.
+ Renegotiate(&client1_, cricket::CONNECTIONROLE_ACTPASS,
+ cricket::CONNECTIONROLE_ACTIVE, NF_REOFFER);
+ TestTransfer(0, 1000, 100, true);
+ TestTransfer(1, 1000, 100, true);
+}
+
+TEST_F(DtlsTransportChannelTest, TestDtlsReOfferFromAnswerer) {
+ MAYBE_SKIP_TEST(HaveDtlsSrtp);
+ SetChannelCount(2);
+ PrepareDtls(true, true);
+ PrepareDtlsSrtp(true, true);
+ // Initial role for client1 is ACTPASS and client2 is ACTIVE.
+ ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS,
+ cricket::CONNECTIONROLE_ACTIVE));
+ TestTransfer(0, 1000, 100, true);
+ TestTransfer(1, 1000, 100, true);
+ // Using input roles for the re-offer.
+ Renegotiate(&client2_, cricket::CONNECTIONROLE_PASSIVE,
+ cricket::CONNECTIONROLE_ACTPASS, NF_REOFFER);
+ TestTransfer(0, 1000, 100, true);
+ TestTransfer(1, 1000, 100, true);
+}
+
+// Test that any change in role after the intial setup will result in failure.
+TEST_F(DtlsTransportChannelTest, TestDtlsRoleReversal) {
+ MAYBE_SKIP_TEST(HaveDtlsSrtp);
+ SetChannelCount(2);
+ PrepareDtls(true, true);
+ PrepareDtlsSrtp(true, true);
+ ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS,
+ cricket::CONNECTIONROLE_PASSIVE));
+
+ // Renegotiate from client2 with actpass and client1 as active.
+ Renegotiate(&client2_, cricket::CONNECTIONROLE_ACTPASS,
+ cricket::CONNECTIONROLE_ACTIVE,
+ NF_REOFFER | NF_EXPECT_FAILURE);
+}
+
+// Test that using different setup attributes which results in similar ssl
+// role as the initial negotiation will result in success.
+TEST_F(DtlsTransportChannelTest, TestDtlsReOfferWithDifferentSetupAttr) {
+ MAYBE_SKIP_TEST(HaveDtlsSrtp);
+ SetChannelCount(2);
+ PrepareDtls(true, true);
+ PrepareDtlsSrtp(true, true);
+ ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS,
+ cricket::CONNECTIONROLE_PASSIVE));
+ // Renegotiate from client2 with actpass and client1 as active.
+ Renegotiate(&client2_, cricket::CONNECTIONROLE_ACTIVE,
+ cricket::CONNECTIONROLE_ACTPASS, NF_REOFFER);
+ TestTransfer(0, 1000, 100, true);
+ TestTransfer(1, 1000, 100, true);
+}
diff --git a/p2p/base/fakesession.h b/p2p/base/fakesession.h
index 6b96c60..d3da2b2 100644
--- a/p2p/base/fakesession.h
+++ b/p2p/base/fakesession.h
@@ -117,6 +117,14 @@
dtls_fingerprint_ = talk_base::SSLFingerprint(alg, digest, digest_len);
return true;
}
+ virtual bool SetSslRole(talk_base::SSLRole role) {
+ ssl_role_ = role;
+ return true;
+ }
+ virtual bool GetSslRole(talk_base::SSLRole* role) const {
+ *role = ssl_role_;
+ return true;
+ }
virtual void Connect() {
if (state_ == STATE_INIT) {
@@ -275,6 +283,7 @@
std::string remote_ice_pwd_;
IceMode remote_ice_mode_;
talk_base::SSLFingerprint dtls_fingerprint_;
+ talk_base::SSLRole ssl_role_;
};
// Fake transport class, which can be passed to anything that needs a Transport.
diff --git a/p2p/base/p2ptransportchannel.cc b/p2p/base/p2ptransportchannel.cc
index 7a72d10..1fd939b 100644
--- a/p2p/base/p2ptransportchannel.cc
+++ b/p2p/base/p2ptransportchannel.cc
@@ -427,6 +427,15 @@
const Candidate* candidate = NULL;
bool known_username = false;
std::string remote_password;
+
+ // If we have not received any candidates from remote yet, as it can happen
+ // in case of trickle, but we have received remote ice_ufrag in O/A, we should
+ // check against it.
+ if (!remote_ice_ufrag_.empty() && (remote_username == remote_ice_ufrag_)) {
+ remote_password = remote_ice_pwd_;
+ known_username = true;
+ }
+
for (it = remote_candidates_.begin(); it != remote_candidates_.end(); ++it) {
if (it->username() == remote_username) {
remote_password = it->password();
diff --git a/p2p/base/p2ptransportchannel.h b/p2p/base/p2ptransportchannel.h
index 74a4483..17a489f 100644
--- a/p2p/base/p2ptransportchannel.h
+++ b/p2p/base/p2ptransportchannel.h
@@ -104,6 +104,51 @@
IceMode remote_ice_mode() const { return remote_ice_mode_; }
+ // DTLS methods.
+ virtual bool IsDtlsActive() const { return false; }
+
+ // Default implementation.
+ virtual bool GetSslRole(talk_base::SSLRole* role) const {
+ return false;
+ }
+
+ virtual bool SetSslRole(talk_base::SSLRole role) {
+ return false;
+ }
+
+ // Set up the ciphers to use for DTLS-SRTP.
+ virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers) {
+ return false;
+ }
+
+ // Find out which DTLS-SRTP cipher was negotiated
+ virtual bool GetSrtpCipher(std::string* cipher) {
+ return false;
+ }
+
+ // Allows key material to be extracted for external encryption.
+ virtual bool ExportKeyingMaterial(
+ const std::string& label,
+ const uint8* context,
+ size_t context_len,
+ bool use_context,
+ uint8* result,
+ size_t result_len) {
+ return false;
+ }
+
+ virtual bool SetLocalIdentity(talk_base::SSLIdentity* identity) {
+ return false;
+ }
+
+ // Set DTLS Remote fingerprint. Must be after local identity set.
+ virtual bool SetRemoteFingerprint(
+ const std::string& digest_alg,
+ const uint8* digest,
+ size_t digest_len) {
+ return false;
+ }
+
private:
talk_base::Thread* thread() { return worker_thread_; }
PortAllocatorSession* allocator_session() {
diff --git a/p2p/base/p2ptransportchannel_unittest.cc b/p2p/base/p2ptransportchannel_unittest.cc
index 32504de..a731b8d 100644
--- a/p2p/base/p2ptransportchannel_unittest.cc
+++ b/p2p/base/p2ptransportchannel_unittest.cc
@@ -601,9 +601,9 @@
c.set_password("");
}
LOG(LS_INFO) << "Candidate(" << data->channel->component() << "->"
- << rch->component() << "): " << c.type() << ", " << c.protocol()
- << ", " << c.address().ToString() << ", " << c.username()
- << ", " << c.generation();
+ << rch->component() << "): " << c.type() << ", "
+ << c.protocol() << ", " << c.address().ToString() << ", "
+ << c.username() << ", " << c.generation();
rch->OnCandidate(c);
}
void OnReadPacket(cricket::TransportChannel* channel, const char* data,
@@ -1266,6 +1266,165 @@
DestroyChannels();
}
+TEST_F(P2PTransportChannelTest, TestBundleAllocatorToBundleAllocator) {
+ AddAddress(0, kPublicAddrs[0]);
+ AddAddress(1, kPublicAddrs[1]);
+ SetAllocatorFlags(0, cricket::PORTALLOCATOR_ENABLE_BUNDLE);
+ SetAllocatorFlags(1, cricket::PORTALLOCATOR_ENABLE_BUNDLE);
+
+ CreateChannels(2);
+
+ EXPECT_TRUE_WAIT(ep1_ch1()->readable() &&
+ ep1_ch1()->writable() &&
+ ep2_ch1()->readable() &&
+ ep2_ch1()->writable(),
+ 1000);
+ EXPECT_TRUE(ep1_ch1()->best_connection() &&
+ ep2_ch1()->best_connection());
+
+ EXPECT_FALSE(ep1_ch2()->readable());
+ EXPECT_FALSE(ep1_ch2()->writable());
+ EXPECT_FALSE(ep2_ch2()->readable());
+ EXPECT_FALSE(ep2_ch2()->writable());
+
+ TestSendRecv(1); // Only 1 channel is writable per Endpoint.
+ DestroyChannels();
+}
+
+TEST_F(P2PTransportChannelTest, TestBundleAllocatorToNonBundleAllocator) {
+ AddAddress(0, kPublicAddrs[0]);
+ AddAddress(1, kPublicAddrs[1]);
+ // Enable BUNDLE flag at one side.
+ SetAllocatorFlags(0, cricket::PORTALLOCATOR_ENABLE_BUNDLE);
+
+ CreateChannels(2);
+
+ EXPECT_TRUE_WAIT(ep1_ch1()->readable() &&
+ ep1_ch1()->writable() &&
+ ep2_ch1()->readable() &&
+ ep2_ch1()->writable(),
+ 1000);
+ EXPECT_TRUE_WAIT(ep1_ch2()->readable() &&
+ ep1_ch2()->writable() &&
+ ep2_ch2()->readable() &&
+ ep2_ch2()->writable(),
+ 1000);
+
+ EXPECT_TRUE(ep1_ch1()->best_connection() &&
+ ep2_ch1()->best_connection());
+ EXPECT_TRUE(ep1_ch2()->best_connection() &&
+ ep2_ch2()->best_connection());
+
+ TestSendRecv(2);
+ DestroyChannels();
+}
+
+TEST_F(P2PTransportChannelTest, TestIceRoleConflictWithoutBundle) {
+ AddAddress(0, kPublicAddrs[0]);
+ AddAddress(1, kPublicAddrs[1]);
+ TestSignalRoleConflict();
+}
+
+TEST_F(P2PTransportChannelTest, TestIceRoleConflictWithBundle) {
+ AddAddress(0, kPublicAddrs[0]);
+ AddAddress(1, kPublicAddrs[1]);
+ SetAllocatorFlags(0, cricket::PORTALLOCATOR_ENABLE_BUNDLE);
+ SetAllocatorFlags(1, cricket::PORTALLOCATOR_ENABLE_BUNDLE);
+ TestSignalRoleConflict();
+}
+
+// Tests that the ice configs (protocol, tiebreaker and role) can be passed
+// down to ports.
+TEST_F(P2PTransportChannelTest, TestIceConfigWillPassDownToPort) {
+ AddAddress(0, kPublicAddrs[0]);
+ AddAddress(1, kPublicAddrs[1]);
+
+ SetIceRole(0, cricket::ICEROLE_CONTROLLING);
+ SetIceProtocol(0, cricket::ICEPROTO_GOOGLE);
+ SetIceTiebreaker(0, kTiebreaker1);
+ SetIceRole(1, cricket::ICEROLE_CONTROLLING);
+ SetIceProtocol(1, cricket::ICEPROTO_RFC5245);
+ SetIceTiebreaker(1, kTiebreaker2);
+
+ CreateChannels(1);
+
+ EXPECT_EQ_WAIT(2u, ep1_ch1()->ports().size(), 1000);
+
+ const std::vector<cricket::PortInterface *> ports_before = ep1_ch1()->ports();
+ for (size_t i = 0; i < ports_before.size(); ++i) {
+ EXPECT_EQ(cricket::ICEROLE_CONTROLLING, ports_before[i]->GetIceRole());
+ EXPECT_EQ(cricket::ICEPROTO_GOOGLE, ports_before[i]->IceProtocol());
+ EXPECT_EQ(kTiebreaker1, ports_before[i]->IceTiebreaker());
+ }
+
+ ep1_ch1()->SetIceRole(cricket::ICEROLE_CONTROLLED);
+ ep1_ch1()->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
+ ep1_ch1()->SetIceTiebreaker(kTiebreaker2);
+
+ const std::vector<cricket::PortInterface *> ports_after = ep1_ch1()->ports();
+ for (size_t i = 0; i < ports_after.size(); ++i) {
+ EXPECT_EQ(cricket::ICEROLE_CONTROLLED, ports_before[i]->GetIceRole());
+ EXPECT_EQ(cricket::ICEPROTO_RFC5245, ports_before[i]->IceProtocol());
+ // SetIceTiebreaker after Connect() has been called will fail. So expect the
+ // original value.
+ EXPECT_EQ(kTiebreaker1, ports_before[i]->IceTiebreaker());
+ }
+
+ EXPECT_TRUE_WAIT(ep1_ch1()->readable() &&
+ ep1_ch1()->writable() &&
+ ep2_ch1()->readable() &&
+ ep2_ch1()->writable(),
+ 1000);
+
+ EXPECT_TRUE(ep1_ch1()->best_connection() &&
+ ep2_ch1()->best_connection());
+
+ TestSendRecv(1);
+ DestroyChannels();
+}
+
+// Test that channel will handle connectivity checks received before the
+// candidates received and channel has remote ice credentials.
+TEST_F(P2PTransportChannelTest, TestSlowSignalingAsIce) {
+ set_clear_remote_candidates_ufrag_pwd(true);
+ AddAddress(0, kPublicAddrs[0]);
+ AddAddress(1, kPublicAddrs[1]);
+
+ // Disable all protocols except TCP.
+ SetAllocatorFlags(0, cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG);
+ SetAllocatorFlags(1, cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG);
+
+ SetIceRole(0, cricket::ICEROLE_CONTROLLING);
+ SetIceProtocol(0, cricket::ICEPROTO_RFC5245);
+ SetIceTiebreaker(0, kTiebreaker1);
+ SetIceRole(1, cricket::ICEROLE_CONTROLLED);
+ SetIceProtocol(1, cricket::ICEPROTO_RFC5245);
+ SetIceTiebreaker(1, kTiebreaker2);
+
+ // Delay handling of the candidates from Endpoint 1.
+ // Since remote ICE username and passwords are already provided during
+ // channel creation time using set_clear_remote_candidates_ufrag_pwd,
+ // channel should make a connection when it receives remote ping before
+ // candidates arrives. Channel will create connections only if remote username
+ // and passwords match with the one received in stun ping messages.
+ SetSignalingDelay(1, 1000);
+ CreateChannels(1);
+
+ EXPECT_TRUE_WAIT(ep1_ch1()->readable() &&
+ ep1_ch1()->writable() &&
+ ep2_ch1()->readable() &&
+ ep2_ch1()->writable(),
+ 1000);
+
+ EXPECT_EQ(cricket::PRFLX_PORT_TYPE, RemoteCandidate(ep1_ch1())->type());
+ EXPECT_EQ(cricket::LOCAL_PORT_TYPE, LocalCandidate(ep1_ch1())->type());
+ EXPECT_EQ(cricket::LOCAL_PORT_TYPE, RemoteCandidate(ep2_ch1())->type());
+ EXPECT_EQ(cricket::LOCAL_PORT_TYPE, LocalCandidate(ep2_ch1())->type());
+
+ TestSendRecv(1);
+ DestroyChannels();
+}
+
// Test what happens when we have 2 users behind the same NAT. This can lead
// to interesting behavior because the STUN server will only give out the
// address of the outermost NAT.
@@ -1387,119 +1546,3 @@
DestroyChannels();
}
-
-TEST_F(P2PTransportChannelTest, TestBundleAllocatorToBundleAllocator) {
- AddAddress(0, kPublicAddrs[0]);
- AddAddress(1, kPublicAddrs[1]);
- SetAllocatorFlags(0, cricket::PORTALLOCATOR_ENABLE_BUNDLE);
- SetAllocatorFlags(1, cricket::PORTALLOCATOR_ENABLE_BUNDLE);
-
- CreateChannels(2);
-
- EXPECT_TRUE_WAIT(ep1_ch1()->readable() &&
- ep1_ch1()->writable() &&
- ep2_ch1()->readable() &&
- ep2_ch1()->writable(),
- 1000);
- EXPECT_TRUE(ep1_ch1()->best_connection() &&
- ep2_ch1()->best_connection());
-
- EXPECT_FALSE(ep1_ch2()->readable());
- EXPECT_FALSE(ep1_ch2()->writable());
- EXPECT_FALSE(ep2_ch2()->readable());
- EXPECT_FALSE(ep2_ch2()->writable());
-
- TestSendRecv(1); // Only 1 channel is writable per Endpoint.
- DestroyChannels();
-}
-
-TEST_F(P2PTransportChannelTest, TestBundleAllocatorToNonBundleAllocator) {
- AddAddress(0, kPublicAddrs[0]);
- AddAddress(1, kPublicAddrs[1]);
- // Enable BUNDLE flag at one side.
- SetAllocatorFlags(0, cricket::PORTALLOCATOR_ENABLE_BUNDLE);
-
- CreateChannels(2);
-
- EXPECT_TRUE_WAIT(ep1_ch1()->readable() &&
- ep1_ch1()->writable() &&
- ep2_ch1()->readable() &&
- ep2_ch1()->writable(),
- 1000);
- EXPECT_TRUE_WAIT(ep1_ch2()->readable() &&
- ep1_ch2()->writable() &&
- ep2_ch2()->readable() &&
- ep2_ch2()->writable(),
- 1000);
-
- EXPECT_TRUE(ep1_ch1()->best_connection() &&
- ep2_ch1()->best_connection());
- EXPECT_TRUE(ep1_ch2()->best_connection() &&
- ep2_ch2()->best_connection());
-
- TestSendRecv(2);
- DestroyChannels();
-}
-
-TEST_F(P2PTransportChannelTest, TestIceRoleConflictWithoutBundle) {
- AddAddress(0, kPublicAddrs[0]);
- AddAddress(1, kPublicAddrs[1]);
- TestSignalRoleConflict();
-}
-
-TEST_F(P2PTransportChannelTest, TestIceRoleConflictWithBundle) {
- AddAddress(0, kPublicAddrs[0]);
- AddAddress(1, kPublicAddrs[1]);
- SetAllocatorFlags(0, cricket::PORTALLOCATOR_ENABLE_BUNDLE);
- SetAllocatorFlags(1, cricket::PORTALLOCATOR_ENABLE_BUNDLE);
- TestSignalRoleConflict();
-}
-
-// Tests that the ice configs (protocol, tiebreaker and role) can be passed
-// down to ports.
-TEST_F(P2PTransportChannelTest, TestIceConfigWillPassDownToPort) {
- AddAddress(0, kPublicAddrs[0]);
- AddAddress(1, kPublicAddrs[1]);
-
- SetIceRole(0, cricket::ICEROLE_CONTROLLING);
- SetIceProtocol(0, cricket::ICEPROTO_GOOGLE);
- SetIceTiebreaker(0, kTiebreaker1);
- SetIceRole(1, cricket::ICEROLE_CONTROLLING);
- SetIceProtocol(1, cricket::ICEPROTO_RFC5245);
- SetIceTiebreaker(1, kTiebreaker2);
-
- CreateChannels(1);
-
- EXPECT_EQ_WAIT(2u, ep1_ch1()->ports().size(), 1000);
-
- const std::vector<cricket::PortInterface *> ports_before = ep1_ch1()->ports();
- for (size_t i = 0; i < ports_before.size(); ++i) {
- EXPECT_EQ(cricket::ICEROLE_CONTROLLING, ports_before[i]->GetIceRole());
- EXPECT_EQ(cricket::ICEPROTO_GOOGLE, ports_before[i]->IceProtocol());
- EXPECT_EQ(kTiebreaker1, ports_before[i]->IceTiebreaker());
- }
-
- ep1_ch1()->SetIceRole(cricket::ICEROLE_CONTROLLED);
- ep1_ch1()->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
- ep1_ch1()->SetIceTiebreaker(kTiebreaker2);
-
- const std::vector<cricket::PortInterface *> ports_after = ep1_ch1()->ports();
- for (size_t i = 0; i < ports_after.size(); ++i) {
- EXPECT_EQ(cricket::ICEROLE_CONTROLLED, ports_before[i]->GetIceRole());
- EXPECT_EQ(cricket::ICEPROTO_RFC5245, ports_before[i]->IceProtocol());
- // SetIceTiebreaker after Connect() has been called will fail. So expect the
- // original value.
- EXPECT_EQ(kTiebreaker1, ports_before[i]->IceTiebreaker());
- }
-
- EXPECT_TRUE_WAIT(ep1_ch1()->readable() &&
- ep1_ch1()->writable() &&
- ep2_ch1()->readable() &&
- ep2_ch1()->writable(),
- 1000);
-
- EXPECT_TRUE(ep1_ch1()->best_connection() &&
- ep2_ch1()->best_connection());
-
- TestSendRecv(1);
-}
diff --git a/p2p/base/pseudotcp.cc b/p2p/base/pseudotcp.cc
index 6a2c1d2..b647fbf 100644
--- a/p2p/base/pseudotcp.cc
+++ b/p2p/base/pseudotcp.cc
@@ -36,6 +36,7 @@
#include "talk/base/byteorder.h"
#include "talk/base/common.h"
#include "talk/base/logging.h"
+#include "talk/base/scoped_ptr.h"
#include "talk/base/socket.h"
#include "talk/base/stringutils.h"
#include "talk/base/timeutils.h"
@@ -538,25 +539,24 @@
uint32 now = Now();
- uint8 buffer[MAX_PACKET];
- long_to_bytes(m_conv, buffer);
- long_to_bytes(seq, buffer + 4);
- long_to_bytes(m_rcv_nxt, buffer + 8);
+ talk_base::scoped_array<uint8> buffer(new uint8[MAX_PACKET]);
+ long_to_bytes(m_conv, buffer.get());
+ long_to_bytes(seq, buffer.get() + 4);
+ long_to_bytes(m_rcv_nxt, buffer.get() + 8);
buffer[12] = 0;
buffer[13] = flags;
- short_to_bytes(static_cast<uint16>(m_rcv_wnd >> m_rwnd_scale), buffer + 14);
+ short_to_bytes(
+ static_cast<uint16>(m_rcv_wnd >> m_rwnd_scale), buffer.get() + 14);
// Timestamp computations
- long_to_bytes(now, buffer + 16);
- long_to_bytes(m_ts_recent, buffer + 20);
+ long_to_bytes(now, buffer.get() + 16);
+ long_to_bytes(m_ts_recent, buffer.get() + 20);
m_ts_lastack = m_rcv_nxt;
if (len) {
size_t bytes_read = 0;
- talk_base::StreamResult result = m_sbuf.ReadOffset(buffer + HEADER_SIZE,
- len,
- offset,
- &bytes_read);
+ talk_base::StreamResult result = m_sbuf.ReadOffset(
+ buffer.get() + HEADER_SIZE, len, offset, &bytes_read);
UNUSED(result);
ASSERT(result == talk_base::SR_SUCCESS);
ASSERT(static_cast<uint32>(bytes_read) == len);
@@ -573,7 +573,8 @@
<< "><LEN=" << len << ">";
#endif // _DEBUGMSG
- IPseudoTcpNotify::WriteResult wres = m_notify->TcpWritePacket(this, reinterpret_cast<char *>(buffer), len + HEADER_SIZE);
+ IPseudoTcpNotify::WriteResult wres = m_notify->TcpWritePacket(
+ this, reinterpret_cast<char *>(buffer.get()), len + HEADER_SIZE);
// Note: When len is 0, this is an ACK packet. We don't read the return value for those,
// and thus we won't retry. So go ahead and treat the packet as a success (basically simulate
// as if it were dropped), which will prevent our timers from being messed up.
diff --git a/p2p/base/rawtransportchannel.h b/p2p/base/rawtransportchannel.h
index 0f606b7..4abea83 100644
--- a/p2p/base/rawtransportchannel.h
+++ b/p2p/base/rawtransportchannel.h
@@ -101,6 +101,55 @@
virtual void SetIcePwd(const std::string& ice_pwd) {}
virtual void SetRemoteIceMode(IceMode mode) {}
+ virtual bool GetStats(ConnectionInfos* infos) {
+ return false;
+ }
+
+ // DTLS methods.
+ virtual bool IsDtlsActive() const { return false; }
+
+ // Default implementation.
+ virtual bool GetSslRole(talk_base::SSLRole* role) const {
+ return false;
+ }
+
+ virtual bool SetSslRole(talk_base::SSLRole role) {
+ return false;
+ }
+
+ // Set up the ciphers to use for DTLS-SRTP.
+ virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers) {
+ return false;
+ }
+
+ // Find out which DTLS-SRTP cipher was negotiated
+ virtual bool GetSrtpCipher(std::string* cipher) {
+ return false;
+ }
+
+ // Allows key material to be extracted for external encryption.
+ virtual bool ExportKeyingMaterial(
+ const std::string& label,
+ const uint8* context,
+ size_t context_len,
+ bool use_context,
+ uint8* result,
+ size_t result_len) {
+ return false;
+ }
+
+ virtual bool SetLocalIdentity(talk_base::SSLIdentity* identity) {
+ return false;
+ }
+
+ // Set DTLS Remote fingerprint. Must be after local identity set.
+ virtual bool SetRemoteFingerprint(
+ const std::string& digest_alg,
+ const uint8* digest,
+ size_t digest_len) {
+ return false;
+ }
+
private:
RawTransport* raw_transport_;
talk_base::Thread *worker_thread_;
diff --git a/p2p/base/session.cc b/p2p/base/session.cc
index 74eda01..3128393 100644
--- a/p2p/base/session.cc
+++ b/p2p/base/session.cc
@@ -999,9 +999,10 @@
TransportInfos tinfos;
for (ContentInfos::const_iterator content = contents.begin();
content != contents.end(); ++content) {
- tinfos.push_back(
- TransportInfo(content->name,
- TransportDescription(transport_type(), Candidates())));
+ tinfos.push_back(TransportInfo(content->name,
+ TransportDescription(transport_type(),
+ std::string(),
+ std::string())));
}
return tinfos;
}
@@ -1558,7 +1559,9 @@
const Candidates& candidates,
SessionError* error) {
return SendTransportInfoMessage(TransportInfo(transproxy->content_name(),
- TransportDescription(transproxy->type(), candidates)), error);
+ TransportDescription(transproxy->type(), std::vector<std::string>(),
+ std::string(), std::string(), ICEMODE_FULL,
+ CONNECTIONROLE_NONE, NULL, candidates)), error);
}
bool Session::WriteSessionAction(SignalingProtocol protocol,
diff --git a/p2p/base/session_unittest.cc b/p2p/base/session_unittest.cc
index 1d072ae..a762066 100644
--- a/p2p/base/session_unittest.cc
+++ b/p2p/base/session_unittest.cc
@@ -713,7 +713,7 @@
new TestContentDescription(gingle_content_type,
content_type_a));
cricket::TransportDescription desc(cricket::NS_GINGLE_P2P,
- cricket::Candidates());
+ std::string(), std::string());
offer->AddTransportInfo(cricket::TransportInfo(content_name_a, desc));
if (content_name_a != content_name_b) {
@@ -735,7 +735,7 @@
offer->AddTransportInfo(cricket::TransportInfo
(content_name, cricket::TransportDescription(
cricket::NS_GINGLE_P2P,
- cricket::Candidates())));
+ std::string(), std::string())));
return offer;
}
diff --git a/p2p/base/sessionmessages.cc b/p2p/base/sessionmessages.cc
index 031c3d6..7a03d76 100644
--- a/p2p/base/sessionmessages.cc
+++ b/p2p/base/sessionmessages.cc
@@ -359,7 +359,7 @@
// If we don't have media, no need to separate the candidates.
if (!has_audio && !has_video) {
TransportInfo tinfo(CN_OTHER,
- TransportDescription(NS_GINGLE_P2P, Candidates()));
+ TransportDescription(NS_GINGLE_P2P, std::string(), std::string()));
if (!ParseGingleCandidates(action_elem, trans_parsers, translators,
CN_OTHER, &tinfo.description.candidates,
error)) {
@@ -371,10 +371,12 @@
}
// If we have media, separate the candidates.
- TransportInfo audio_tinfo(CN_AUDIO,
- TransportDescription(NS_GINGLE_P2P, Candidates()));
- TransportInfo video_tinfo(CN_VIDEO,
- TransportDescription(NS_GINGLE_P2P, Candidates()));
+ TransportInfo audio_tinfo(
+ CN_AUDIO,
+ TransportDescription(NS_GINGLE_P2P, std::string(), std::string()));
+ TransportInfo video_tinfo(
+ CN_VIDEO,
+ TransportDescription(NS_GINGLE_P2P, std::string(), std::string()));
for (const buzz::XmlElement* candidate_elem = action_elem->FirstElement();
candidate_elem != NULL;
candidate_elem = candidate_elem->NextElement()) {
diff --git a/p2p/base/stunport.cc b/p2p/base/stunport.cc
index e182a51..25d9e8d 100644
--- a/p2p/base/stunport.cc
+++ b/p2p/base/stunport.cc
@@ -254,8 +254,7 @@
// Even if the response doesn't match one of our outstanding requests, we
// will eat it because it might be a response to a retransmitted packet, and
// we already cleared the request when we got the first response.
- ASSERT(!server_addr_.IsUnresolved());
- if (remote_addr == server_addr_) {
+ if (!server_addr_.IsUnresolved() && remote_addr == server_addr_) {
requests_.CheckResponse(data, size);
return;
}
diff --git a/p2p/base/stunport_unittest.cc b/p2p/base/stunport_unittest.cc
index ba36c48..3c1c683 100644
--- a/p2p/base/stunport_unittest.cc
+++ b/p2p/base/stunport_unittest.cc
@@ -71,10 +71,36 @@
&StunPortTest::OnPortError);
}
+ void CreateSharedStunPort(const talk_base::SocketAddress& server_addr) {
+ socket_.reset(socket_factory_.CreateUdpSocket(
+ talk_base::SocketAddress(kLocalAddr.ipaddr(), 0), 0, 0));
+ ASSERT_TRUE(socket_ != NULL);
+ socket_->SignalReadPacket.connect(this, &StunPortTest::OnReadPacket);
+ stun_port_.reset(cricket::UDPPort::Create(
+ talk_base::Thread::Current(), &network_, socket_.get(),
+ talk_base::CreateRandomString(16), talk_base::CreateRandomString(22)));
+ ASSERT_TRUE(stun_port_ != NULL);
+ stun_port_->set_server_addr(server_addr);
+ stun_port_->SignalPortComplete.connect(this,
+ &StunPortTest::OnPortComplete);
+ stun_port_->SignalPortError.connect(this,
+ &StunPortTest::OnPortError);
+ }
+
void PrepareAddress() {
stun_port_->PrepareAddress();
}
+ void OnReadPacket(talk_base::AsyncPacketSocket* socket, const char* data,
+ size_t size, const talk_base::SocketAddress& remote_addr) {
+ stun_port_->HandleIncomingPacket(socket, data, size, remote_addr);
+ }
+
+ void SendData(const char* data, size_t len) {
+ stun_port_->HandleIncomingPacket(
+ socket_.get(), data, len, talk_base::SocketAddress("22.22.22.22", 0));
+ }
+
protected:
static void SetUpTestCase() {
// Ensure the RNG is inited.
@@ -96,8 +122,9 @@
private:
talk_base::Network network_;
talk_base::BasicPacketSocketFactory socket_factory_;
- talk_base::scoped_ptr<cricket::StunPort> stun_port_;
+ talk_base::scoped_ptr<cricket::UDPPort> stun_port_;
talk_base::scoped_ptr<cricket::TestStunServer> stun_server_;
+ talk_base::scoped_ptr<talk_base::AsyncPacketSocket> socket_;
bool done_;
bool error_;
int stun_keepalive_delay_;
@@ -164,3 +191,28 @@
ASSERT_EQ(1U, port()->Candidates().size());
}
+// Test that a local candidate can be generated using a shared socket.
+TEST_F(StunPortTest, TestSharedSocketPrepareAddress) {
+ CreateSharedStunPort(kStunAddr);
+ PrepareAddress();
+ EXPECT_TRUE_WAIT(done(), kTimeoutMs);
+ ASSERT_EQ(1U, port()->Candidates().size());
+ EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
+}
+
+// Test that we still a get a local candidate with invalid stun server hostname.
+// Also verifing that UDPPort can receive packets when stun address can't be
+// resolved.
+TEST_F(StunPortTest, TestSharedSocketPrepareAddressInvalidHostname) {
+ CreateSharedStunPort(kBadHostnameAddr);
+ PrepareAddress();
+ EXPECT_TRUE_WAIT(done(), kTimeoutMs);
+ ASSERT_EQ(1U, port()->Candidates().size());
+ EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
+
+ // Send data to port after it's ready. This is to make sure, UDP port can
+ // handle data with unresolved stun server address.
+ std::string data = "some random data, sending to cricket::Port.";
+ SendData(data.c_str(), data.length());
+ // No crash is success.
+}
diff --git a/p2p/base/transport.cc b/p2p/base/transport.cc
index 6ccd90b..d9064b8 100644
--- a/p2p/base/transport.cc
+++ b/p2p/base/transport.cc
@@ -27,6 +27,7 @@
#include "talk/p2p/base/transport.h"
+#include "talk/base/bind.h"
#include "talk/base/common.h"
#include "talk/base/logging.h"
#include "talk/p2p/base/candidate.h"
@@ -293,7 +294,8 @@
TransportDescription desc(NS_GINGLE_P2P, std::vector<std::string>(),
talk_base::CreateRandomString(ICE_UFRAG_LENGTH),
talk_base::CreateRandomString(ICE_PWD_LENGTH),
- ICEMODE_FULL, NULL, Candidates());
+ ICEMODE_FULL, CONNECTIONROLE_NONE, NULL,
+ Candidates());
SetLocalTransportDescription_w(desc, CA_OFFER);
}
@@ -424,6 +426,11 @@
return true;
}
+bool Transport::GetSslRole(talk_base::SSLRole* ssl_role) const {
+ return worker_thread_->Invoke<bool>(
+ Bind(&Transport::GetSslRole_w, this, ssl_role));
+}
+
void Transport::OnRemoteCandidates(const std::vector<Candidate>& candidates) {
for (std::vector<Candidate>::const_iterator iter = candidates.begin();
iter != candidates.end();
@@ -668,19 +675,20 @@
return true;
}
-void Transport::ApplyNegotiatedTransportDescription_w(
+bool Transport::ApplyNegotiatedTransportDescription_w(
TransportChannelImpl* channel) {
channel->SetIceProtocolType(protocol_);
channel->SetRemoteIceMode(remote_ice_mode_);
+ return true;
}
-bool Transport::NegotiateTransportDescription_w(ContentAction local_role_) {
+bool Transport::NegotiateTransportDescription_w(ContentAction local_role) {
// TODO(ekr@rtfm.com): This is ICE-specific stuff. Refactor into
// P2PTransport.
const TransportDescription* offer;
const TransportDescription* answer;
- if (local_role_ == CA_OFFER) {
+ if (local_role == CA_OFFER) {
offer = local_description_.get();
answer = remote_description_.get();
} else {
@@ -724,7 +732,8 @@
for (ChannelMap::iterator iter = channels_.begin();
iter != channels_.end();
++iter) {
- ApplyNegotiatedTransportDescription_w(iter->second.get());
+ if (!ApplyNegotiatedTransportDescription_w(iter->second.get()))
+ return false;
}
return true;
}
diff --git a/p2p/base/transport.h b/p2p/base/transport.h
index 63c3734..381215f 100644
--- a/p2p/base/transport.h
+++ b/p2p/base/transport.h
@@ -52,6 +52,7 @@
#include "talk/base/criticalsection.h"
#include "talk/base/messagequeue.h"
#include "talk/base/sigslot.h"
+#include "talk/base/sslstreamadapter.h"
#include "talk/p2p/base/candidate.h"
#include "talk/p2p/base/constants.h"
#include "talk/p2p/base/sessiondescription.h"
@@ -323,6 +324,8 @@
// Forwards the signal from TransportChannel to BaseSession.
sigslot::signal0<> SignalRoleConflict;
+ virtual bool GetSslRole(talk_base::SSLRole* ssl_role) const;
+
protected:
// These are called by Create/DestroyChannel above in order to create or
// destroy the appropriate type of channel.
@@ -366,9 +369,13 @@
// Pushes down the transport parameters obtained via negotiation.
// Derived classes can set their specific parameters here, but must call the
// base as well.
- virtual void ApplyNegotiatedTransportDescription_w(
+ virtual bool ApplyNegotiatedTransportDescription_w(
TransportChannelImpl* channel);
+ virtual bool GetSslRole_w(talk_base::SSLRole* ssl_role) const {
+ return false;
+ }
+
private:
struct ChannelMapEntry {
ChannelMapEntry() : impl_(NULL), candidates_allocated_(false), ref_(0) {}
diff --git a/p2p/base/transport_unittest.cc b/p2p/base/transport_unittest.cc
index f9bebdf..e3b7bad 100644
--- a/p2p/base/transport_unittest.cc
+++ b/p2p/base/transport_unittest.cc
@@ -145,8 +145,7 @@
transport_->SetIceRole(cricket::ICEROLE_CONTROLLING);
transport_->SetIceTiebreaker(99U);
cricket::TransportDescription local_desc(
- cricket::NS_JINGLE_ICE_UDP, std::vector<std::string>(),
- kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, NULL, cricket::Candidates());
+ cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc,
cricket::CA_OFFER));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role());
@@ -157,8 +156,7 @@
EXPECT_EQ(kIcePwd1, channel_->ice_pwd());
cricket::TransportDescription remote_desc(
- cricket::NS_JINGLE_ICE_UDP, std::vector<std::string>(),
- kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, NULL, cricket::Candidates());
+ cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc,
cricket::CA_ANSWER));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole());
@@ -177,12 +175,12 @@
transport_->SetIceRole(cricket::ICEROLE_CONTROLLED);
cricket::TransportDescription remote_desc(
cricket::NS_JINGLE_ICE_UDP, std::vector<std::string>(),
- kIceUfrag1, kIcePwd1, cricket::ICEMODE_LITE, NULL, cricket::Candidates());
+ kIceUfrag1, kIcePwd1, cricket::ICEMODE_LITE,
+ cricket::CONNECTIONROLE_ACTPASS, NULL, cricket::Candidates());
ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc,
cricket::CA_OFFER));
cricket::TransportDescription local_desc(
- cricket::NS_JINGLE_ICE_UDP, std::vector<std::string>(),
- kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, NULL, cricket::Candidates());
+ cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc,
cricket::CA_ANSWER));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role());
@@ -195,8 +193,7 @@
TEST_F(TransportTest, TestSetRemoteIceLiteInAnswer) {
transport_->SetIceRole(cricket::ICEROLE_CONTROLLING);
cricket::TransportDescription local_desc(
- cricket::NS_JINGLE_ICE_UDP, std::vector<std::string>(),
- kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, NULL, cricket::Candidates());
+ cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc,
cricket::CA_OFFER));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role());
@@ -206,7 +203,8 @@
EXPECT_EQ(cricket::ICEMODE_FULL, channel_->remote_ice_mode());
cricket::TransportDescription remote_desc(
cricket::NS_JINGLE_ICE_UDP, std::vector<std::string>(),
- kIceUfrag1, kIcePwd1, cricket::ICEMODE_LITE, NULL, cricket::Candidates());
+ kIceUfrag1, kIcePwd1, cricket::ICEMODE_LITE,
+ cricket::CONNECTIONROLE_NONE, NULL, cricket::Candidates());
ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc,
cricket::CA_ANSWER));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole());
diff --git a/p2p/base/transportchannel.h b/p2p/base/transportchannel.h
index 2b09f54..a5e41b9 100644
--- a/p2p/base/transportchannel.h
+++ b/p2p/base/transportchannel.h
@@ -89,30 +89,20 @@
// Returns the most recent error that occurred on this channel.
virtual int GetError() = 0;
- // TODO(mallinath) - Move this to TransportChannelImpl, after channel.cc
- // no longer needs it.
- // Returns current transportchannel ICE role.
- virtual IceRole GetIceRole() const = 0;
-
// Returns the current stats for this connection.
- virtual bool GetStats(ConnectionInfos* infos) {
- return false;
- }
+ virtual bool GetStats(ConnectionInfos* infos) = 0;
// Is DTLS active?
- virtual bool IsDtlsActive() const {
- return false;
- }
+ virtual bool IsDtlsActive() const = 0;
+
+ // Default implementation.
+ virtual bool GetSslRole(talk_base::SSLRole* role) const = 0;
// Set up the ciphers to use for DTLS-SRTP.
- virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers) {
- return false;
- }
+ virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers) = 0;
// Find out which DTLS-SRTP cipher was negotiated
- virtual bool GetSrtpCipher(std::string* cipher) {
- return false;
- }
+ virtual bool GetSrtpCipher(std::string* cipher) = 0;
// Allows key material to be extracted for external encryption.
virtual bool ExportKeyingMaterial(const std::string& label,
@@ -120,9 +110,7 @@
size_t context_len,
bool use_context,
uint8* result,
- size_t result_len) {
- return false;
- }
+ size_t result_len) = 0;
// Signalled each time a packet is received on this channel.
sigslot::signal4<TransportChannel*, const char*,
diff --git a/p2p/base/transportchannelimpl.h b/p2p/base/transportchannelimpl.h
index 32e2471..cde2441 100644
--- a/p2p/base/transportchannelimpl.h
+++ b/p2p/base/transportchannelimpl.h
@@ -50,6 +50,7 @@
virtual Transport* GetTransport() = 0;
// For ICE channels.
+ virtual IceRole GetIceRole() const = 0;
virtual void SetIceRole(IceRole role) = 0;
virtual void SetIceTiebreaker(uint64 tiebreaker) = 0;
// To toggle G-ICE/ICE.
@@ -93,16 +94,14 @@
// DTLS methods
// Set DTLS local identity.
- virtual bool SetLocalIdentity(talk_base::SSLIdentity* identity) {
- return false;
- }
+ virtual bool SetLocalIdentity(talk_base::SSLIdentity* identity) = 0;
// Set DTLS Remote fingerprint. Must be after local identity set.
virtual bool SetRemoteFingerprint(const std::string& digest_alg,
const uint8* digest,
- size_t digest_len) {
- return false;
- }
+ size_t digest_len) = 0;
+
+ virtual bool SetSslRole(talk_base::SSLRole role) = 0;
// TransportChannel is forwarding this signal from PortAllocatorSession.
sigslot::signal1<TransportChannelImpl*> SignalCandidatesAllocationDone;
diff --git a/p2p/base/transportchannelproxy.cc b/p2p/base/transportchannelproxy.cc
index b25f757..9f84620 100644
--- a/p2p/base/transportchannelproxy.cc
+++ b/p2p/base/transportchannelproxy.cc
@@ -135,6 +135,22 @@
return impl_->IsDtlsActive();
}
+bool TransportChannelProxy::GetSslRole(talk_base::SSLRole* role) const {
+ ASSERT(talk_base::Thread::Current() == worker_thread_);
+ if (!impl_) {
+ return false;
+ }
+ return impl_->GetSslRole(role);
+}
+
+bool TransportChannelProxy::SetSslRole(talk_base::SSLRole role) {
+ ASSERT(talk_base::Thread::Current() == worker_thread_);
+ if (!impl_) {
+ return false;
+ }
+ return impl_->SetSslRole(role);
+}
+
bool TransportChannelProxy::SetSrtpCiphers(const std::vector<std::string>&
ciphers) {
ASSERT(talk_base::Thread::Current() == worker_thread_);
diff --git a/p2p/base/transportchannelproxy.h b/p2p/base/transportchannelproxy.h
index 828c0ae..ea2843d 100644
--- a/p2p/base/transportchannelproxy.h
+++ b/p2p/base/transportchannelproxy.h
@@ -69,6 +69,8 @@
virtual IceRole GetIceRole() const;
virtual bool GetStats(ConnectionInfos* infos);
virtual bool IsDtlsActive() const;
+ virtual bool GetSslRole(talk_base::SSLRole* role) const;
+ virtual bool SetSslRole(talk_base::SSLRole role);
virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers);
virtual bool GetSrtpCipher(std::string* cipher);
virtual bool ExportKeyingMaterial(const std::string& label,
diff --git a/p2p/base/transportdescription.cc b/p2p/base/transportdescription.cc
new file mode 100644
index 0000000..5687342
--- /dev/null
+++ b/p2p/base/transportdescription.cc
@@ -0,0 +1,52 @@
+/*
+ * libjingle
+ * Copyright 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/transportdescription.h"
+
+#include "talk/p2p/base/constants.h"
+
+namespace cricket {
+
+bool StringToConnectionRole(const std::string& role_str, ConnectionRole* role) {
+ const char* const roles[] = {
+ CONNECTIONROLE_ACTIVE_STR,
+ CONNECTIONROLE_PASSIVE_STR,
+ CONNECTIONROLE_ACTPASS_STR,
+ CONNECTIONROLE_HOLDCONN_STR
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(roles); ++i) {
+ if (stricmp(roles[i], role_str.c_str()) == 0) {
+ *role = static_cast<ConnectionRole>(CONNECTIONROLE_ACTIVE + i);
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace cricket
+
diff --git a/p2p/base/transportdescription.h b/p2p/base/transportdescription.h
index 92da8d6..64fbb89 100644
--- a/p2p/base/transportdescription.h
+++ b/p2p/base/transportdescription.h
@@ -75,6 +75,26 @@
ICEMODE_LITE // As defined in http://tools.ietf.org/html/rfc5245#section-4.2
};
+// RFC 4145 - http://tools.ietf.org/html/rfc4145#section-4
+// 'active': The endpoint will initiate an outgoing connection.
+// 'passive': The endpoint will accept an incoming connection.
+// 'actpass': The endpoint is willing to accept an incoming
+// connection or to initiate an outgoing connection.
+enum ConnectionRole {
+ CONNECTIONROLE_NONE = 0,
+ CONNECTIONROLE_ACTIVE,
+ CONNECTIONROLE_PASSIVE,
+ CONNECTIONROLE_ACTPASS,
+ CONNECTIONROLE_HOLDCONN,
+};
+
+extern const char CONNECTIONROLE_ACTIVE_STR[];
+extern const char CONNECTIONROLE_PASSIVE_STR[];
+extern const char CONNECTIONROLE_ACTPASS_STR[];
+extern const char CONNECTIONROLE_HOLDCONN_STR[];
+
+bool StringToConnectionRole(const std::string& role_str, ConnectionRole* role);
+
typedef std::vector<Candidate> Candidates;
struct TransportDescription {
@@ -85,6 +105,7 @@
const std::string& ice_ufrag,
const std::string& ice_pwd,
IceMode ice_mode,
+ ConnectionRole role,
const talk_base::SSLFingerprint* identity_fingerprint,
const Candidates& candidates)
: transport_type(transport_type),
@@ -92,19 +113,24 @@
ice_ufrag(ice_ufrag),
ice_pwd(ice_pwd),
ice_mode(ice_mode),
+ connection_role(role),
identity_fingerprint(CopyFingerprint(identity_fingerprint)),
candidates(candidates) {}
TransportDescription(const std::string& transport_type,
- const Candidates& candidates)
+ const std::string& ice_ufrag,
+ const std::string& ice_pwd)
: transport_type(transport_type),
+ ice_ufrag(ice_ufrag),
+ ice_pwd(ice_pwd),
ice_mode(ICEMODE_FULL),
- candidates(candidates) {}
+ connection_role(CONNECTIONROLE_NONE) {}
TransportDescription(const TransportDescription& from)
: transport_type(from.transport_type),
transport_options(from.transport_options),
ice_ufrag(from.ice_ufrag),
ice_pwd(from.ice_pwd),
ice_mode(from.ice_mode),
+ connection_role(from.connection_role),
identity_fingerprint(CopyFingerprint(from.identity_fingerprint.get())),
candidates(from.candidates) {}
@@ -118,6 +144,7 @@
ice_ufrag = from.ice_ufrag;
ice_pwd = from.ice_pwd;
ice_mode = from.ice_mode;
+ connection_role = from.connection_role;
identity_fingerprint.reset(CopyFingerprint(
from.identity_fingerprint.get()));
@@ -147,6 +174,7 @@
std::string ice_ufrag;
std::string ice_pwd;
IceMode ice_mode;
+ ConnectionRole connection_role;
talk_base::scoped_ptr<talk_base::SSLFingerprint> identity_fingerprint;
Candidates candidates;
diff --git a/p2p/base/transportdescriptionfactory.cc b/p2p/base/transportdescriptionfactory.cc
index 8fbfff1..0c12943 100644
--- a/p2p/base/transportdescriptionfactory.cc
+++ b/p2p/base/transportdescriptionfactory.cc
@@ -73,10 +73,12 @@
// If we are trying to establish a secure transport, add a fingerprint.
if (secure_ == SEC_ENABLED || secure_ == SEC_REQUIRED) {
// Fail if we can't create the fingerprint.
- if (!CreateIdentityDigest(desc.get())) {
+ // If we are the initiator set role to "actpass".
+ if (!SetSecurityInfo(desc.get(), CONNECTIONROLE_ACTPASS)) {
return NULL;
}
}
+
return desc.release();
}
@@ -101,7 +103,7 @@
desc->transport_type = NS_GINGLE_P2P;
// Offer is hybrid, we support GICE: use GICE.
} else if ((!offer || offer->transport_type == NS_GINGLE_P2P) &&
- (protocol_ == ICEPROTO_HYBRID || protocol_ == ICEPROTO_GOOGLE)) {
+ (protocol_ == ICEPROTO_HYBRID || protocol_ == ICEPROTO_GOOGLE)) {
// Offer is GICE, we support hybrid or GICE: use GICE.
desc->transport_type = NS_GINGLE_P2P;
} else {
@@ -126,7 +128,11 @@
// 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.
- if (!CreateIdentityDigest(desc.get())) {
+ // Setting DTLS role to active.
+ ConnectionRole role = (options.prefer_passive_role) ?
+ CONNECTIONROLE_PASSIVE : CONNECTIONROLE_ACTIVE;
+
+ if (!SetSecurityInfo(desc.get(), role)) {
return NULL;
}
}
@@ -140,8 +146,8 @@
return desc.release();
}
-bool TransportDescriptionFactory::CreateIdentityDigest(
- TransportDescription* desc) const {
+bool TransportDescriptionFactory::SetSecurityInfo(
+ TransportDescription* desc, ConnectionRole role) const {
if (!identity_) {
LOG(LS_ERROR) << "Cannot create identity digest with no identity";
return false;
@@ -154,6 +160,8 @@
return false;
}
+ // Assign security role.
+ desc->connection_role = role;
return true;
}
diff --git a/p2p/base/transportdescriptionfactory.h b/p2p/base/transportdescriptionfactory.h
index 32836f3..ddf2799 100644
--- a/p2p/base/transportdescriptionfactory.h
+++ b/p2p/base/transportdescriptionfactory.h
@@ -37,8 +37,9 @@
namespace cricket {
struct TransportOptions {
- TransportOptions() : ice_restart(false) {}
+ TransportOptions() : ice_restart(false), prefer_passive_role(false) {}
bool ice_restart;
+ bool prefer_passive_role;
};
// Creates transport descriptions according to the supplied configuration.
@@ -71,7 +72,8 @@
const TransportDescription* current_description) const;
private:
- bool CreateIdentityDigest(TransportDescription* description) const;
+ bool SetSecurityInfo(TransportDescription* description,
+ ConnectionRole role) const;
TransportProtocol protocol_;
SecurePolicy secure_;
diff --git a/session/media/channel.cc b/session/media/channel.cc
index 1bce2ac..76fafae 100644
--- a/session/media/channel.cc
+++ b/session/media/channel.cc
@@ -1003,8 +1003,13 @@
&dtls_buffer[offset], SRTP_MASTER_KEY_SALT_LEN);
std::vector<unsigned char> *send_key, *recv_key;
+ talk_base::SSLRole role;
+ if (!channel->GetSslRole(&role)) {
+ LOG(LS_WARNING) << "GetSslRole failed";
+ return false;
+ }
- if (channel->GetIceRole() == ICEROLE_CONTROLLING) {
+ if (role == talk_base::SSL_SERVER) {
send_key = &server_write_key;
recv_key = &client_write_key;
} else {
diff --git a/session/media/mediasession_unittest.cc b/session/media/mediasession_unittest.cc
index 6e04915..f2e576c 100644
--- a/session/media/mediasession_unittest.cc
+++ b/session/media/mediasession_unittest.cc
@@ -219,25 +219,19 @@
current_desc.reset(new SessionDescription());
EXPECT_TRUE(current_desc->AddTransportInfo(
TransportInfo("audio",
- TransportDescription("", std::vector<std::string>(),
+ TransportDescription("",
current_audio_ufrag,
- current_audio_pwd,
- cricket::ICEMODE_FULL,
- NULL, Candidates()))));
+ current_audio_pwd))));
EXPECT_TRUE(current_desc->AddTransportInfo(
TransportInfo("video",
- TransportDescription("", std::vector<std::string>(),
+ TransportDescription("",
current_video_ufrag,
- current_video_pwd,
- cricket::ICEMODE_FULL,
- NULL, Candidates()))));
+ current_video_pwd))));
EXPECT_TRUE(current_desc->AddTransportInfo(
TransportInfo("data",
- TransportDescription("", std::vector<std::string>(),
+ TransportDescription("",
current_data_ufrag,
- current_data_pwd,
- cricket::ICEMODE_FULL,
- NULL, Candidates()))));
+ current_data_pwd))));
}
if (offer) {
desc.reset(f1_.CreateOffer(options, current_desc.get()));