aboutsummaryrefslogtreecommitdiff
path: root/webrtc/p2p/base
diff options
context:
space:
mode:
Diffstat (limited to 'webrtc/p2p/base')
-rw-r--r--webrtc/p2p/base/candidate.h1
-rw-r--r--webrtc/p2p/base/dtlstransport.h2
-rw-r--r--webrtc/p2p/base/dtlstransportchannel.cc59
-rw-r--r--webrtc/p2p/base/dtlstransportchannel.h7
-rw-r--r--webrtc/p2p/base/dtlstransportchannel_unittest.cc178
-rw-r--r--webrtc/p2p/base/faketransportcontroller.h29
-rw-r--r--webrtc/p2p/base/p2ptransportchannel.cc279
-rw-r--r--webrtc/p2p/base/p2ptransportchannel.h64
-rw-r--r--webrtc/p2p/base/p2ptransportchannel_unittest.cc230
-rw-r--r--webrtc/p2p/base/port.cc121
-rw-r--r--webrtc/p2p/base/port.h34
-rw-r--r--webrtc/p2p/base/port_unittest.cc91
-rw-r--r--webrtc/p2p/base/portallocator.h55
-rw-r--r--webrtc/p2p/base/portinterface.h4
-rw-r--r--webrtc/p2p/base/pseudotcp.cc3
-rw-r--r--webrtc/p2p/base/relayport.cc2
-rw-r--r--webrtc/p2p/base/relayport.h34
-rw-r--r--webrtc/p2p/base/stun_unittest.cc11
-rw-r--r--webrtc/p2p/base/stunport.cc119
-rw-r--r--webrtc/p2p/base/stunport.h34
-rw-r--r--webrtc/p2p/base/stunrequest.cc10
-rw-r--r--webrtc/p2p/base/stunrequest.h7
-rw-r--r--webrtc/p2p/base/tcpport.cc21
-rw-r--r--webrtc/p2p/base/tcpport.h44
-rw-r--r--webrtc/p2p/base/transport.cc4
-rw-r--r--webrtc/p2p/base/transport.h8
-rw-r--r--webrtc/p2p/base/transportchannel.cc15
-rw-r--r--webrtc/p2p/base/transportchannel.h21
-rw-r--r--webrtc/p2p/base/transportcontroller.cc16
-rw-r--r--webrtc/p2p/base/transportcontroller.h10
-rw-r--r--webrtc/p2p/base/transportcontroller_unittest.cc15
-rw-r--r--webrtc/p2p/base/transportdescription.cc4
-rw-r--r--webrtc/p2p/base/transportdescriptionfactory_unittest.cc9
-rw-r--r--webrtc/p2p/base/turnport.cc256
-rw-r--r--webrtc/p2p/base/turnport.h47
-rw-r--r--webrtc/p2p/base/turnport_unittest.cc238
-rw-r--r--webrtc/p2p/base/turnserver.cc6
-rw-r--r--webrtc/p2p/base/turnserver.h6
38 files changed, 1551 insertions, 543 deletions
diff --git a/webrtc/p2p/base/candidate.h b/webrtc/p2p/base/candidate.h
index 3f0ea43cde..ac7acabf05 100644
--- a/webrtc/p2p/base/candidate.h
+++ b/webrtc/p2p/base/candidate.h
@@ -105,6 +105,7 @@ class Candidate {
std::min(prio_val, static_cast<uint64_t>(UINT_MAX)));
}
+ // TODO(honghaiz): Change to usernameFragment or ufrag.
const std::string & username() const { return username_; }
void set_username(const std::string & username) { username_ = username; }
diff --git a/webrtc/p2p/base/dtlstransport.h b/webrtc/p2p/base/dtlstransport.h
index e9a1ae2ada..9f2903e1d7 100644
--- a/webrtc/p2p/base/dtlstransport.h
+++ b/webrtc/p2p/base/dtlstransport.h
@@ -35,7 +35,7 @@ class DtlsTransport : public Base {
: Base(name, allocator),
certificate_(certificate),
secure_role_(rtc::SSL_CLIENT),
- ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10) {}
+ ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12) {}
~DtlsTransport() {
Base::DestroyAllChannels();
diff --git a/webrtc/p2p/base/dtlstransportchannel.cc b/webrtc/p2p/base/dtlstransportchannel.cc
index 0c063e0323..d6b5bce723 100644
--- a/webrtc/p2p/base/dtlstransportchannel.cc
+++ b/webrtc/p2p/base/dtlstransportchannel.cc
@@ -8,6 +8,8 @@
* be found in the AUTHORS file in the root of the source tree.
*/
+#include <utility>
+
#include "webrtc/p2p/base/dtlstransportchannel.h"
#include "webrtc/p2p/base/common.h"
@@ -95,7 +97,7 @@ DtlsTransportChannelWrapper::DtlsTransportChannelWrapper(
channel_(channel),
downward_(NULL),
ssl_role_(rtc::SSL_CLIENT),
- ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10) {
+ ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12) {
channel_->SignalWritableState.connect(this,
&DtlsTransportChannelWrapper::OnWritableState);
channel_->SignalReadPacket.connect(this,
@@ -199,6 +201,8 @@ bool DtlsTransportChannelWrapper::SetRemoteFingerprint(
size_t digest_len) {
rtc::Buffer remote_fingerprint_value(digest, digest_len);
+ // Once we have the local certificate, the same remote fingerprint can be set
+ // multiple times.
if (dtls_active_ && remote_fingerprint_value_ == remote_fingerprint_value &&
!digest_alg.empty()) {
// This may happen during renegotiation.
@@ -206,28 +210,36 @@ bool DtlsTransportChannelWrapper::SetRemoteFingerprint(
return true;
}
- // Allow SetRemoteFingerprint with a NULL digest even if SetLocalCertificate
- // hasn't been called.
- if (dtls_ || (!dtls_active_ && !digest_alg.empty())) {
- LOG_J(LS_ERROR, this) << "Can't set DTLS remote settings in this state.";
- return false;
- }
-
+ // If the other side doesn't support DTLS, turn off |dtls_active_|.
if (digest_alg.empty()) {
+ RTC_DCHECK(!digest_len);
LOG_J(LS_INFO, this) << "Other side didn't support DTLS.";
dtls_active_ = false;
return true;
}
+ // Otherwise, we must have a local certificate before setting remote
+ // fingerprint.
+ if (!dtls_active_) {
+ LOG_J(LS_ERROR, this) << "Can't set DTLS remote settings in this state.";
+ return false;
+ }
+
// At this point we know we are doing DTLS
- remote_fingerprint_value_ = remote_fingerprint_value.Pass();
+ remote_fingerprint_value_ = std::move(remote_fingerprint_value);
remote_fingerprint_algorithm_ = digest_alg;
+ bool reconnect = dtls_;
+
if (!SetupDtls()) {
set_dtls_state(DTLS_TRANSPORT_FAILED);
return false;
}
+ if (reconnect) {
+ Reconnect();
+ }
+
return true;
}
@@ -267,7 +279,7 @@ bool DtlsTransportChannelWrapper::SetupDtls() {
// Set up DTLS-SRTP, if it's been enabled.
if (!srtp_ciphers_.empty()) {
- if (!dtls_->SetDtlsSrtpCiphers(srtp_ciphers_)) {
+ if (!dtls_->SetDtlsSrtpCryptoSuites(srtp_ciphers_)) {
LOG_J(LS_ERROR, this) << "Couldn't set DTLS-SRTP ciphers.";
return false;
}
@@ -279,11 +291,10 @@ bool DtlsTransportChannelWrapper::SetupDtls() {
return true;
}
-bool DtlsTransportChannelWrapper::SetSrtpCiphers(
- const std::vector<std::string>& ciphers) {
- if (srtp_ciphers_ == ciphers) {
+bool DtlsTransportChannelWrapper::SetSrtpCryptoSuites(
+ const std::vector<int>& ciphers) {
+ if (srtp_ciphers_ == ciphers)
return true;
- }
if (dtls_state() == DTLS_TRANSPORT_CONNECTING) {
LOG(LS_WARNING) << "Ignoring new SRTP ciphers while DTLS is negotiating";
@@ -294,18 +305,18 @@ bool DtlsTransportChannelWrapper::SetSrtpCiphers(
// We don't support DTLS renegotiation currently. If new set of srtp ciphers
// are different than what's being used currently, we will not use it.
// So for now, let's be happy (or sad) with a warning message.
- std::string current_srtp_cipher;
- if (!dtls_->GetDtlsSrtpCipher(&current_srtp_cipher)) {
+ int current_srtp_cipher;
+ if (!dtls_->GetDtlsSrtpCryptoSuite(&current_srtp_cipher)) {
LOG(LS_ERROR) << "Failed to get the current SRTP cipher for DTLS channel";
return false;
}
- const std::vector<std::string>::const_iterator iter =
+ const std::vector<int>::const_iterator iter =
std::find(ciphers.begin(), ciphers.end(), current_srtp_cipher);
if (iter == ciphers.end()) {
std::string requested_str;
for (size_t i = 0; i < ciphers.size(); ++i) {
requested_str.append(" ");
- requested_str.append(ciphers[i]);
+ requested_str.append(rtc::SrtpCryptoSuiteToName(ciphers[i]));
requested_str.append(" ");
}
LOG(LS_WARNING) << "Ignoring new set of SRTP ciphers, as DTLS "
@@ -324,12 +335,12 @@ bool DtlsTransportChannelWrapper::SetSrtpCiphers(
return true;
}
-bool DtlsTransportChannelWrapper::GetSrtpCryptoSuite(std::string* cipher) {
+bool DtlsTransportChannelWrapper::GetSrtpCryptoSuite(int* cipher) {
if (dtls_state() != DTLS_TRANSPORT_CONNECTED) {
return false;
}
- return dtls_->GetDtlsSrtpCipher(cipher);
+ return dtls_->GetDtlsSrtpCryptoSuite(cipher);
}
@@ -617,4 +628,12 @@ void DtlsTransportChannelWrapper::OnConnectionRemoved(
SignalConnectionRemoved(this);
}
+void DtlsTransportChannelWrapper::Reconnect() {
+ set_dtls_state(DTLS_TRANSPORT_NEW);
+ set_writable(false);
+ if (channel_->writable()) {
+ OnWritableState(channel_);
+ }
+}
+
} // namespace cricket
diff --git a/webrtc/p2p/base/dtlstransportchannel.h b/webrtc/p2p/base/dtlstransportchannel.h
index 41e081b7fe..955b963a36 100644
--- a/webrtc/p2p/base/dtlstransportchannel.h
+++ b/webrtc/p2p/base/dtlstransportchannel.h
@@ -126,10 +126,10 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl {
// Set up the ciphers to use for DTLS-SRTP. If this method is not called
// before DTLS starts, or |ciphers| is empty, SRTP keys won't be negotiated.
// This method should be called before SetupDtls.
- bool SetSrtpCiphers(const std::vector<std::string>& ciphers) override;
+ bool SetSrtpCryptoSuites(const std::vector<int>& ciphers) override;
// Find out which DTLS-SRTP cipher was negotiated
- bool GetSrtpCryptoSuite(std::string* cipher) override;
+ bool GetSrtpCryptoSuite(int* cipher) override;
bool GetSslRole(rtc::SSLRole* role) const override;
bool SetSslRole(rtc::SSLRole role) override;
@@ -216,6 +216,7 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl {
void OnRoleConflict(TransportChannelImpl* channel);
void OnRouteChange(TransportChannel* channel, const Candidate& candidate);
void OnConnectionRemoved(TransportChannelImpl* channel);
+ void Reconnect();
Transport* transport_; // The transport_ that created us.
rtc::Thread* worker_thread_; // Everything should occur on this thread.
@@ -223,7 +224,7 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl {
TransportChannelImpl* const channel_;
rtc::scoped_ptr<rtc::SSLStreamAdapter> dtls_; // The DTLS stream
StreamInterfaceChannel* downward_; // Wrapper for channel_, owned by dtls_.
- std::vector<std::string> srtp_ciphers_; // SRTP ciphers to use with DTLS.
+ std::vector<int> srtp_ciphers_; // SRTP ciphers to use with DTLS.
bool dtls_active_ = false;
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate_;
rtc::SSLRole ssl_role_;
diff --git a/webrtc/p2p/base/dtlstransportchannel_unittest.cc b/webrtc/p2p/base/dtlstransportchannel_unittest.cc
index 07e3b87847..f5d42f3c6e 100644
--- a/webrtc/p2p/base/dtlstransportchannel_unittest.cc
+++ b/webrtc/p2p/base/dtlstransportchannel_unittest.cc
@@ -28,7 +28,6 @@
return; \
}
-static const char AES_CM_128_HMAC_SHA1_80[] = "AES_CM_128_HMAC_SHA1_80";
static const char kIceUfrag1[] = "TESTICEUFRAG0001";
static const char kIcePwd1[] = "TESTICEPWD00000000000001";
static const size_t kPacketNumOffset = 8;
@@ -49,14 +48,14 @@ class DtlsTestClient : public sigslot::has_slots<> {
: name_(name),
packet_size_(0),
use_dtls_srtp_(false),
- ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10),
+ ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12),
negotiated_dtls_(false),
received_dtls_client_hello_(false),
received_dtls_server_hello_(false) {}
void CreateCertificate(rtc::KeyType key_type) {
- certificate_ = rtc::RTCCertificate::Create(
- rtc::scoped_ptr<rtc::SSLIdentity>(
- rtc::SSLIdentity::Generate(name_, key_type)).Pass());
+ certificate_ =
+ rtc::RTCCertificate::Create(rtc::scoped_ptr<rtc::SSLIdentity>(
+ rtc::SSLIdentity::Generate(name_, key_type)));
}
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate() {
return certificate_;
@@ -150,9 +149,9 @@ class DtlsTestClient : public sigslot::has_slots<> {
// 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;
- ciphers.push_back(AES_CM_128_HMAC_SHA1_80);
- ASSERT_TRUE((*it)->SetSrtpCiphers(ciphers));
+ std::vector<int> ciphers;
+ ciphers.push_back(rtc::SRTP_AES128_CM_SHA1_80);
+ ASSERT_TRUE((*it)->SetSrtpCryptoSuites(ciphers));
}
}
@@ -215,16 +214,16 @@ class DtlsTestClient : public sigslot::has_slots<> {
}
}
- void CheckSrtp(const std::string& expected_cipher) {
+ void CheckSrtp(int expected_crypto_suite) {
for (std::vector<cricket::DtlsTransportChannelWrapper*>::iterator it =
channels_.begin(); it != channels_.end(); ++it) {
- std::string cipher;
+ int crypto_suite;
- bool rv = (*it)->GetSrtpCryptoSuite(&cipher);
- if (negotiated_dtls_ && !expected_cipher.empty()) {
+ bool rv = (*it)->GetSrtpCryptoSuite(&crypto_suite);
+ if (negotiated_dtls_ && expected_crypto_suite) {
ASSERT_TRUE(rv);
- ASSERT_EQ(cipher, expected_cipher);
+ ASSERT_EQ(crypto_suite, expected_crypto_suite);
} else {
ASSERT_FALSE(rv);
}
@@ -401,7 +400,7 @@ class DtlsTransportChannelTest : public testing::Test {
channel_ct_(1),
use_dtls_(false),
use_dtls_srtp_(false),
- ssl_expected_version_(rtc::SSL_PROTOCOL_DTLS_10) {}
+ ssl_expected_version_(rtc::SSL_PROTOCOL_DTLS_12) {}
void SetChannelCount(size_t channel_ct) {
channel_ct_ = static_cast<int>(channel_ct);
@@ -469,11 +468,11 @@ class DtlsTransportChannelTest : public testing::Test {
// Check that we negotiated the right ciphers.
if (use_dtls_srtp_) {
- client1_.CheckSrtp(AES_CM_128_HMAC_SHA1_80);
- client2_.CheckSrtp(AES_CM_128_HMAC_SHA1_80);
+ client1_.CheckSrtp(rtc::SRTP_AES128_CM_SHA1_80);
+ client2_.CheckSrtp(rtc::SRTP_AES128_CM_SHA1_80);
} else {
- client1_.CheckSrtp("");
- client2_.CheckSrtp("");
+ client1_.CheckSrtp(rtc::SRTP_INVALID_CRYPTO_SUITE);
+ client2_.CheckSrtp(rtc::SRTP_INVALID_CRYPTO_SUITE);
}
client1_.CheckSsl(rtc::SSLStreamAdapter::GetDefaultSslCipherForTest(
ssl_expected_version_, rtc::KT_DEFAULT));
@@ -601,16 +600,30 @@ TEST_F(DtlsTransportChannelTest, TestTransferSrtpTwoChannels) {
TestTransfer(1, 1000, 100, true);
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestTransferDtls DISABLED_TestTransferDtls
+#else
+#define MAYBE_TestTransferDtls TestTransferDtls
+#endif
// Connect with DTLS, and transfer some data.
-TEST_F(DtlsTransportChannelTest, TestTransferDtls) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestTransferDtls) {
MAYBE_SKIP_TEST(HaveDtls);
PrepareDtls(true, true, rtc::KT_DEFAULT);
ASSERT_TRUE(Connect());
TestTransfer(0, 1000, 100, false);
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestTransferDtlsTwoChannels DISABLED_TestTransferDtlsTwoChannels
+#else
+#define MAYBE_TestTransferDtlsTwoChannels TestTransferDtlsTwoChannels
+#endif
// Create two channels with DTLS, and transfer some data.
-TEST_F(DtlsTransportChannelTest, TestTransferDtlsTwoChannels) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestTransferDtlsTwoChannels) {
MAYBE_SKIP_TEST(HaveDtls);
SetChannelCount(2);
PrepareDtls(true, true, rtc::KT_DEFAULT);
@@ -642,8 +655,15 @@ TEST_F(DtlsTransportChannelTest, TestDtls12None) {
ASSERT_TRUE(Connect());
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestDtls12Both DISABLED_TestDtls12Both
+#else
+#define MAYBE_TestDtls12Both TestDtls12Both
+#endif
// Create two channels with DTLS 1.2 and check ciphers.
-TEST_F(DtlsTransportChannelTest, TestDtls12Both) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestDtls12Both) {
MAYBE_SKIP_TEST(HaveDtls);
SetChannelCount(2);
PrepareDtls(true, true, rtc::KT_DEFAULT);
@@ -669,8 +689,15 @@ TEST_F(DtlsTransportChannelTest, TestDtls12Client2) {
ASSERT_TRUE(Connect());
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestTransferDtlsSrtp DISABLED_TestTransferDtlsSrtp
+#else
+#define MAYBE_TestTransferDtlsSrtp TestTransferDtlsSrtp
+#endif
// Connect with DTLS, negotiate DTLS-SRTP, and transfer SRTP using bypass.
-TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtp) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestTransferDtlsSrtp) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
PrepareDtls(true, true, rtc::KT_DEFAULT);
PrepareDtlsSrtp(true, true);
@@ -678,9 +705,18 @@ TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtp) {
TestTransfer(0, 1000, 100, true);
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestTransferDtlsInvalidSrtpPacket \
+ DISABLED_TestTransferDtlsInvalidSrtpPacket
+#else
+#define MAYBE_TestTransferDtlsInvalidSrtpPacket \
+ TestTransferDtlsInvalidSrtpPacket
+#endif
// Connect with DTLS-SRTP, transfer an invalid SRTP packet, and expects -1
// returned.
-TEST_F(DtlsTransportChannelTest, TestTransferDtlsInvalidSrtpPacket) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestTransferDtlsInvalidSrtpPacket) {
MAYBE_SKIP_TEST(HaveDtls);
PrepareDtls(true, true, rtc::KT_DEFAULT);
PrepareDtlsSrtp(true, true);
@@ -689,24 +725,47 @@ TEST_F(DtlsTransportChannelTest, TestTransferDtlsInvalidSrtpPacket) {
ASSERT_EQ(-1, result);
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestTransferDtlsSrtpRejected DISABLED_TestTransferDtlsSrtpRejected
+#else
+#define MAYBE_TestTransferDtlsSrtpRejected TestTransferDtlsSrtpRejected
+#endif
// Connect with DTLS. A does DTLS-SRTP but B does not.
-TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpRejected) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestTransferDtlsSrtpRejected) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
PrepareDtls(true, true, rtc::KT_DEFAULT);
PrepareDtlsSrtp(true, false);
ASSERT_TRUE(Connect());
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestTransferDtlsSrtpNotOffered \
+ DISABLED_TestTransferDtlsSrtpNotOffered
+#else
+#define MAYBE_TestTransferDtlsSrtpNotOffered TestTransferDtlsSrtpNotOffered
+#endif
// Connect with DTLS. B does DTLS-SRTP but A does not.
-TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpNotOffered) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestTransferDtlsSrtpNotOffered) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
PrepareDtls(true, true, rtc::KT_DEFAULT);
PrepareDtlsSrtp(false, true);
ASSERT_TRUE(Connect());
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestTransferDtlsSrtpTwoChannels \
+ DISABLED_TestTransferDtlsSrtpTwoChannels
+#else
+#define MAYBE_TestTransferDtlsSrtpTwoChannels TestTransferDtlsSrtpTwoChannels
+#endif
// Create two channels with DTLS, negotiate DTLS-SRTP, and transfer bypass SRTP.
-TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpTwoChannels) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestTransferDtlsSrtpTwoChannels) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true, rtc::KT_DEFAULT);
@@ -716,8 +775,15 @@ TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpTwoChannels) {
TestTransfer(1, 1000, 100, true);
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestTransferDtlsSrtpDemux DISABLED_TestTransferDtlsSrtpDemux
+#else
+#define MAYBE_TestTransferDtlsSrtpDemux TestTransferDtlsSrtpDemux
+#endif
// Create a single channel with DTLS, and send normal data and SRTP data on it.
-TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpDemux) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestTransferDtlsSrtpDemux) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
PrepareDtls(true, true, rtc::KT_DEFAULT);
PrepareDtlsSrtp(true, true);
@@ -726,8 +792,17 @@ TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpDemux) {
TestTransfer(0, 1000, 100, true);
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestTransferDtlsAnswererIsPassive \
+ DISABLED_TestTransferDtlsAnswererIsPassive
+#else
+#define MAYBE_TestTransferDtlsAnswererIsPassive \
+ TestTransferDtlsAnswererIsPassive
+#endif
// Testing when the remote is passive.
-TEST_F(DtlsTransportChannelTest, TestTransferDtlsAnswererIsPassive) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestTransferDtlsAnswererIsPassive) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true, rtc::KT_DEFAULT);
@@ -752,9 +827,16 @@ TEST_F(DtlsTransportChannelTest, TestDtlsSetupWithLegacyAsAnswerer) {
EXPECT_EQ(rtc::SSL_CLIENT, channel2_role);
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestDtlsReOfferFromOfferer DISABLED_TestDtlsReOfferFromOfferer
+#else
+#define MAYBE_TestDtlsReOfferFromOfferer TestDtlsReOfferFromOfferer
+#endif
// Testing re offer/answer after the session is estbalished. Roles will be
// kept same as of the previous negotiation.
-TEST_F(DtlsTransportChannelTest, TestDtlsReOfferFromOfferer) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestDtlsReOfferFromOfferer) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true, rtc::KT_DEFAULT);
@@ -771,7 +853,14 @@ TEST_F(DtlsTransportChannelTest, TestDtlsReOfferFromOfferer) {
TestTransfer(1, 1000, 100, true);
}
-TEST_F(DtlsTransportChannelTest, TestDtlsReOfferFromAnswerer) {
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestDtlsReOfferFromAnswerer DISABLED_TestDtlsReOfferFromAnswerer
+#else
+#define MAYBE_TestDtlsReOfferFromAnswerer TestDtlsReOfferFromAnswerer
+#endif
+TEST_F(DtlsTransportChannelTest, MAYBE_TestDtlsReOfferFromAnswerer) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true, rtc::KT_DEFAULT);
@@ -788,8 +877,15 @@ TEST_F(DtlsTransportChannelTest, TestDtlsReOfferFromAnswerer) {
TestTransfer(1, 1000, 100, true);
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestDtlsRoleReversal DISABLED_TestDtlsRoleReversal
+#else
+#define MAYBE_TestDtlsRoleReversal TestDtlsRoleReversal
+#endif
// Test that any change in role after the intial setup will result in failure.
-TEST_F(DtlsTransportChannelTest, TestDtlsRoleReversal) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestDtlsRoleReversal) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true, rtc::KT_DEFAULT);
@@ -803,9 +899,18 @@ TEST_F(DtlsTransportChannelTest, TestDtlsRoleReversal) {
NF_REOFFER | NF_EXPECT_FAILURE);
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestDtlsReOfferWithDifferentSetupAttr \
+ DISABLED_TestDtlsReOfferWithDifferentSetupAttr
+#else
+#define MAYBE_TestDtlsReOfferWithDifferentSetupAttr \
+ TestDtlsReOfferWithDifferentSetupAttr
+#endif
// Test that using different setup attributes which results in similar ssl
// role as the initial negotiation will result in success.
-TEST_F(DtlsTransportChannelTest, TestDtlsReOfferWithDifferentSetupAttr) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestDtlsReOfferWithDifferentSetupAttr) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true, rtc::KT_DEFAULT);
@@ -865,8 +970,15 @@ TEST_F(DtlsTransportChannelTest, TestCertificatesBeforeConnect) {
ASSERT_FALSE(remote_cert2 != NULL);
}
+#if defined(MEMORY_SANITIZER)
+// Fails under MemorySanitizer:
+// See https://code.google.com/p/webrtc/issues/detail?id=5381.
+#define MAYBE_TestCertificatesAfterConnect DISABLED_TestCertificatesAfterConnect
+#else
+#define MAYBE_TestCertificatesAfterConnect TestCertificatesAfterConnect
+#endif
// Test Certificates state after connection.
-TEST_F(DtlsTransportChannelTest, TestCertificatesAfterConnect) {
+TEST_F(DtlsTransportChannelTest, MAYBE_TestCertificatesAfterConnect) {
MAYBE_SKIP_TEST(HaveDtls);
PrepareDtls(true, true, rtc::KT_DEFAULT);
ASSERT_TRUE(Connect());
diff --git a/webrtc/p2p/base/faketransportcontroller.h b/webrtc/p2p/base/faketransportcontroller.h
index 3e656fa4a3..65c59be98d 100644
--- a/webrtc/p2p/base/faketransportcontroller.h
+++ b/webrtc/p2p/base/faketransportcontroller.h
@@ -242,20 +242,20 @@ class FakeTransportChannel : public TransportChannelImpl,
bool IsDtlsActive() const override { return do_dtls_; }
- bool SetSrtpCiphers(const std::vector<std::string>& ciphers) override {
+ bool SetSrtpCryptoSuites(const std::vector<int>& ciphers) override {
srtp_ciphers_ = ciphers;
return true;
}
- bool GetSrtpCryptoSuite(std::string* cipher) override {
- if (!chosen_srtp_cipher_.empty()) {
- *cipher = chosen_srtp_cipher_;
+ bool GetSrtpCryptoSuite(int* crypto_suite) override {
+ if (chosen_crypto_suite_ != rtc::SRTP_INVALID_CRYPTO_SUITE) {
+ *crypto_suite = chosen_crypto_suite_;
return true;
}
return false;
}
- bool GetSslCipherSuite(int* cipher) override { return false; }
+ bool GetSslCipherSuite(int* cipher_suite) override { return false; }
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const {
return local_cert_;
@@ -275,7 +275,7 @@ class FakeTransportChannel : public TransportChannelImpl,
bool use_context,
uint8_t* result,
size_t result_len) override {
- if (!chosen_srtp_cipher_.empty()) {
+ if (chosen_crypto_suite_ != rtc::SRTP_INVALID_CRYPTO_SUITE) {
memset(result, 0xff, result_len);
return true;
}
@@ -284,14 +284,13 @@ class FakeTransportChannel : public TransportChannelImpl,
}
void NegotiateSrtpCiphers() {
- for (std::vector<std::string>::const_iterator it1 = srtp_ciphers_.begin();
+ for (std::vector<int>::const_iterator it1 = srtp_ciphers_.begin();
it1 != srtp_ciphers_.end(); ++it1) {
- for (std::vector<std::string>::const_iterator it2 =
- dest_->srtp_ciphers_.begin();
+ for (std::vector<int>::const_iterator it2 = dest_->srtp_ciphers_.begin();
it2 != dest_->srtp_ciphers_.end(); ++it2) {
if (*it1 == *it2) {
- chosen_srtp_cipher_ = *it1;
- dest_->chosen_srtp_cipher_ = *it2;
+ chosen_crypto_suite_ = *it1;
+ dest_->chosen_crypto_suite_ = *it2;
return;
}
}
@@ -322,8 +321,8 @@ class FakeTransportChannel : public TransportChannelImpl,
rtc::scoped_refptr<rtc::RTCCertificate> local_cert_;
rtc::FakeSSLCertificate* remote_cert_ = nullptr;
bool do_dtls_ = false;
- std::vector<std::string> srtp_ciphers_;
- std::string chosen_srtp_cipher_;
+ std::vector<int> srtp_ciphers_;
+ int chosen_crypto_suite_ = rtc::SRTP_INVALID_CRYPTO_SUITE;
int receiving_timeout_ = -1;
bool gather_continually_ = false;
IceRole role_ = ICEROLE_UNKNOWN;
@@ -333,7 +332,7 @@ class FakeTransportChannel : public TransportChannelImpl,
std::string remote_ice_ufrag_;
std::string remote_ice_pwd_;
IceMode remote_ice_mode_ = ICEMODE_FULL;
- rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10;
+ rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12;
rtc::SSLFingerprint dtls_fingerprint_;
rtc::SSLRole ssl_role_ = rtc::SSL_CLIENT;
size_t connection_count_ = 0;
@@ -454,7 +453,7 @@ class FakeTransport : public Transport {
FakeTransport* dest_ = nullptr;
bool async_ = false;
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
- rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10;
+ rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12;
};
// Fake TransportController class, which can be passed into a BaseChannel object
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
index 623085f9a8..952cfab747 100644
--- a/webrtc/p2p/base/p2ptransportchannel.cc
+++ b/webrtc/p2p/base/p2ptransportchannel.cc
@@ -187,6 +187,7 @@ namespace cricket {
// well on a 28.8K modem, which is the slowest connection on which the voice
// quality is reasonable at all.
static const uint32_t PING_PACKET_SIZE = 60 * 8;
+// TODO(honghaiz): Change the word DELAY to INTERVAL whenever appropriate.
// STRONG_PING_DELAY (480ms) is applied when the best connection is both
// writable and receiving.
static const uint32_t STRONG_PING_DELAY = 1000 * PING_PACKET_SIZE / 1000;
@@ -201,7 +202,6 @@ static const uint32_t MAX_CURRENT_STRONG_DELAY = 900;
static const int MIN_CHECK_RECEIVING_DELAY = 50; // ms
-
P2PTransportChannel::P2PTransportChannel(const std::string& transport_name,
int component,
P2PTransport* transport,
@@ -215,14 +215,13 @@ P2PTransportChannel::P2PTransportChannel(const std::string& transport_name,
best_connection_(NULL),
pending_best_connection_(NULL),
sort_dirty_(false),
- was_writable_(false),
remote_ice_mode_(ICEMODE_FULL),
ice_role_(ICEROLE_UNKNOWN),
tiebreaker_(0),
- remote_candidate_generation_(0),
gathering_state_(kIceGatheringNew),
check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5),
- receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50) {
+ receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50),
+ backup_connection_ping_interval_(0) {
uint32_t weak_ping_delay = ::strtoul(
webrtc::field_trial::FindFullName("WebRTC-StunInterPacketDelay").c_str(),
nullptr, 10);
@@ -241,6 +240,8 @@ P2PTransportChannel::~P2PTransportChannel() {
// Add the allocator session to our list so that we know which sessions
// are still active.
void P2PTransportChannel::AddAllocatorSession(PortAllocatorSession* session) {
+ ASSERT(worker_thread_ == rtc::Thread::Current());
+
session->set_generation(static_cast<uint32_t>(allocator_sessions_.size()));
allocator_sessions_.push_back(session);
@@ -295,9 +296,13 @@ void P2PTransportChannel::SetIceTiebreaker(uint64_t tiebreaker) {
tiebreaker_ = tiebreaker;
}
+TransportChannelState P2PTransportChannel::GetState() const {
+ return state_;
+}
+
// A channel is considered ICE completed once there is at most one active
// connection per network and at least one active connection.
-TransportChannelState P2PTransportChannel::GetState() const {
+TransportChannelState P2PTransportChannel::ComputeState() const {
if (!had_connection_) {
return TransportChannelState::STATE_INIT;
}
@@ -341,25 +346,23 @@ void P2PTransportChannel::SetIceCredentials(const std::string& ice_ufrag,
void P2PTransportChannel::SetRemoteIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd) {
ASSERT(worker_thread_ == rtc::Thread::Current());
- bool ice_restart = false;
- if (!remote_ice_ufrag_.empty() && !remote_ice_pwd_.empty()) {
- ice_restart = (remote_ice_ufrag_ != ice_ufrag) ||
- (remote_ice_pwd_!= ice_pwd);
+ IceParameters* current_ice = remote_ice();
+ IceParameters new_ice(ice_ufrag, ice_pwd);
+ if (!current_ice || *current_ice != new_ice) {
+ // Keep the ICE credentials so that newer connections
+ // are prioritized over the older ones.
+ remote_ice_parameters_.push_back(new_ice);
+ }
+
+ // Update the pwd of remote candidate if needed.
+ for (RemoteCandidate& candidate : remote_candidates_) {
+ if (candidate.username() == ice_ufrag && candidate.password().empty()) {
+ candidate.set_password(ice_pwd);
+ }
}
-
- remote_ice_ufrag_ = ice_ufrag;
- remote_ice_pwd_ = ice_pwd;
-
// We need to update the credentials for any peer reflexive candidates.
- std::vector<Connection*>::iterator it = connections_.begin();
- for (; it != connections_.end(); ++it) {
- (*it)->MaybeSetRemoteIceCredentials(ice_ufrag, ice_pwd);
- }
-
- if (ice_restart) {
- // We need to keep track of the remote ice restart so newer
- // connections are prioritized over the older.
- ++remote_candidate_generation_;
+ for (Connection* conn : connections_) {
+ conn->MaybeSetRemoteIceCredentials(ice_ufrag, ice_pwd);
}
}
@@ -371,18 +374,26 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) {
gather_continually_ = config.gather_continually;
LOG(LS_INFO) << "Set gather_continually to " << gather_continually_;
- if (config.receiving_timeout_ms < 0) {
- return;
+ if (config.backup_connection_ping_interval >= 0 &&
+ backup_connection_ping_interval_ !=
+ config.backup_connection_ping_interval) {
+ backup_connection_ping_interval_ = config.backup_connection_ping_interval;
+ LOG(LS_INFO) << "Set backup connection ping interval to "
+ << backup_connection_ping_interval_ << " milliseconds.";
}
- receiving_timeout_ = config.receiving_timeout_ms;
- check_receiving_delay_ =
- std::max(MIN_CHECK_RECEIVING_DELAY, receiving_timeout_ / 10);
- for (Connection* connection : connections_) {
- connection->set_receiving_timeout(receiving_timeout_);
+ if (config.receiving_timeout_ms >= 0 &&
+ receiving_timeout_ != config.receiving_timeout_ms) {
+ receiving_timeout_ = config.receiving_timeout_ms;
+ check_receiving_delay_ =
+ std::max(MIN_CHECK_RECEIVING_DELAY, receiving_timeout_ / 10);
+
+ for (Connection* connection : connections_) {
+ connection->set_receiving_timeout(receiving_timeout_);
+ }
+ LOG(LS_INFO) << "Set ICE receiving timeout to " << receiving_timeout_
+ << " milliseconds";
}
- LOG(LS_INFO) << "Set ICE receiving timeout to " << receiving_timeout_
- << " milliseconds";
}
// Go into the state of processing candidates, and running in general
@@ -519,11 +530,17 @@ void P2PTransportChannel::OnUnknownAddress(
}
}
+ uint32_t remote_generation = 0;
// The STUN binding request may arrive after setRemoteDescription and before
// adding remote candidate, so we need to set the password to the shared
// password if the user name matches.
- if (remote_password.empty() && remote_username == remote_ice_ufrag_) {
- remote_password = remote_ice_pwd_;
+ if (remote_password.empty()) {
+ const IceParameters* ice_param =
+ FindRemoteIceFromUfrag(remote_username, &remote_generation);
+ // Note: if not found, the remote_generation will still be 0.
+ if (ice_param != nullptr) {
+ remote_password = ice_param->pwd;
+ }
}
Candidate remote_candidate;
@@ -555,9 +572,9 @@ void P2PTransportChannel::OnUnknownAddress(
// If the source transport address of the request does not match any
// existing remote candidates, it represents a new peer reflexive remote
// candidate.
- remote_candidate =
- Candidate(component(), ProtoToString(proto), address, 0,
- remote_username, remote_password, PRFLX_PORT_TYPE, 0U, "");
+ remote_candidate = Candidate(component(), ProtoToString(proto), address, 0,
+ remote_username, remote_password,
+ PRFLX_PORT_TYPE, remote_generation, "");
// From RFC 5245, section-7.2.1.3:
// The foundation of the candidate is set to an arbitrary value, different
@@ -604,14 +621,7 @@ void P2PTransportChannel::OnUnknownAddress(
<< (remote_candidate_is_new ? "peer reflexive" : "resurrected")
<< " candidate: " << remote_candidate.ToString();
AddConnection(connection);
- connection->ReceivedPing();
-
- bool received_use_candidate =
- stun_msg->GetByteString(STUN_ATTR_USE_CANDIDATE) != nullptr;
- if (received_use_candidate && ice_role_ == ICEROLE_CONTROLLED) {
- connection->set_nominated(true);
- OnNominated(connection);
- }
+ connection->HandleBindingRequest(stun_msg);
// Update the list of connections since we just added another. We do this
// after sending the response since it could (in principle) delete the
@@ -624,6 +634,21 @@ void P2PTransportChannel::OnRoleConflict(PortInterface* port) {
// from Transport.
}
+const IceParameters* P2PTransportChannel::FindRemoteIceFromUfrag(
+ const std::string& ufrag,
+ uint32_t* generation) {
+ const auto& params = remote_ice_parameters_;
+ auto it = std::find_if(
+ params.rbegin(), params.rend(),
+ [ufrag](const IceParameters& param) { return param.ufrag == ufrag; });
+ if (it == params.rend()) {
+ // Not found.
+ return nullptr;
+ }
+ *generation = params.rend() - it - 1;
+ return &(*it);
+}
+
void P2PTransportChannel::OnNominated(Connection* conn) {
ASSERT(worker_thread_ == rtc::Thread::Current());
ASSERT(ice_role_ == ICEROLE_CONTROLLED);
@@ -648,19 +673,39 @@ void P2PTransportChannel::OnNominated(Connection* conn) {
void P2PTransportChannel::AddRemoteCandidate(const Candidate& candidate) {
ASSERT(worker_thread_ == rtc::Thread::Current());
- uint32_t generation = candidate.generation();
- // Network may not guarantee the order of the candidate delivery. If a
- // remote candidate with an older generation arrives, drop it.
- if (generation != 0 && generation < remote_candidate_generation_) {
- LOG(LS_WARNING) << "Dropping a remote candidate because its generation "
- << generation
- << " is lower than the current remote generation "
- << remote_candidate_generation_;
+ uint32_t generation = GetRemoteCandidateGeneration(candidate);
+ // If a remote candidate with a previous generation arrives, drop it.
+ if (generation < remote_ice_generation()) {
+ LOG(LS_WARNING) << "Dropping a remote candidate because its ufrag "
+ << candidate.username()
+ << " indicates it was for a previous generation.";
return;
}
+ Candidate new_remote_candidate(candidate);
+ new_remote_candidate.set_generation(generation);
+ // ICE candidates don't need to have username and password set, but
+ // the code below this (specifically, ConnectionRequest::Prepare in
+ // port.cc) uses the remote candidates's username. So, we set it
+ // here.
+ if (remote_ice()) {
+ if (candidate.username().empty()) {
+ new_remote_candidate.set_username(remote_ice()->ufrag);
+ }
+ if (new_remote_candidate.username() == remote_ice()->ufrag) {
+ if (candidate.password().empty()) {
+ new_remote_candidate.set_password(remote_ice()->pwd);
+ }
+ } else {
+ // The candidate belongs to the next generation. Its pwd will be set
+ // when the new remote ICE credentials arrive.
+ LOG(LS_WARNING) << "A remote candidate arrives with an unknown ufrag: "
+ << candidate.username();
+ }
+ }
+
// Create connections to this remote candidate.
- CreateConnections(candidate, NULL);
+ CreateConnections(new_remote_candidate, NULL);
// Resort the connections list, which may have new elements.
SortConnections();
@@ -673,20 +718,6 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate,
PortInterface* origin_port) {
ASSERT(worker_thread_ == rtc::Thread::Current());
- Candidate new_remote_candidate(remote_candidate);
- new_remote_candidate.set_generation(
- GetRemoteCandidateGeneration(remote_candidate));
- // ICE candidates don't need to have username and password set, but
- // the code below this (specifically, ConnectionRequest::Prepare in
- // port.cc) uses the remote candidates's username. So, we set it
- // here.
- if (remote_candidate.username().empty()) {
- new_remote_candidate.set_username(remote_ice_ufrag_);
- }
- if (remote_candidate.password().empty()) {
- new_remote_candidate.set_password(remote_ice_pwd_);
- }
-
// If we've already seen the new remote candidate (in the current candidate
// generation), then we shouldn't try creating connections for it.
// We either already have a connection for it, or we previously created one
@@ -695,7 +726,7 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate,
// immediately be re-pruned, churning the network for no purpose.
// This only applies to candidates received over signaling (i.e. origin_port
// is NULL).
- if (!origin_port && IsDuplicateRemoteCandidate(new_remote_candidate)) {
+ if (!origin_port && IsDuplicateRemoteCandidate(remote_candidate)) {
// return true to indicate success, without creating any new connections.
return true;
}
@@ -708,7 +739,7 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate,
bool created = false;
std::vector<PortInterface *>::reverse_iterator it;
for (it = ports_.rbegin(); it != ports_.rend(); ++it) {
- if (CreateConnection(*it, new_remote_candidate, origin_port)) {
+ if (CreateConnection(*it, remote_candidate, origin_port)) {
if (*it == origin_port)
created = true;
}
@@ -716,12 +747,12 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate,
if ((origin_port != NULL) &&
std::find(ports_.begin(), ports_.end(), origin_port) == ports_.end()) {
- if (CreateConnection(origin_port, new_remote_candidate, origin_port))
+ if (CreateConnection(origin_port, remote_candidate, origin_port))
created = true;
}
// Remember this remote candidate so that we can add it to future ports.
- RememberRemoteCandidate(new_remote_candidate, origin_port);
+ RememberRemoteCandidate(remote_candidate, origin_port);
return created;
}
@@ -731,6 +762,9 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate,
bool P2PTransportChannel::CreateConnection(PortInterface* port,
const Candidate& remote_candidate,
PortInterface* origin_port) {
+ if (!port->SupportsProtocol(remote_candidate.protocol())) {
+ return false;
+ }
// Look for an existing connection with this remote address. If one is not
// found, then we can create a new connection for this address.
Connection* connection = port->GetConnection(remote_candidate.address());
@@ -777,11 +811,21 @@ bool P2PTransportChannel::FindConnection(
uint32_t P2PTransportChannel::GetRemoteCandidateGeneration(
const Candidate& candidate) {
- // We need to keep track of the remote ice restart so newer
- // connections are prioritized over the older.
- ASSERT(candidate.generation() == 0 ||
- candidate.generation() == remote_candidate_generation_);
- return remote_candidate_generation_;
+ // If the candidate has a ufrag, use it to find the generation.
+ if (!candidate.username().empty()) {
+ uint32_t generation = 0;
+ if (!FindRemoteIceFromUfrag(candidate.username(), &generation)) {
+ // If the ufrag is not found, assume the next/future generation.
+ generation = static_cast<uint32_t>(remote_ice_parameters_.size());
+ }
+ return generation;
+ }
+ // If candidate generation is set, use that.
+ if (candidate.generation() > 0) {
+ return candidate.generation();
+ }
+ // Otherwise, assume the generation from remote ice parameters.
+ return remote_ice_generation();
}
// Check if remote candidate is already cached.
@@ -990,17 +1034,13 @@ void P2PTransportChannel::SortConnections() {
// Now update the writable state of the channel with the information we have
// so far.
- if (best_connection_ && best_connection_->writable()) {
- HandleWritable();
- } else if (all_connections_timedout) {
+ if (all_connections_timedout) {
HandleAllTimedOut();
- } else {
- HandleNotWritable();
}
// Update the state of this channel. This method is called whenever the
// state of any connection changes, so this is a good place to do this.
- UpdateChannelState();
+ UpdateState();
}
Connection* P2PTransportChannel::best_nominated_connection() const {
@@ -1060,13 +1100,17 @@ void P2PTransportChannel::SwitchBestConnectionTo(Connection* conn) {
}
}
-void P2PTransportChannel::UpdateChannelState() {
- // The Handle* functions already set the writable state. We'll just double-
- // check it here.
+// Warning: UpdateState should eventually be called whenever a connection
+// is added, deleted, or the write state of any connection changes so that the
+// transport controller will get the up-to-date channel state. However it
+// should not be called too often; in the case that multiple connection states
+// change, it should be called after all the connection states have changed. For
+// example, we call this at the end of SortConnections.
+void P2PTransportChannel::UpdateState() {
+ state_ = ComputeState();
+
bool writable = best_connection_ && best_connection_->writable();
- ASSERT(writable == this->writable());
- if (writable != this->writable())
- LOG(LS_ERROR) << "UpdateChannelState: writable state mismatch";
+ set_writable(writable);
bool receiving = false;
for (const Connection* connection : connections_) {
@@ -1078,11 +1122,8 @@ void P2PTransportChannel::UpdateChannelState() {
set_receiving(receiving);
}
-// We checked the status of our connections and we had at least one that
-// was writable, go into the writable state.
-void P2PTransportChannel::HandleWritable() {
- ASSERT(worker_thread_ == rtc::Thread::Current());
- if (writable()) {
+void P2PTransportChannel::MaybeStopPortAllocatorSessions() {
+ if (!IsGettingPorts()) {
return;
}
@@ -1098,18 +1139,6 @@ void P2PTransportChannel::HandleWritable() {
}
session->StopGettingPorts();
}
-
- was_writable_ = true;
- set_writable(true);
-}
-
-// Notify upper layer about channel not writable state, if it was before.
-void P2PTransportChannel::HandleNotWritable() {
- ASSERT(worker_thread_ == rtc::Thread::Current());
- if (was_writable_) {
- was_writable_ = false;
- set_writable(false);
- }
}
// If all connections timed out, delete them all.
@@ -1179,10 +1208,17 @@ void P2PTransportChannel::OnCheckAndPing() {
thread()->PostDelayed(check_delay, this, MSG_CHECK_AND_PING);
}
+// A connection is considered a backup connection if the channel state
+// is completed, the connection is not the best connection and it is active.
+bool P2PTransportChannel::IsBackupConnection(Connection* conn) const {
+ return state_ == STATE_COMPLETED && conn != best_connection_ &&
+ conn->active();
+}
+
// Is the connection in a state for us to even consider pinging the other side?
// We consider a connection pingable even if it's not connected because that's
// how a TCP connection is kicked into reconnecting on the active side.
-bool P2PTransportChannel::IsPingable(Connection* conn) {
+bool P2PTransportChannel::IsPingable(Connection* conn, uint32_t now) {
const Candidate& remote = conn->remote_candidate();
// We should never get this far with an empty remote ufrag.
ASSERT(!remote.username().empty());
@@ -1198,9 +1234,18 @@ bool P2PTransportChannel::IsPingable(Connection* conn) {
return false;
}
- // If the channel is weak, ping all candidates. Otherwise, we only
- // want to ping connections that have not timed out on writing.
- return weak() || conn->write_state() != Connection::STATE_WRITE_TIMEOUT;
+ // If the channel is weakly connected, ping all connections.
+ if (weak()) {
+ return true;
+ }
+
+ // Always ping active connections regardless whether the channel is completed
+ // or not, but backup connections are pinged at a slower rate.
+ if (IsBackupConnection(conn)) {
+ return (now >= conn->last_ping_response_received() +
+ backup_connection_ping_interval_);
+ }
+ return conn->active();
}
// Returns the next pingable connection to ping. This will be the oldest
@@ -1224,7 +1269,7 @@ Connection* P2PTransportChannel::FindNextPingableConnection() {
Connection* oldest_needing_triggered_check = nullptr;
Connection* oldest = nullptr;
for (Connection* conn : connections_) {
- if (!IsPingable(conn)) {
+ if (!IsPingable(conn, now)) {
continue;
}
bool needs_triggered_check =
@@ -1291,6 +1336,14 @@ void P2PTransportChannel::OnConnectionStateChange(Connection* connection) {
}
}
+ // May stop the allocator session when at least one connection becomes
+ // strongly connected after starting to get ports. It is not enough to check
+ // that the connection becomes weakly connected because the connection may be
+ // changing from (writable, receiving) to (writable, not receiving).
+ if (!connection->weak()) {
+ MaybeStopPortAllocatorSessions();
+ }
+
// We have to unroll the stack before doing this because we may be changing
// the state of connections while sorting.
RequestSort();
@@ -1328,6 +1381,9 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) {
RequestSort();
}
+ UpdateState();
+ // SignalConnectionRemoved should be called after the channel state is
+ // updated because the receiver of the event may access the channel state.
SignalConnectionRemoved(this);
}
@@ -1368,8 +1424,7 @@ void P2PTransportChannel::OnReadPacket(Connection* connection,
}
}
-void P2PTransportChannel::OnSentPacket(PortInterface* port,
- const rtc::SentPacket& sent_packet) {
+void P2PTransportChannel::OnSentPacket(const rtc::SentPacket& sent_packet) {
ASSERT(worker_thread_ == rtc::Thread::Current());
SignalSentPacket(this, sent_packet);
diff --git a/webrtc/p2p/base/p2ptransportchannel.h b/webrtc/p2p/base/p2ptransportchannel.h
index 9efb96c42d..f2e9315343 100644
--- a/webrtc/p2p/base/p2ptransportchannel.h
+++ b/webrtc/p2p/base/p2ptransportchannel.h
@@ -36,6 +36,18 @@ namespace cricket {
extern const uint32_t WEAK_PING_DELAY;
+struct IceParameters {
+ std::string ufrag;
+ std::string pwd;
+ IceParameters(const std::string& ice_ufrag, const std::string& ice_pwd)
+ : ufrag(ice_ufrag), pwd(ice_pwd) {}
+
+ bool operator==(const IceParameters& other) {
+ return ufrag == other.ufrag && pwd == other.pwd;
+ }
+ bool operator!=(const IceParameters& other) { return !(*this == other); }
+};
+
// Adds the port on which the candidate originated.
class RemoteCandidate : public Candidate {
public:
@@ -108,12 +120,12 @@ class P2PTransportChannel : public TransportChannelImpl,
bool SetSslRole(rtc::SSLRole role) override { return false; }
// Set up the ciphers to use for DTLS-SRTP.
- bool SetSrtpCiphers(const std::vector<std::string>& ciphers) override {
+ bool SetSrtpCryptoSuites(const std::vector<int>& ciphers) override {
return false;
}
// Find out which DTLS-SRTP cipher was negotiated.
- bool GetSrtpCryptoSuite(std::string* cipher) override { return false; }
+ bool GetSrtpCryptoSuite(int* cipher) override { return false; }
// Find out which DTLS cipher was negotiated.
bool GetSslCipherSuite(int* cipher) override { return false; }
@@ -161,12 +173,20 @@ class P2PTransportChannel : public TransportChannelImpl,
// Public for unit tests.
const std::vector<Connection*>& connections() const { return connections_; }
- private:
- rtc::Thread* thread() { return worker_thread_; }
+ // Public for unit tests.
PortAllocatorSession* allocator_session() {
return allocator_sessions_.back();
}
+ // Public for unit tests.
+ const std::vector<RemoteCandidate>& remote_candidates() const {
+ return remote_candidates_;
+ }
+
+ private:
+ rtc::Thread* thread() { return worker_thread_; }
+ bool IsGettingPorts() { return allocator_session()->IsGettingPorts(); }
+
// A transport channel is weak if the current best connection is either
// not receiving or not writable, or if there is no best connection at all.
bool weak() const;
@@ -174,10 +194,10 @@ class P2PTransportChannel : public TransportChannelImpl,
void RequestSort();
void SortConnections();
void SwitchBestConnectionTo(Connection* conn);
- void UpdateChannelState();
- void HandleWritable();
- void HandleNotWritable();
+ void UpdateState();
void HandleAllTimedOut();
+ void MaybeStopPortAllocatorSessions();
+ TransportChannelState ComputeState() const;
Connection* GetBestConnectionOnNetwork(rtc::Network* network) const;
bool CreateConnections(const Candidate& remote_candidate,
@@ -191,7 +211,7 @@ class P2PTransportChannel : public TransportChannelImpl,
bool IsDuplicateRemoteCandidate(const Candidate& candidate);
void RememberRemoteCandidate(const Candidate& remote_candidate,
PortInterface* origin_port);
- bool IsPingable(Connection* conn);
+ bool IsPingable(Connection* conn, uint32_t now);
void PingConnection(Connection* conn);
void AddAllocatorSession(PortAllocatorSession* session);
void AddConnection(Connection* connection);
@@ -212,7 +232,7 @@ class P2PTransportChannel : public TransportChannelImpl,
void OnConnectionStateChange(Connection* connection);
void OnReadPacket(Connection *connection, const char *data, size_t len,
const rtc::PacketTime& packet_time);
- void OnSentPacket(PortInterface* port, const rtc::SentPacket& sent_packet);
+ void OnSentPacket(const rtc::SentPacket& sent_packet);
void OnReadyToSend(Connection* connection);
void OnConnectionDestroyed(Connection *connection);
@@ -224,6 +244,25 @@ class P2PTransportChannel : public TransportChannelImpl,
void PruneConnections();
Connection* best_nominated_connection() const;
+ bool IsBackupConnection(Connection* conn) const;
+
+ // Returns the latest remote ICE parameters or nullptr if there are no remote
+ // ICE parameters yet.
+ IceParameters* remote_ice() {
+ return remote_ice_parameters_.empty() ? nullptr
+ : &remote_ice_parameters_.back();
+ }
+ // Returns the remote IceParameters and generation that match |ufrag|
+ // if found, and returns nullptr otherwise.
+ const IceParameters* FindRemoteIceFromUfrag(const std::string& ufrag,
+ uint32_t* generation);
+ // Returns the index of the latest remote ICE parameters, or 0 if no remote
+ // ICE parameters have been received.
+ uint32_t remote_ice_generation() {
+ return remote_ice_parameters_.empty()
+ ? 0
+ : static_cast<uint32_t>(remote_ice_parameters_.size() - 1);
+ }
P2PTransport* transport_;
PortAllocator* allocator_;
@@ -239,25 +278,24 @@ class P2PTransportChannel : public TransportChannelImpl,
Connection* pending_best_connection_;
std::vector<RemoteCandidate> remote_candidates_;
bool sort_dirty_; // indicates whether another sort is needed right now
- bool was_writable_;
bool had_connection_ = false; // if connections_ has ever been nonempty
typedef std::map<rtc::Socket::Option, int> OptionMap;
OptionMap options_;
std::string ice_ufrag_;
std::string ice_pwd_;
- std::string remote_ice_ufrag_;
- std::string remote_ice_pwd_;
+ std::vector<IceParameters> remote_ice_parameters_;
IceMode remote_ice_mode_;
IceRole ice_role_;
uint64_t tiebreaker_;
- uint32_t remote_candidate_generation_;
IceGatheringState gathering_state_;
int check_receiving_delay_;
int receiving_timeout_;
+ int backup_connection_ping_interval_;
uint32_t last_ping_sent_ms_ = 0;
bool gather_continually_ = false;
int weak_ping_delay_ = WEAK_PING_DELAY;
+ TransportChannelState state_ = TransportChannelState::STATE_INIT;
RTC_DISALLOW_COPY_AND_ASSIGN(P2PTransportChannel);
};
diff --git a/webrtc/p2p/base/p2ptransportchannel_unittest.cc b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
index 37cda7c661..90ddd43714 100644
--- a/webrtc/p2p/base/p2ptransportchannel_unittest.cc
+++ b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
@@ -101,10 +101,12 @@ enum {
};
static cricket::IceConfig CreateIceConfig(int receiving_timeout_ms,
- bool gather_continually) {
+ bool gather_continually,
+ int backup_ping_interval = -1) {
cricket::IceConfig config;
config.receiving_timeout_ms = receiving_timeout_ms;
config.gather_continually = gather_continually;
+ config.backup_connection_ping_interval = backup_ping_interval;
return config;
}
@@ -650,6 +652,21 @@ class P2PTransportChannelTestBase : public testing::Test,
GetEndpoint(endpoint)->save_candidates_ = true;
}
+ // Tcp candidate verification has to be done when they are generated.
+ void VerifySavedTcpCandidates(int endpoint, const std::string& tcptype) {
+ for (auto& data : GetEndpoint(endpoint)->saved_candidates_) {
+ EXPECT_EQ(data->candidate.protocol(), cricket::TCP_PROTOCOL_NAME);
+ EXPECT_EQ(data->candidate.tcptype(), tcptype);
+ if (data->candidate.tcptype() == cricket::TCPTYPE_ACTIVE_STR) {
+ EXPECT_EQ(data->candidate.address().port(), cricket::DISCARD_PORT);
+ } else if (data->candidate.tcptype() == cricket::TCPTYPE_PASSIVE_STR) {
+ EXPECT_NE(data->candidate.address().port(), cricket::DISCARD_PORT);
+ } else {
+ FAIL() << "Unknown tcptype: " << data->candidate.tcptype();
+ }
+ }
+ }
+
void ResumeCandidates(int endpoint) {
Endpoint* ed = GetEndpoint(endpoint);
std::vector<CandidateData*>::iterator it = ed->saved_candidates_.begin();
@@ -825,12 +842,12 @@ class P2PTransportChannelTest : public P2PTransportChannelTestBase {
rtc::SocketAddress(), rtc::SocketAddress(),
rtc::SocketAddress()));
- cricket::RelayServerConfig relay_server(cricket::RELAY_TURN);
- relay_server.credentials = kRelayCredentials;
- relay_server.ports.push_back(
+ cricket::RelayServerConfig turn_server(cricket::RELAY_TURN);
+ turn_server.credentials = kRelayCredentials;
+ turn_server.ports.push_back(
cricket::ProtocolAddress(kTurnUdpIntAddr, cricket::PROTO_UDP, false));
- GetEndpoint(0)->allocator_->AddRelay(relay_server);
- GetEndpoint(1)->allocator_->AddRelay(relay_server);
+ GetEndpoint(0)->allocator_->AddTurnServer(turn_server);
+ GetEndpoint(1)->allocator_->AddTurnServer(turn_server);
int delay = kMinimumStepDelay;
ConfigureEndpoint(0, config1);
@@ -1290,8 +1307,19 @@ TEST_F(P2PTransportChannelTest, TestTcpConnectionsFromActiveToPassive) {
SetAllowTcpListen(0, true); // actpass.
SetAllowTcpListen(1, false); // active.
+ // Pause candidate so we could verify the candidate properties.
+ PauseCandidates(0);
+ PauseCandidates(1);
CreateChannels(1);
+ // Verify tcp candidates.
+ VerifySavedTcpCandidates(0, cricket::TCPTYPE_PASSIVE_STR);
+ VerifySavedTcpCandidates(1, cricket::TCPTYPE_ACTIVE_STR);
+
+ // Resume candidates.
+ ResumeCandidates(0);
+ ResumeCandidates(1);
+
EXPECT_TRUE_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() && ep2_ch1()->writable(),
1000);
@@ -1300,12 +1328,6 @@ TEST_F(P2PTransportChannelTest, TestTcpConnectionsFromActiveToPassive) {
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
- std::string kTcpProtocol = "tcp";
- EXPECT_EQ(kTcpProtocol, RemoteCandidate(ep1_ch1())->protocol());
- EXPECT_EQ(kTcpProtocol, LocalCandidate(ep1_ch1())->protocol());
- EXPECT_EQ(kTcpProtocol, RemoteCandidate(ep2_ch1())->protocol());
- EXPECT_EQ(kTcpProtocol, LocalCandidate(ep2_ch1())->protocol());
-
TestSendRecv(1);
DestroyChannels();
}
@@ -1539,19 +1561,19 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControlledSide) {
// Create channels and let them go writable, as usual.
CreateChannels(1);
- // Make the receiving timeout shorter for testing.
- cricket::IceConfig config = CreateIceConfig(1000, false);
- ep1_ch1()->SetIceConfig(config);
- ep2_ch1()->SetIceConfig(config);
-
- EXPECT_TRUE_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
- ep2_ch1()->receiving() && ep2_ch1()->writable(),
- 1000);
+ EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
+ ep2_ch1()->receiving() && ep2_ch1()->writable(),
+ 1000, 1000);
EXPECT_TRUE(
ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
+ // Make the receiving timeout shorter for testing.
+ cricket::IceConfig config = CreateIceConfig(1000, false);
+ ep1_ch1()->SetIceConfig(config);
+ ep2_ch1()->SetIceConfig(config);
+
// Blackhole any traffic to or from the public addrs.
LOG(LS_INFO) << "Failing over...";
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[1]);
@@ -1591,18 +1613,19 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) {
// Create channels and let them go writable, as usual.
CreateChannels(1);
- // Make the receiving timeout shorter for testing.
- cricket::IceConfig config = CreateIceConfig(1000, false);
- ep1_ch1()->SetIceConfig(config);
- ep2_ch1()->SetIceConfig(config);
- EXPECT_TRUE_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
- ep2_ch1()->receiving() && ep2_ch1()->writable(),
- 1000);
+ EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
+ ep2_ch1()->receiving() && ep2_ch1()->writable(),
+ 1000, 1000);
EXPECT_TRUE(
ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
+ // Make the receiving timeout shorter for testing.
+ cricket::IceConfig config = CreateIceConfig(1000, false);
+ ep1_ch1()->SetIceConfig(config);
+ ep2_ch1()->SetIceConfig(config);
+
// Blackhole any traffic to or from the public addrs.
LOG(LS_INFO) << "Failing over...";
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[0]);
@@ -1627,6 +1650,43 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) {
DestroyChannels();
}
+// Test that the backup connection is pinged at a rate no faster than
+// what was configured.
+TEST_F(P2PTransportChannelMultihomedTest, TestPingBackupConnectionRate) {
+ AddAddress(0, kPublicAddrs[0]);
+ // Adding alternate address will make sure |kPublicAddrs| has the higher
+ // priority than others. This is due to FakeNetwork::AddInterface method.
+ AddAddress(1, kAlternateAddrs[1]);
+ AddAddress(1, kPublicAddrs[1]);
+
+ // Use only local ports for simplicity.
+ SetAllocatorFlags(0, kOnlyLocalPorts);
+ SetAllocatorFlags(1, kOnlyLocalPorts);
+
+ // Create channels and let them go writable, as usual.
+ CreateChannels(1);
+ EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
+ ep2_ch1()->receiving() && ep2_ch1()->writable(),
+ 1000, 1000);
+ int backup_ping_interval = 2000;
+ ep2_ch1()->SetIceConfig(CreateIceConfig(2000, false, backup_ping_interval));
+ // After the state becomes COMPLETED, the backup connection will be pinged
+ // once every |backup_ping_interval| milliseconds.
+ ASSERT_TRUE_WAIT(ep2_ch1()->GetState() == cricket::STATE_COMPLETED, 1000);
+ const std::vector<cricket::Connection*>& connections =
+ ep2_ch1()->connections();
+ ASSERT_EQ(2U, connections.size());
+ cricket::Connection* backup_conn = connections[1];
+ EXPECT_TRUE_WAIT(backup_conn->writable(), 3000);
+ uint32_t last_ping_response_ms = backup_conn->last_ping_response_received();
+ EXPECT_TRUE_WAIT(
+ last_ping_response_ms < backup_conn->last_ping_response_received(), 5000);
+ int time_elapsed =
+ backup_conn->last_ping_response_received() - last_ping_response_ms;
+ LOG(LS_INFO) << "Time elapsed: " << time_elapsed;
+ EXPECT_GE(time_elapsed, backup_ping_interval);
+}
+
TEST_F(P2PTransportChannelMultihomedTest, TestGetState) {
AddAddress(0, kAlternateAddrs[0]);
AddAddress(0, kPublicAddrs[0]);
@@ -1707,12 +1767,14 @@ class P2PTransportChannelPingTest : public testing::Test,
cricket::Candidate CreateCandidate(const std::string& ip,
int port,
- int priority) {
+ int priority,
+ const std::string& ufrag = "") {
cricket::Candidate c;
c.set_address(rtc::SocketAddress(ip, port));
c.set_component(1);
c.set_protocol(cricket::UDP_PROTOCOL_NAME);
c.set_priority(priority);
+ c.set_username(ufrag);
return c;
}
@@ -1796,6 +1858,62 @@ TEST_F(P2PTransportChannelPingTest, TestNoTriggeredChecksWhenWritable) {
EXPECT_EQ(conn2, ch.FindNextPingableConnection());
}
+// Test adding remote candidates with different ufrags. If a remote candidate
+// is added with an old ufrag, it will be discarded. If it is added with a
+// ufrag that was not seen before, it will be used to create connections
+// although the ICE pwd in the remote candidate will be set when the ICE
+// credentials arrive. If a remote candidate is added with the current ICE
+// ufrag, its pwd and generation will be set properly.
+TEST_F(P2PTransportChannelPingTest, TestAddRemoteCandidateWithVariousUfrags) {
+ cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
+ cricket::P2PTransportChannel ch("add candidate", 1, nullptr, &pa);
+ PrepareChannel(&ch);
+ ch.Connect();
+ ch.MaybeStartGathering();
+ // Add a candidate with a future ufrag.
+ ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1, kIceUfrag[2]));
+ cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
+ ASSERT_TRUE(conn1 != nullptr);
+ const cricket::Candidate& candidate = conn1->remote_candidate();
+ EXPECT_EQ(kIceUfrag[2], candidate.username());
+ EXPECT_TRUE(candidate.password().empty());
+ EXPECT_TRUE(ch.FindNextPingableConnection() == nullptr);
+
+ // Set the remote credentials with the "future" ufrag.
+ // This should set the ICE pwd in the remote candidate of |conn1|, making
+ // it pingable.
+ ch.SetRemoteIceCredentials(kIceUfrag[2], kIcePwd[2]);
+ EXPECT_EQ(kIceUfrag[2], candidate.username());
+ EXPECT_EQ(kIcePwd[2], candidate.password());
+ EXPECT_EQ(conn1, ch.FindNextPingableConnection());
+
+ // Add a candidate with an old ufrag. No connection will be created.
+ ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2, kIceUfrag[1]));
+ rtc::Thread::Current()->ProcessMessages(500);
+ EXPECT_TRUE(GetConnectionTo(&ch, "2.2.2.2", 2) == nullptr);
+
+ // Add a candidate with the current ufrag, its pwd and generation will be
+ // assigned, even if the generation is not set.
+ ch.AddRemoteCandidate(CreateCandidate("3.3.3.3", 3, 0, kIceUfrag[2]));
+ cricket::Connection* conn3 = nullptr;
+ ASSERT_TRUE_WAIT((conn3 = GetConnectionTo(&ch, "3.3.3.3", 3)) != nullptr,
+ 3000);
+ const cricket::Candidate& new_candidate = conn3->remote_candidate();
+ EXPECT_EQ(kIcePwd[2], new_candidate.password());
+ EXPECT_EQ(1U, new_candidate.generation());
+
+ // Check that the pwd of all remote candidates are properly assigned.
+ for (const cricket::RemoteCandidate& candidate : ch.remote_candidates()) {
+ EXPECT_TRUE(candidate.username() == kIceUfrag[1] ||
+ candidate.username() == kIceUfrag[2]);
+ if (candidate.username() == kIceUfrag[1]) {
+ EXPECT_EQ(kIcePwd[1], candidate.password());
+ } else if (candidate.username() == kIceUfrag[2]) {
+ EXPECT_EQ(kIcePwd[2], candidate.password());
+ }
+ }
+}
+
TEST_F(P2PTransportChannelPingTest, ConnectionResurrection) {
cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
cricket::P2PTransportChannel ch("connection resurrection", 1, nullptr, &pa);
@@ -1868,7 +1986,7 @@ TEST_F(P2PTransportChannelPingTest, TestReceivingStateChange) {
conn1->ReceivedPing();
conn1->OnReadPacket("ABC", 3, rtc::CreatePacketTime(0));
- EXPECT_TRUE_WAIT(ch.best_connection() != nullptr, 1000)
+ EXPECT_TRUE_WAIT(ch.best_connection() != nullptr, 1000);
EXPECT_TRUE_WAIT(ch.receiving(), 1000);
EXPECT_TRUE_WAIT(!ch.receiving(), 1000);
}
@@ -1932,7 +2050,8 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) {
// The controlled side will select a connection as the "best connection" based
// on requests from an unknown address before the controlling side nominates
// a connection, and will nominate a connection from an unknown address if the
-// request contains the use_candidate attribute.
+// request contains the use_candidate attribute. Plus, it will also sends back
+// a ping response and set the ICE pwd in the remote candidate appropriately.
TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) {
cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
cricket::P2PTransportChannel ch("receiving state change", 1, nullptr, &pa);
@@ -1948,14 +2067,16 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) {
uint32_t prflx_priority = cricket::ICE_TYPE_PREFERENCE_PRFLX << 24;
request.AddAttribute(new cricket::StunUInt32Attribute(
cricket::STUN_ATTR_PRIORITY, prflx_priority));
- cricket::Port* port = GetPort(&ch);
+ cricket::TestUDPPort* port = static_cast<cricket::TestUDPPort*>(GetPort(&ch));
port->SignalUnknownAddress(port, rtc::SocketAddress("1.1.1.1", 1),
cricket::PROTO_UDP, &request, kIceUfrag[1], false);
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
ASSERT_TRUE(conn1 != nullptr);
+ EXPECT_TRUE(port->sent_binding_response());
EXPECT_EQ(conn1, ch.best_connection());
conn1->ReceivedPingResponse();
EXPECT_EQ(conn1, ch.best_connection());
+ port->set_sent_binding_response(false);
// Another connection is nominated via use_candidate.
ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 1));
@@ -1977,8 +2098,10 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) {
cricket::PROTO_UDP, &request, kIceUfrag[1], false);
cricket::Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3);
ASSERT_TRUE(conn3 != nullptr);
+ EXPECT_TRUE(port->sent_binding_response());
conn3->ReceivedPingResponse(); // Become writable.
EXPECT_EQ(conn2, ch.best_connection());
+ port->set_sent_binding_response(false);
// However if the request contains use_candidate attribute, it will be
// selected as the best connection.
@@ -1988,10 +2111,23 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) {
cricket::PROTO_UDP, &request, kIceUfrag[1], false);
cricket::Connection* conn4 = WaitForConnectionTo(&ch, "4.4.4.4", 4);
ASSERT_TRUE(conn4 != nullptr);
+ EXPECT_TRUE(port->sent_binding_response());
// conn4 is not the best connection yet because it is not writable.
EXPECT_EQ(conn2, ch.best_connection());
conn4->ReceivedPingResponse(); // Become writable.
EXPECT_EQ(conn4, ch.best_connection());
+
+ // Test that the request from an unknown address contains a ufrag from an old
+ // generation.
+ port->set_sent_binding_response(false);
+ ch.SetRemoteIceCredentials(kIceUfrag[2], kIcePwd[2]);
+ ch.SetRemoteIceCredentials(kIceUfrag[3], kIcePwd[3]);
+ port->SignalUnknownAddress(port, rtc::SocketAddress("5.5.5.5", 5),
+ cricket::PROTO_UDP, &request, kIceUfrag[2], false);
+ cricket::Connection* conn5 = WaitForConnectionTo(&ch, "5.5.5.5", 5);
+ ASSERT_TRUE(conn5 != nullptr);
+ EXPECT_TRUE(port->sent_binding_response());
+ EXPECT_EQ(kIcePwd[2], conn5->remote_candidate().password());
}
// The controlled side will select a connection as the "best connection"
@@ -2114,7 +2250,9 @@ TEST_F(P2PTransportChannelPingTest, TestGetState) {
EXPECT_TRUE_WAIT(conn2->pruned(), 1000);
EXPECT_EQ(cricket::TransportChannelState::STATE_COMPLETED, ch.GetState());
conn1->Prune(); // All connections are pruned.
- EXPECT_EQ(cricket::TransportChannelState::STATE_FAILED, ch.GetState());
+ // Need to wait until the channel state is updated.
+ EXPECT_EQ_WAIT(cricket::TransportChannelState::STATE_FAILED, ch.GetState(),
+ 1000);
}
// Test that when a low-priority connection is pruned, it is not deleted
@@ -2190,3 +2328,31 @@ TEST_F(P2PTransportChannelPingTest, TestDeleteConnectionsIfAllWriteTimedout) {
conn3->Prune();
EXPECT_TRUE_WAIT(ch.connections().empty(), 1000);
}
+
+// Test that after a port allocator session is started, it will be stopped
+// when a new connection becomes writable and receiving. Also test that this
+// holds even if the transport channel did not lose the writability.
+TEST_F(P2PTransportChannelPingTest, TestStopPortAllocatorSessions) {
+ cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
+ cricket::P2PTransportChannel ch("test channel", 1, nullptr, &pa);
+ PrepareChannel(&ch);
+ ch.SetIceConfig(CreateIceConfig(2000, false));
+ ch.Connect();
+ ch.MaybeStartGathering();
+ ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 100));
+ cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
+ ASSERT_TRUE(conn1 != nullptr);
+ conn1->ReceivedPingResponse(); // Becomes writable and receiving
+ EXPECT_TRUE(!ch.allocator_session()->IsGettingPorts());
+
+ // Restart gathering even if the transport channel is still writable.
+ // It should stop getting ports after a new connection becomes strongly
+ // connected.
+ ch.SetIceCredentials(kIceUfrag[1], kIcePwd[1]);
+ ch.MaybeStartGathering();
+ ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 100));
+ cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
+ ASSERT_TRUE(conn2 != nullptr);
+ conn2->ReceivedPingResponse(); // Becomes writable and receiving
+ EXPECT_TRUE(!ch.allocator_session()->IsGettingPorts());
+}
diff --git a/webrtc/p2p/base/port.cc b/webrtc/p2p/base/port.cc
index d34b05f8e9..9dd5c83fed 100644
--- a/webrtc/p2p/base/port.cc
+++ b/webrtc/p2p/base/port.cc
@@ -310,10 +310,6 @@ void Port::OnReadPacket(
}
}
-void Port::OnSentPacket(const rtc::SentPacket& sent_packet) {
- PortInterface::SignalSentPacket(this, sent_packet);
-}
-
void Port::OnReadyToSend() {
AddressMap::iterator iter = connections_.begin();
for (; iter != connections_.end(); ++iter) {
@@ -567,10 +563,6 @@ void Port::SendBindingResponse(StunMessage* request,
response.AddMessageIntegrity(password_);
response.AddFingerprint();
- // The fact that we received a successful request means that this connection
- // (if one exists) should now be receiving.
- Connection* conn = GetConnection(addr);
-
// Send the response message.
rtc::ByteBuffer buf;
response.Write(&buf);
@@ -585,6 +577,7 @@ void Port::SendBindingResponse(StunMessage* request,
} else {
// Log at LS_INFO if we send a stun ping response on an unwritable
// connection.
+ Connection* conn = GetConnection(addr);
rtc::LoggingSeverity sev = (conn && !conn->writable()) ?
rtc::LS_INFO : rtc::LS_VERBOSE;
LOG_JV(sev, this)
@@ -592,10 +585,6 @@ void Port::SendBindingResponse(StunMessage* request,
<< ", to=" << addr.ToSensitiveString()
<< ", id=" << rtc::hex_encode(response.transaction_id());
}
-
- ASSERT(conn != NULL);
- if (conn)
- conn->ReceivedPing();
}
void Port::SendBindingErrorResponse(StunMessage* request,
@@ -924,29 +913,7 @@ void Connection::OnReadPacket(
<< ", id=" << rtc::hex_encode(msg->transaction_id());
if (remote_ufrag == remote_candidate_.username()) {
- // Check for role conflicts.
- if (!port_->MaybeIceRoleConflict(addr, msg.get(), remote_ufrag)) {
- // Received conflicting role from the peer.
- LOG(LS_INFO) << "Received conflicting role from the peer.";
- return;
- }
-
- // Incoming, validated stun request from remote peer.
- // This call will also set the connection receiving.
- port_->SendBindingResponse(msg.get(), addr);
-
- // If timed out sending writability checks, start up again
- if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT))
- set_write_state(STATE_WRITE_INIT);
-
- if (port_->GetIceRole() == ICEROLE_CONTROLLED) {
- const StunByteStringAttribute* use_candidate_attr =
- msg->GetByteString(STUN_ATTR_USE_CANDIDATE);
- if (use_candidate_attr) {
- set_nominated(true);
- SignalNominated(this);
- }
- }
+ HandleBindingRequest(msg.get());
} else {
// The packet had the right local username, but the remote username
// was not the right one for the remote address.
@@ -986,6 +953,37 @@ void Connection::OnReadPacket(
}
}
+void Connection::HandleBindingRequest(IceMessage* msg) {
+ // This connection should now be receiving.
+ ReceivedPing();
+
+ const rtc::SocketAddress& remote_addr = remote_candidate_.address();
+ const std::string& remote_ufrag = remote_candidate_.username();
+ // Check for role conflicts.
+ if (!port_->MaybeIceRoleConflict(remote_addr, msg, remote_ufrag)) {
+ // Received conflicting role from the peer.
+ LOG(LS_INFO) << "Received conflicting role from the peer.";
+ return;
+ }
+
+ // This is a validated stun request from remote peer.
+ port_->SendBindingResponse(msg, remote_addr);
+
+ // If it timed out on writing check, start up again
+ if (!pruned_ && write_state_ == STATE_WRITE_TIMEOUT) {
+ set_write_state(STATE_WRITE_INIT);
+ }
+
+ if (port_->GetIceRole() == ICEROLE_CONTROLLED) {
+ const StunByteStringAttribute* use_candidate_attr =
+ msg->GetByteString(STUN_ATTR_USE_CANDIDATE);
+ if (use_candidate_attr) {
+ set_nominated(true);
+ SignalNominated(this);
+ }
+ }
+}
+
void Connection::OnReadyToSend() {
if (write_state_ == STATE_WRITABLE) {
SignalReadyToSend(this);
@@ -1006,6 +1004,11 @@ void Connection::Destroy() {
port_->thread()->Post(this, MSG_DELETE);
}
+void Connection::FailAndDestroy() {
+ set_state(Connection::STATE_FAILED);
+ Destroy();
+}
+
void Connection::PrintPingsSinceLastResponse(std::string* s, size_t max) {
std::ostringstream oss;
oss << std::boolalpha;
@@ -1117,25 +1120,28 @@ void Connection::ReceivedPingResponse() {
}
bool Connection::dead(uint32_t now) const {
- if (now < (time_created_ms_ + MIN_CONNECTION_LIFETIME)) {
- // A connection that hasn't passed its minimum lifetime is still alive.
- // We do this to prevent connections from being pruned too quickly
- // during a network change event when two networks would be up
- // simultaneously but only for a brief period.
- return false;
+ if (last_received() > 0) {
+ // If it has ever received anything, we keep it alive until it hasn't
+ // received anything for DEAD_CONNECTION_RECEIVE_TIMEOUT. This covers the
+ // normal case of a successfully used connection that stops working. This
+ // also allows a remote peer to continue pinging over a locally inactive
+ // (pruned) connection.
+ return (now > (last_received() + DEAD_CONNECTION_RECEIVE_TIMEOUT));
}
- if (receiving_) {
- // A connection that is receiving is alive.
+ if (active()) {
+ // If it has never received anything, keep it alive as long as it is
+ // actively pinging and not pruned. Otherwise, the connection might be
+ // deleted before it has a chance to ping. This is the normal case for a
+ // new connection that is pinging but hasn't received anything yet.
return false;
}
- // A connection is alive until it is inactive.
- return !active();
-
- // TODO(honghaiz): Move from using the write state to using the receiving
- // state with something like the following:
- // return (now > (last_received() + DEAD_CONNECTION_RECEIVE_TIMEOUT));
+ // If it has never received anything and is not actively pinging (pruned), we
+ // keep it around for at least MIN_CONNECTION_LIFETIME to prevent connections
+ // from being pruned too quickly during a network change event when two
+ // networks would be up simultaneously but only for a brief period.
+ return now > (time_created_ms_ + MIN_CONNECTION_LIFETIME);
}
std::string Connection::ToDebugId() const {
@@ -1248,8 +1254,7 @@ void Connection::OnConnectionRequestErrorResponse(ConnectionRequest* request,
// This is not a valid connection.
LOG_J(LS_ERROR, this) << "Received STUN error response, code="
<< error_code << "; killing connection";
- set_state(STATE_FAILED);
- Destroy();
+ FailAndDestroy();
}
}
@@ -1302,13 +1307,13 @@ void Connection::OnMessage(rtc::Message *pmsg) {
delete this;
}
-uint32_t Connection::last_received() {
+uint32_t Connection::last_received() const {
return std::max(last_data_received_,
std::max(last_ping_received_, last_ping_response_received_));
}
size_t Connection::recv_bytes_second() {
- return recv_rate_tracker_.ComputeRate();
+ return round(recv_rate_tracker_.ComputeRate());
}
size_t Connection::recv_total_bytes() {
@@ -1316,7 +1321,7 @@ size_t Connection::recv_total_bytes() {
}
size_t Connection::sent_bytes_second() {
- return send_rate_tracker_.ComputeRate();
+ return round(send_rate_tracker_.ComputeRate());
}
size_t Connection::sent_total_bytes() {
@@ -1396,10 +1401,10 @@ void Connection::MaybeAddPrflxCandidate(ConnectionRequest* request,
SignalStateChange(this);
}
-ProxyConnection::ProxyConnection(Port* port, size_t index,
- const Candidate& candidate)
- : Connection(port, index, candidate), error_(0) {
-}
+ProxyConnection::ProxyConnection(Port* port,
+ size_t index,
+ const Candidate& remote_candidate)
+ : Connection(port, index, remote_candidate) {}
int ProxyConnection::Send(const void* data, size_t size,
const rtc::PacketOptions& options) {
diff --git a/webrtc/p2p/base/port.h b/webrtc/p2p/base/port.h
index 01c45f26d8..436b1e7faa 100644
--- a/webrtc/p2p/base/port.h
+++ b/webrtc/p2p/base/port.h
@@ -54,6 +54,10 @@ extern const char TCPTYPE_SIMOPEN_STR[];
// it.
const uint32_t MIN_CONNECTION_LIFETIME = 10 * 1000; // 10 seconds.
+// A connection will be declared dead if it has not received anything for this
+// long.
+const uint32_t DEAD_CONNECTION_RECEIVE_TIMEOUT = 30 * 1000; // 30 seconds.
+
// The timeout duration when a connection does not receive anything.
const uint32_t WEAK_CONNECTION_RECEIVE_TIMEOUT = 2500; // 2.5 seconds
@@ -276,7 +280,11 @@ class Port : public PortInterface, public rtc::MessageHandler,
const std::string& remote_ufrag);
// Called when a packet has been sent to the socket.
- void OnSentPacket(const rtc::SentPacket& sent_packet);
+ // This is made pure virtual to notify subclasses of Port that they MUST
+ // listen to AsyncPacketSocket::SignalSentPacket and then call
+ // PortInterface::OnSentPacket.
+ virtual void OnSentPacket(rtc::AsyncPacketSocket* socket,
+ const rtc::SentPacket& sent_packet) = 0;
// Called when the socket is currently able to send.
void OnReadyToSend();
@@ -442,7 +450,6 @@ class Connection : public rtc::MessageHandler,
bool connected() const { return connected_; }
bool weak() const { return !(writable() && receiving() && connected()); }
bool active() const {
- // TODO(honghaiz): Move from using |write_state_| to using |pruned_|.
return write_state_ != STATE_WRITE_TIMEOUT;
}
// A connection is dead if it can be safely deleted.
@@ -510,6 +517,9 @@ class Connection : public rtc::MessageHandler,
// Makes the connection go away.
void Destroy();
+ // Makes the connection go away, in a failed state.
+ void FailAndDestroy();
+
// Checks that the state of this connection is up-to-date. The argument is
// the current time, which is compared against various timeouts.
void UpdateState(uint32_t now);
@@ -518,11 +528,16 @@ class Connection : public rtc::MessageHandler,
uint32_t last_ping_sent() const { return last_ping_sent_; }
void Ping(uint32_t now);
void ReceivedPingResponse();
+ uint32_t last_ping_response_received() const {
+ return last_ping_response_received_;
+ }
// Called whenever a valid ping is received on this connection. This is
// public because the connection intercepts the first ping for us.
uint32_t last_ping_received() const { return last_ping_received_; }
void ReceivedPing();
+ // Handles the binding request; sends a response if this is a valid request.
+ void HandleBindingRequest(IceMessage* msg);
// Debugging description of this connection
std::string ToDebugId() const;
@@ -557,7 +572,7 @@ class Connection : public rtc::MessageHandler,
// Returns the last received time of any data, stun request, or stun
// response in milliseconds
- uint32_t last_received();
+ uint32_t last_received() const;
protected:
enum { MSG_DELETE = 0, MSG_FIRST_AVAILABLE };
@@ -628,17 +643,18 @@ class Connection : public rtc::MessageHandler,
friend class ConnectionRequest;
};
-// ProxyConnection defers all the interesting work to the port
+// ProxyConnection defers all the interesting work to the port.
class ProxyConnection : public Connection {
public:
- ProxyConnection(Port* port, size_t index, const Candidate& candidate);
+ ProxyConnection(Port* port, size_t index, const Candidate& remote_candidate);
- virtual int Send(const void* data, size_t size,
- const rtc::PacketOptions& options);
- virtual int GetError() { return error_; }
+ int Send(const void* data,
+ size_t size,
+ const rtc::PacketOptions& options) override;
+ int GetError() override { return error_; }
private:
- int error_;
+ int error_ = 0;
};
} // namespace cricket
diff --git a/webrtc/p2p/base/port_unittest.cc b/webrtc/p2p/base/port_unittest.cc
index 4a4ed32456..449021ad9f 100644
--- a/webrtc/p2p/base/port_unittest.cc
+++ b/webrtc/p2p/base/port_unittest.cc
@@ -17,6 +17,7 @@
#include "webrtc/p2p/base/testturnserver.h"
#include "webrtc/p2p/base/transport.h"
#include "webrtc/p2p/base/turnport.h"
+#include "webrtc/base/arraysize.h"
#include "webrtc/base/crc32.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/helpers.h"
@@ -140,6 +141,10 @@ class TestPort : public Port {
ICE_TYPE_PREFERENCE_HOST, 0, true);
}
+ virtual bool SupportsProtocol(const std::string& protocol) const {
+ return true;
+ }
+
// Exposed for testing candidate building.
void AddCandidateAddress(const rtc::SocketAddress& addr) {
AddAddress(addr, addr, rtc::SocketAddress(), "udp", "", "", Type(),
@@ -199,9 +204,13 @@ class TestPort : public Port {
}
private:
+ void OnSentPacket(rtc::AsyncPacketSocket* socket,
+ const rtc::SentPacket& sent_packet) {
+ PortInterface::SignalSentPacket(sent_packet);
+ }
rtc::scoped_ptr<ByteBuffer> last_stun_buf_;
rtc::scoped_ptr<IceMessage> last_stun_msg_;
- int type_preference_;
+ int type_preference_ = 0;
};
class TestChannel : public sigslot::has_slots<> {
@@ -456,9 +465,8 @@ class PortTest : public testing::Test, public sigslot::has_slots<> {
}
UDPPort* CreateUdpPort(const SocketAddress& addr,
PacketSocketFactory* socket_factory) {
- return UDPPort::Create(main_, socket_factory, &network_,
- addr.ipaddr(), 0, 0, username_, password_,
- std::string(), false);
+ return UDPPort::Create(main_, socket_factory, &network_, addr.ipaddr(), 0,
+ 0, username_, password_, std::string(), true);
}
TCPPort* CreateTcpPort(const SocketAddress& addr) {
return CreateTcpPort(addr, &socket_factory_);
@@ -1235,6 +1243,58 @@ TEST_F(PortTest, TestSslTcpToSslTcpRelay) {
}
*/
+// Test that a connection will be dead and deleted if
+// i) it has never received anything for MIN_CONNECTION_LIFETIME milliseconds
+// since it was created, or
+// ii) it has not received anything for DEAD_CONNECTION_RECEIVE_TIMEOUT
+// milliseconds since last receiving.
+TEST_F(PortTest, TestConnectionDead) {
+ UDPPort* port1 = CreateUdpPort(kLocalAddr1);
+ UDPPort* port2 = CreateUdpPort(kLocalAddr2);
+ TestChannel ch1(port1);
+ TestChannel ch2(port2);
+ // Acquire address.
+ ch1.Start();
+ ch2.Start();
+ ASSERT_EQ_WAIT(1, ch1.complete_count(), kTimeout);
+ ASSERT_EQ_WAIT(1, ch2.complete_count(), kTimeout);
+
+ // Test case that the connection has never received anything.
+ uint32_t before_created = rtc::Time();
+ ch1.CreateConnection(GetCandidate(port2));
+ uint32_t after_created = rtc::Time();
+ Connection* conn = ch1.conn();
+ ASSERT(conn != nullptr);
+ // It is not dead if it is after MIN_CONNECTION_LIFETIME but not pruned.
+ conn->UpdateState(after_created + MIN_CONNECTION_LIFETIME + 1);
+ rtc::Thread::Current()->ProcessMessages(0);
+ EXPECT_TRUE(ch1.conn() != nullptr);
+ // It is not dead if it is before MIN_CONNECTION_LIFETIME and pruned.
+ conn->UpdateState(before_created + MIN_CONNECTION_LIFETIME - 1);
+ conn->Prune();
+ rtc::Thread::Current()->ProcessMessages(0);
+ EXPECT_TRUE(ch1.conn() != nullptr);
+ // It will be dead after MIN_CONNECTION_LIFETIME and pruned.
+ conn->UpdateState(after_created + MIN_CONNECTION_LIFETIME + 1);
+ EXPECT_TRUE_WAIT(ch1.conn() == nullptr, kTimeout);
+
+ // Test case that the connection has received something.
+ // Create a connection again and receive a ping.
+ ch1.CreateConnection(GetCandidate(port2));
+ conn = ch1.conn();
+ ASSERT(conn != nullptr);
+ uint32_t before_last_receiving = rtc::Time();
+ conn->ReceivedPing();
+ uint32_t after_last_receiving = rtc::Time();
+ // The connection will be dead after DEAD_CONNECTION_RECEIVE_TIMEOUT
+ conn->UpdateState(
+ before_last_receiving + DEAD_CONNECTION_RECEIVE_TIMEOUT - 1);
+ rtc::Thread::Current()->ProcessMessages(100);
+ EXPECT_TRUE(ch1.conn() != nullptr);
+ conn->UpdateState(after_last_receiving + DEAD_CONNECTION_RECEIVE_TIMEOUT + 1);
+ EXPECT_TRUE_WAIT(ch1.conn() == nullptr, kTimeout);
+}
+
// This test case verifies standard ICE features in STUN messages. Currently it
// verifies Message Integrity attribute in STUN messages and username in STUN
// binding request will have colon (":") between remote and local username.
@@ -2224,7 +2284,7 @@ TEST_F(PortTest, TestWritableState) {
// Data should be unsendable until the connection is accepted.
char data[] = "abcd";
- int data_size = ARRAY_SIZE(data);
+ int data_size = arraysize(data);
rtc::PacketOptions options;
EXPECT_EQ(SOCKET_ERROR, ch1.conn()->Send(data, data_size, options));
@@ -2450,3 +2510,24 @@ TEST_F(PortTest, TestControlledToControllingNotDestroyed) {
rtc::Thread::Current()->ProcessMessages(kTimeout);
EXPECT_FALSE(destroyed());
}
+
+TEST_F(PortTest, TestSupportsProtocol) {
+ rtc::scoped_ptr<Port> udp_port(CreateUdpPort(kLocalAddr1));
+ EXPECT_TRUE(udp_port->SupportsProtocol(UDP_PROTOCOL_NAME));
+ EXPECT_FALSE(udp_port->SupportsProtocol(TCP_PROTOCOL_NAME));
+
+ rtc::scoped_ptr<Port> stun_port(
+ CreateStunPort(kLocalAddr1, nat_socket_factory1()));
+ EXPECT_TRUE(stun_port->SupportsProtocol(UDP_PROTOCOL_NAME));
+ EXPECT_FALSE(stun_port->SupportsProtocol(TCP_PROTOCOL_NAME));
+
+ rtc::scoped_ptr<Port> tcp_port(CreateTcpPort(kLocalAddr1));
+ EXPECT_TRUE(tcp_port->SupportsProtocol(TCP_PROTOCOL_NAME));
+ EXPECT_TRUE(tcp_port->SupportsProtocol(SSLTCP_PROTOCOL_NAME));
+ EXPECT_FALSE(tcp_port->SupportsProtocol(UDP_PROTOCOL_NAME));
+
+ rtc::scoped_ptr<Port> turn_port(
+ CreateTurnPort(kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP));
+ EXPECT_TRUE(turn_port->SupportsProtocol(UDP_PROTOCOL_NAME));
+ EXPECT_FALSE(turn_port->SupportsProtocol(TCP_PROTOCOL_NAME));
+}
diff --git a/webrtc/p2p/base/portallocator.h b/webrtc/p2p/base/portallocator.h
index 4f8ec2fbe6..6fb79b065e 100644
--- a/webrtc/p2p/base/portallocator.h
+++ b/webrtc/p2p/base/portallocator.h
@@ -14,6 +14,7 @@
#include <string>
#include <vector>
+#include "webrtc/p2p/base/port.h"
#include "webrtc/p2p/base/portinterface.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/proxyinfo.h"
@@ -46,10 +47,14 @@ enum {
PORTALLOCATOR_ENABLE_SHARED_UFRAG = 0x80,
PORTALLOCATOR_ENABLE_SHARED_SOCKET = 0x100,
PORTALLOCATOR_ENABLE_STUN_RETRANSMIT_ATTRIBUTE = 0x200,
+ // When specified, we'll only allocate the STUN candidate for the public
+ // interface as seen by regular http traffic and the HOST candidate associated
+ // with the default local interface.
PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION = 0x400,
- // When specified, a loopback candidate will be generated if
- // PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION is specified.
- PORTALLOCATOR_ENABLE_LOCALHOST_CANDIDATE = 0x800,
+ // When specified along with PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION, the
+ // default local candidate mentioned above will not be allocated. Only the
+ // STUN candidate will be.
+ PORTALLOCATOR_DISABLE_DEFAULT_LOCAL_CANDIDATE = 0x800,
// Disallow use of UDP when connecting to a relay server. Since proxy servers
// usually don't handle UDP, using UDP will leak the IP address.
PORTALLOCATOR_DISABLE_UDP_RELAY = 0x1000,
@@ -71,6 +76,38 @@ enum {
CF_ALL = 0x7,
};
+// TODO(deadbeef): Rename to TurnCredentials (and username to ufrag).
+struct RelayCredentials {
+ RelayCredentials() {}
+ RelayCredentials(const std::string& username, const std::string& password)
+ : username(username), password(password) {}
+
+ std::string username;
+ std::string password;
+};
+
+typedef std::vector<ProtocolAddress> PortList;
+// TODO(deadbeef): Rename to TurnServerConfig.
+struct RelayServerConfig {
+ RelayServerConfig(RelayType type) : type(type), priority(0) {}
+
+ RelayServerConfig(const std::string& address,
+ int port,
+ const std::string& username,
+ const std::string& password,
+ ProtocolType proto,
+ bool secure)
+ : type(RELAY_TURN), credentials(username, password) {
+ ports.push_back(
+ ProtocolAddress(rtc::SocketAddress(address, port), proto, secure));
+ }
+
+ RelayType type;
+ PortList ports;
+ RelayCredentials credentials;
+ int priority;
+};
+
class PortAllocatorSession : public sigslot::has_slots<> {
public:
// Content name passed in mostly for logging and debugging.
@@ -137,6 +174,18 @@ class PortAllocator : public sigslot::has_slots<> {
}
virtual ~PortAllocator() {}
+ // Set STUN and TURN servers to be used in future sessions.
+ virtual void SetIceServers(
+ const ServerAddresses& stun_servers,
+ const std::vector<RelayServerConfig>& turn_servers) = 0;
+
+ // Sets the network types to ignore.
+ // Values are defined by the AdapterType enum.
+ // For instance, calling this with
+ // ADAPTER_TYPE_ETHERNET | ADAPTER_TYPE_LOOPBACK will ignore Ethernet and
+ // loopback interfaces.
+ virtual void SetNetworkIgnoreMask(int network_ignore_mask) = 0;
+
PortAllocatorSession* CreateSession(
const std::string& sid,
const std::string& content_name,
diff --git a/webrtc/p2p/base/portinterface.h b/webrtc/p2p/base/portinterface.h
index 0f77036ac1..e83879f3b7 100644
--- a/webrtc/p2p/base/portinterface.h
+++ b/webrtc/p2p/base/portinterface.h
@@ -53,6 +53,8 @@ class PortInterface {
virtual bool SharedSocket() const = 0;
+ virtual bool SupportsProtocol(const std::string& protocol) const = 0;
+
// PrepareAddress will attempt to get an address for this port that other
// clients can send to. It may take some time before the address is ready.
// Once it is ready, we will send SignalAddressReady. If errors are
@@ -114,7 +116,7 @@ class PortInterface {
const rtc::SocketAddress&> SignalReadPacket;
// Emitted each time a packet is sent on this port.
- sigslot::signal2<PortInterface*, const rtc::SentPacket&> SignalSentPacket;
+ sigslot::signal1<const rtc::SentPacket&> SignalSentPacket;
virtual std::string ToString() const = 0;
diff --git a/webrtc/p2p/base/pseudotcp.cc b/webrtc/p2p/base/pseudotcp.cc
index 5f035ca652..6281315dc1 100644
--- a/webrtc/p2p/base/pseudotcp.cc
+++ b/webrtc/p2p/base/pseudotcp.cc
@@ -16,6 +16,7 @@
#include <algorithm>
#include <set>
+#include "webrtc/base/arraysize.h"
#include "webrtc/base/basictypes.h"
#include "webrtc/base/bytebuffer.h"
#include "webrtc/base/byteorder.h"
@@ -187,7 +188,7 @@ void ReportStats() {
char buffer[256];
size_t len = 0;
for (int i = 0; i < S_NUM_STATS; ++i) {
- len += rtc::sprintfn(buffer, ARRAY_SIZE(buffer), "%s%s:%d",
+ len += rtc::sprintfn(buffer, arraysize(buffer), "%s%s:%d",
(i == 0) ? "" : ",", STAT_NAMES[i], g_stats[i]);
g_stats[i] = 0;
}
diff --git a/webrtc/p2p/base/relayport.cc b/webrtc/p2p/base/relayport.cc
index 88adcf2f88..19883a3121 100644
--- a/webrtc/p2p/base/relayport.cc
+++ b/webrtc/p2p/base/relayport.cc
@@ -754,7 +754,7 @@ void RelayEntry::OnReadPacket(
void RelayEntry::OnSentPacket(rtc::AsyncPacketSocket* socket,
const rtc::SentPacket& sent_packet) {
- port_->OnSentPacket(sent_packet);
+ port_->OnSentPacket(socket, sent_packet);
}
void RelayEntry::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
diff --git a/webrtc/p2p/base/relayport.h b/webrtc/p2p/base/relayport.h
index 8452b5b430..402736c34d 100644
--- a/webrtc/p2p/base/relayport.h
+++ b/webrtc/p2p/base/relayport.h
@@ -29,7 +29,7 @@ class RelayConnection;
// is created. The RelayEntry will try to reach the remote destination
// by connecting to all available server addresses in a pre defined
// order with a small delay in between. When a connection is
-// successful all other connection attemts are aborted.
+// successful all other connection attempts are aborted.
class RelayPort : public Port {
public:
typedef std::pair<rtc::Socket::Option, int> OptionValue;
@@ -46,7 +46,7 @@ class RelayPort : public Port {
return new RelayPort(thread, factory, network, ip, min_port, max_port,
username, password);
}
- virtual ~RelayPort();
+ ~RelayPort() override;
void AddServerAddress(const ProtocolAddress& addr);
void AddExternalAddress(const ProtocolAddress& addr);
@@ -54,12 +54,16 @@ class RelayPort : public Port {
const std::vector<OptionValue>& options() const { return options_; }
bool HasMagicCookie(const char* data, size_t size);
- virtual void PrepareAddress();
- virtual Connection* CreateConnection(const Candidate& address,
- CandidateOrigin origin);
- virtual int SetOption(rtc::Socket::Option opt, int value);
- virtual int GetOption(rtc::Socket::Option opt, int* value);
- virtual int GetError();
+ void PrepareAddress() override;
+ Connection* CreateConnection(const Candidate& address,
+ CandidateOrigin origin) override;
+ int SetOption(rtc::Socket::Option opt, int value) override;
+ int GetOption(rtc::Socket::Option opt, int* value) override;
+ int GetError() override;
+ bool SupportsProtocol(const std::string& protocol) const override {
+ // Relay port may create both TCP and UDP connections.
+ return true;
+ }
const ProtocolAddress * ServerAddress(size_t index) const;
bool IsReady() { return ready_; }
@@ -81,10 +85,11 @@ class RelayPort : public Port {
void SetReady();
- virtual int SendTo(const void* data, size_t size,
- const rtc::SocketAddress& addr,
- const rtc::PacketOptions& options,
- bool payload);
+ int SendTo(const void* data,
+ size_t size,
+ const rtc::SocketAddress& addr,
+ const rtc::PacketOptions& options,
+ bool payload) override;
// Dispatches the given packet to the port or connection as appropriate.
void OnReadPacket(const char* data, size_t size,
@@ -92,6 +97,11 @@ class RelayPort : public Port {
ProtocolType proto,
const rtc::PacketTime& packet_time);
+ // The OnSentPacket callback is left empty here since they are handled by
+ // RelayEntry.
+ void OnSentPacket(rtc::AsyncPacketSocket* socket,
+ const rtc::SentPacket& sent_packet) override {}
+
private:
friend class RelayEntry;
diff --git a/webrtc/p2p/base/stun_unittest.cc b/webrtc/p2p/base/stun_unittest.cc
index cd4f7e1cbb..12492570c4 100644
--- a/webrtc/p2p/base/stun_unittest.cc
+++ b/webrtc/p2p/base/stun_unittest.cc
@@ -11,6 +11,7 @@
#include <string>
#include "webrtc/p2p/base/stun.h"
+#include "webrtc/base/arraysize.h"
#include "webrtc/base/bytebuffer.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/logging.h"
@@ -515,11 +516,11 @@ TEST_F(StunTest, MessageTypes) {
STUN_BINDING_REQUEST, STUN_BINDING_INDICATION,
STUN_BINDING_RESPONSE, STUN_BINDING_ERROR_RESPONSE
};
- for (int i = 0; i < ARRAY_SIZE(types); ++i) {
- EXPECT_EQ(i == 0, IsStunRequestType(types[i]));
- EXPECT_EQ(i == 1, IsStunIndicationType(types[i]));
- EXPECT_EQ(i == 2, IsStunSuccessResponseType(types[i]));
- EXPECT_EQ(i == 3, IsStunErrorResponseType(types[i]));
+ for (size_t i = 0; i < arraysize(types); ++i) {
+ EXPECT_EQ(i == 0U, IsStunRequestType(types[i]));
+ EXPECT_EQ(i == 1U, IsStunIndicationType(types[i]));
+ EXPECT_EQ(i == 2U, IsStunSuccessResponseType(types[i]));
+ EXPECT_EQ(i == 3U, IsStunErrorResponseType(types[i]));
EXPECT_EQ(1, types[i] & 0xFEEF);
}
}
diff --git a/webrtc/p2p/base/stunport.cc b/webrtc/p2p/base/stunport.cc
index 1598fe43ce..8f37dd5218 100644
--- a/webrtc/p2p/base/stunport.cc
+++ b/webrtc/p2p/base/stunport.cc
@@ -13,6 +13,7 @@
#include "webrtc/p2p/base/common.h"
#include "webrtc/p2p/base/portallocator.h"
#include "webrtc/p2p/base/stun.h"
+#include "webrtc/base/checks.h"
#include "webrtc/base/common.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/ipaddress.h"
@@ -23,15 +24,19 @@ namespace cricket {
// TODO: Move these to a common place (used in relayport too)
const int KEEPALIVE_DELAY = 10 * 1000; // 10 seconds - sort timeouts
-const int RETRY_DELAY = 50; // 50ms, from ICE spec
const int RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs
+// Stop sending STUN binding requests after this amount of time
+// (in milliseconds) because the connection binding requests should keep
+// the NAT binding alive.
+const int KEEP_ALIVE_TIMEOUT = 2 * 60 * 1000; // 2 minutes
// Handles a binding request sent to the STUN server.
class StunBindingRequest : public StunRequest {
public:
- StunBindingRequest(UDPPort* port, bool keep_alive,
- const rtc::SocketAddress& addr)
- : port_(port), keep_alive_(keep_alive), server_addr_(addr) {
+ StunBindingRequest(UDPPort* port,
+ const rtc::SocketAddress& addr,
+ uint32_t deadline)
+ : port_(port), server_addr_(addr), deadline_(deadline) {
start_time_ = rtc::Time();
}
@@ -58,10 +63,10 @@ class StunBindingRequest : public StunRequest {
}
// We will do a keep-alive regardless of whether this request succeeds.
- // This should have almost no impact on network usage.
- if (keep_alive_) {
+ // It will be stopped after |deadline_| mostly to conserve the battery life.
+ if (rtc::Time() <= deadline_) {
port_->requests_.SendDelayed(
- new StunBindingRequest(port_, true, server_addr_),
+ new StunBindingRequest(port_, server_addr_, deadline_),
port_->stun_keepalive_delay());
}
}
@@ -79,10 +84,10 @@ class StunBindingRequest : public StunRequest {
port_->OnStunBindingOrResolveRequestFailed(server_addr_);
- if (keep_alive_
- && (rtc::TimeSince(start_time_) <= RETRY_TIMEOUT)) {
+ uint32_t now = rtc::Time();
+ if (now <= deadline_ && rtc::TimeDiff(now, start_time_) <= RETRY_TIMEOUT) {
port_->requests_.SendDelayed(
- new StunBindingRequest(port_, true, server_addr_),
+ new StunBindingRequest(port_, server_addr_, deadline_),
port_->stun_keepalive_delay());
}
}
@@ -93,20 +98,13 @@ class StunBindingRequest : public StunRequest {
<< " (" << port_->Network()->name() << ")";
port_->OnStunBindingOrResolveRequestFailed(server_addr_);
-
- if (keep_alive_
- && (rtc::TimeSince(start_time_) <= RETRY_TIMEOUT)) {
- port_->requests_.SendDelayed(
- new StunBindingRequest(port_, true, server_addr_),
- RETRY_DELAY);
- }
}
private:
UDPPort* port_;
- bool keep_alive_;
const rtc::SocketAddress server_addr_;
uint32_t start_time_;
+ uint32_t deadline_;
};
UDPPort::AddressResolver::AddressResolver(
@@ -116,7 +114,10 @@ UDPPort::AddressResolver::AddressResolver(
UDPPort::AddressResolver::~AddressResolver() {
for (ResolverMap::iterator it = resolvers_.begin();
it != resolvers_.end(); ++it) {
- it->second->Destroy(true);
+ // TODO(guoweis): Change to asynchronous DNS resolution to prevent the hang
+ // when passing true to the Destroy() which is a safer way to avoid the code
+ // unloaded before the thread exits. Please see webrtc bug 5139.
+ it->second->Destroy(false);
}
}
@@ -166,15 +167,19 @@ UDPPort::UDPPort(rtc::Thread* thread,
const std::string& username,
const std::string& password,
const std::string& origin,
- bool emit_localhost_for_anyaddress)
- : Port(thread, factory, network, socket->GetLocalAddress().ipaddr(),
- username, password),
+ bool emit_local_for_anyaddress)
+ : Port(thread,
+ factory,
+ network,
+ socket->GetLocalAddress().ipaddr(),
+ username,
+ password),
requests_(thread),
socket_(socket),
error_(0),
ready_(false),
stun_keepalive_delay_(KEEPALIVE_DELAY),
- emit_localhost_for_anyaddress_(emit_localhost_for_anyaddress) {
+ emit_local_for_anyaddress_(emit_local_for_anyaddress) {
requests_.set_origin(origin);
}
@@ -187,7 +192,7 @@ UDPPort::UDPPort(rtc::Thread* thread,
const std::string& username,
const std::string& password,
const std::string& origin,
- bool emit_localhost_for_anyaddress)
+ bool emit_local_for_anyaddress)
: Port(thread,
LOCAL_PORT_TYPE,
factory,
@@ -202,7 +207,7 @@ UDPPort::UDPPort(rtc::Thread* thread,
error_(0),
ready_(false),
stun_keepalive_delay_(KEEPALIVE_DELAY),
- emit_localhost_for_anyaddress_(emit_localhost_for_anyaddress) {
+ emit_local_for_anyaddress_(emit_local_for_anyaddress) {
requests_.set_origin(origin);
}
@@ -248,9 +253,10 @@ void UDPPort::MaybePrepareStunCandidate() {
}
Connection* UDPPort::CreateConnection(const Candidate& address,
- CandidateOrigin origin) {
- if (address.protocol() != "udp")
+ CandidateOrigin origin) {
+ if (!SupportsProtocol(address.protocol())) {
return NULL;
+ }
if (!IsCompatibleAddress(address.address())) {
return NULL;
@@ -294,25 +300,27 @@ int UDPPort::GetError() {
void UDPPort::OnLocalAddressReady(rtc::AsyncPacketSocket* socket,
const rtc::SocketAddress& address) {
// When adapter enumeration is disabled and binding to the any address, the
- // loopback address will be issued as a candidate instead if
- // |emit_localhost_for_anyaddress| is true. This is to allow connectivity on
- // demo pages without STUN/TURN to work.
+ // default local address will be issued as a candidate instead if
+ // |emit_local_for_anyaddress| is true. This is to allow connectivity for
+ // applications which absolutely requires a HOST candidate.
rtc::SocketAddress addr = address;
- if (addr.IsAnyIP() && emit_localhost_for_anyaddress_) {
- addr.SetIP(rtc::GetLoopbackIP(addr.family()));
- }
+
+ // If MaybeSetDefaultLocalAddress fails, we keep the "any" IP so that at
+ // least the port is listening.
+ MaybeSetDefaultLocalAddress(&addr);
AddAddress(addr, addr, rtc::SocketAddress(), UDP_PROTOCOL_NAME, "", "",
LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST, 0, false);
MaybePrepareStunCandidate();
}
-void UDPPort::OnReadPacket(
- rtc::AsyncPacketSocket* socket, const char* data, size_t size,
- const rtc::SocketAddress& remote_addr,
- const rtc::PacketTime& packet_time) {
+void UDPPort::OnReadPacket(rtc::AsyncPacketSocket* socket,
+ const char* data,
+ size_t size,
+ const rtc::SocketAddress& remote_addr,
+ const rtc::PacketTime& packet_time) {
ASSERT(socket == socket_);
- ASSERT(!remote_addr.IsUnresolved());
+ ASSERT(!remote_addr.IsUnresolvedIP());
// Look for a response from the STUN server.
// Even if the response doesn't match one of our outstanding requests, we
@@ -332,7 +340,7 @@ void UDPPort::OnReadPacket(
void UDPPort::OnSentPacket(rtc::AsyncPacketSocket* socket,
const rtc::SentPacket& sent_packet) {
- Port::OnSentPacket(sent_packet);
+ PortInterface::SignalSentPacket(sent_packet);
}
void UDPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
@@ -341,7 +349,7 @@ void UDPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
void UDPPort::SendStunBindingRequests() {
// We will keep pinging the stun server to make sure our NAT pin-hole stays
- // open during the call.
+ // open until the deadline (specified in SendStunBindingRequest).
ASSERT(requests_.empty());
for (ServerAddresses::const_iterator it = server_addresses_.begin();
@@ -356,6 +364,8 @@ void UDPPort::ResolveStunAddress(const rtc::SocketAddress& stun_addr) {
resolver_->SignalDone.connect(this, &UDPPort::OnResolveResult);
}
+ LOG_J(LS_INFO, this) << "Starting STUN host lookup for "
+ << stun_addr.ToSensitiveString();
resolver_->Resolve(stun_addr);
}
@@ -380,15 +390,15 @@ void UDPPort::OnResolveResult(const rtc::SocketAddress& input,
}
}
-void UDPPort::SendStunBindingRequest(
- const rtc::SocketAddress& stun_addr) {
- if (stun_addr.IsUnresolved()) {
+void UDPPort::SendStunBindingRequest(const rtc::SocketAddress& stun_addr) {
+ if (stun_addr.IsUnresolvedIP()) {
ResolveStunAddress(stun_addr);
} else if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) {
// Check if |server_addr_| is compatible with the port's ip.
if (IsCompatibleAddress(stun_addr)) {
- requests_.Send(new StunBindingRequest(this, true, stun_addr));
+ requests_.Send(new StunBindingRequest(this, stun_addr,
+ rtc::Time() + KEEP_ALIVE_TIMEOUT));
} else {
// Since we can't send stun messages to the server, we should mark this
// port ready.
@@ -398,6 +408,23 @@ void UDPPort::SendStunBindingRequest(
}
}
+bool UDPPort::MaybeSetDefaultLocalAddress(rtc::SocketAddress* addr) const {
+ if (!addr->IsAnyIP() || !emit_local_for_anyaddress_ ||
+ !Network()->default_local_address_provider()) {
+ return true;
+ }
+ rtc::IPAddress default_address;
+ bool result =
+ Network()->default_local_address_provider()->GetDefaultLocalAddress(
+ addr->family(), &default_address);
+ if (!result || default_address.IsNil()) {
+ return false;
+ }
+
+ addr->SetIP(default_address);
+ return true;
+}
+
void UDPPort::OnStunBindingRequestSucceeded(
const rtc::SocketAddress& stun_server_addr,
const rtc::SocketAddress& stun_reflected_addr) {
@@ -415,7 +442,9 @@ void UDPPort::OnStunBindingRequestSucceeded(
!HasCandidateWithAddress(stun_reflected_addr)) {
rtc::SocketAddress related_address = socket_->GetLocalAddress();
- if (!(candidate_filter() & CF_HOST)) {
+ // If we can't stamp the related address correctly, empty it to avoid leak.
+ if (!MaybeSetDefaultLocalAddress(&related_address) ||
+ !(candidate_filter() & CF_HOST)) {
// If candidate filter doesn't have CF_HOST specified, empty raddr to
// avoid local address leakage.
related_address = rtc::EmptySocketAddressWithFamily(
diff --git a/webrtc/p2p/base/stunport.h b/webrtc/p2p/base/stunport.h
index 62b23cf074..ecf61a782d 100644
--- a/webrtc/p2p/base/stunport.h
+++ b/webrtc/p2p/base/stunport.h
@@ -35,10 +35,9 @@ class UDPPort : public Port {
const std::string& username,
const std::string& password,
const std::string& origin,
- bool emit_localhost_for_anyaddress) {
- UDPPort* port = new UDPPort(thread, factory, network, socket,
- username, password, origin,
- emit_localhost_for_anyaddress);
+ bool emit_local_for_anyaddress) {
+ UDPPort* port = new UDPPort(thread, factory, network, socket, username,
+ password, origin, emit_local_for_anyaddress);
if (!port->Init()) {
delete port;
port = NULL;
@@ -55,11 +54,10 @@ class UDPPort : public Port {
const std::string& username,
const std::string& password,
const std::string& origin,
- bool emit_localhost_for_anyaddress) {
- UDPPort* port = new UDPPort(thread, factory, network,
- ip, min_port, max_port,
- username, password, origin,
- emit_localhost_for_anyaddress);
+ bool emit_local_for_anyaddress) {
+ UDPPort* port =
+ new UDPPort(thread, factory, network, ip, min_port, max_port, username,
+ password, origin, emit_local_for_anyaddress);
if (!port->Init()) {
delete port;
port = NULL;
@@ -97,6 +95,9 @@ class UDPPort : public Port {
OnReadPacket(socket, data, size, remote_addr, packet_time);
return true;
}
+ virtual bool SupportsProtocol(const std::string& protocol) const {
+ return protocol == UDP_PROTOCOL_NAME;
+ }
void set_stun_keepalive_delay(int delay) {
stun_keepalive_delay_ = delay;
@@ -115,7 +116,7 @@ class UDPPort : public Port {
const std::string& username,
const std::string& password,
const std::string& origin,
- bool emit_localhost_for_anyaddress);
+ bool emit_local_for_anyaddress);
UDPPort(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
@@ -124,7 +125,7 @@ class UDPPort : public Port {
const std::string& username,
const std::string& password,
const std::string& origin,
- bool emit_localhost_for_anyaddress);
+ bool emit_local_for_anyaddress);
bool Init();
@@ -150,6 +151,12 @@ class UDPPort : public Port {
void SendStunBindingRequests();
+ // Helper function which will set |addr|'s IP to the default local address if
+ // |addr| is the "any" address and |emit_local_for_anyaddress_| is true. When
+ // returning false, it indicates that the operation has failed and the
+ // address shouldn't be used by any candidate.
+ bool MaybeSetDefaultLocalAddress(rtc::SocketAddress* addr) const;
+
private:
// A helper class which can be called repeatedly to resolve multiple
// addresses, as opposed to rtc::AsyncResolverInterface, which can only
@@ -211,8 +218,9 @@ class UDPPort : public Port {
bool ready_;
int stun_keepalive_delay_;
- // This is true when PORTALLOCATOR_ENABLE_LOCALHOST_CANDIDATE is specified.
- bool emit_localhost_for_anyaddress_;
+ // This is true by default and false when
+ // PORTALLOCATOR_DISABLE_DEFAULT_LOCAL_CANDIDATE is specified.
+ bool emit_local_for_anyaddress_;
friend class StunBindingRequest;
};
diff --git a/webrtc/p2p/base/stunrequest.cc b/webrtc/p2p/base/stunrequest.cc
index df5614d3cc..ce0364e8db 100644
--- a/webrtc/p2p/base/stunrequest.cc
+++ b/webrtc/p2p/base/stunrequest.cc
@@ -53,6 +53,16 @@ void StunRequestManager::SendDelayed(StunRequest* request, int delay) {
}
}
+void StunRequestManager::Flush(int msg_type) {
+ for (const auto kv : requests_) {
+ StunRequest* request = kv.second;
+ if (msg_type == kAllRequests || msg_type == request->type()) {
+ thread_->Clear(request, MSG_STUN_SEND);
+ thread_->Send(request, MSG_STUN_SEND, NULL);
+ }
+ }
+}
+
void StunRequestManager::Remove(StunRequest* request) {
ASSERT(request->manager() == this);
RequestMap::iterator iter = requests_.find(request->id());
diff --git a/webrtc/p2p/base/stunrequest.h b/webrtc/p2p/base/stunrequest.h
index 267b4a1959..44c1ebff56 100644
--- a/webrtc/p2p/base/stunrequest.h
+++ b/webrtc/p2p/base/stunrequest.h
@@ -21,6 +21,8 @@ namespace cricket {
class StunRequest;
+const int kAllRequests = 0;
+
// Manages a set of STUN requests, sending and resending until we receive a
// response or determine that the request has timed out.
class StunRequestManager {
@@ -32,6 +34,11 @@ class StunRequestManager {
void Send(StunRequest* request);
void SendDelayed(StunRequest* request, int delay);
+ // If |msg_type| is kAllRequests, sends all pending requests right away.
+ // Otherwise, sends those that have a matching type right away.
+ // Only for testing.
+ void Flush(int msg_type);
+
// Removes a stun request that was added previously. This will happen
// automatically when a request succeeds, fails, or times out.
void Remove(StunRequest* request);
diff --git a/webrtc/p2p/base/tcpport.cc b/webrtc/p2p/base/tcpport.cc
index 2590d0aca8..cd3c9192e4 100644
--- a/webrtc/p2p/base/tcpport.cc
+++ b/webrtc/p2p/base/tcpport.cc
@@ -125,9 +125,7 @@ TCPPort::~TCPPort() {
Connection* TCPPort::CreateConnection(const Candidate& address,
CandidateOrigin origin) {
- // We only support TCP protocols
- if ((address.protocol() != TCP_PROTOCOL_NAME) &&
- (address.protocol() != SSLTCP_PROTOCOL_NAME)) {
+ if (!SupportsProtocol(address.protocol())) {
return NULL;
}
@@ -184,10 +182,13 @@ void TCPPort::PrepareAddress() {
} else {
LOG_J(LS_INFO, this) << "Not listening due to firewall restrictions.";
// Note: We still add the address, since otherwise the remote side won't
- // recognize our incoming TCP connections.
- AddAddress(rtc::SocketAddress(ip(), 0), rtc::SocketAddress(ip(), 0),
- rtc::SocketAddress(), TCP_PROTOCOL_NAME, "", TCPTYPE_ACTIVE_STR,
- LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP, 0, true);
+ // recognize our incoming TCP connections. According to
+ // https://tools.ietf.org/html/rfc6544#section-4.5, for active candidate,
+ // the port must be set to the discard port, i.e. 9.
+ AddAddress(rtc::SocketAddress(ip(), DISCARD_PORT),
+ rtc::SocketAddress(ip(), 0), rtc::SocketAddress(),
+ TCP_PROTOCOL_NAME, "", TCPTYPE_ACTIVE_STR, LOCAL_PORT_TYPE,
+ ICE_TYPE_PREFERENCE_HOST_TCP, 0, true);
}
}
@@ -257,6 +258,7 @@ void TCPPort::OnNewConnection(rtc::AsyncPacketSocket* socket,
incoming.socket = new_socket;
incoming.socket->SignalReadPacket.connect(this, &TCPPort::OnReadPacket);
incoming.socket->SignalReadyToSend.connect(this, &TCPPort::OnReadyToSend);
+ incoming.socket->SignalSentPacket.connect(this, &TCPPort::OnSentPacket);
LOG_J(LS_VERBOSE, this) << "Accepted connection from "
<< incoming.addr.ToSensitiveString();
@@ -285,6 +287,11 @@ void TCPPort::OnReadPacket(rtc::AsyncPacketSocket* socket,
Port::OnReadPacket(data, size, remote_addr, PROTO_TCP);
}
+void TCPPort::OnSentPacket(rtc::AsyncPacketSocket* socket,
+ const rtc::SentPacket& sent_packet) {
+ PortInterface::SignalSentPacket(sent_packet);
+}
+
void TCPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
Port::OnReadyToSend();
}
diff --git a/webrtc/p2p/base/tcpport.h b/webrtc/p2p/base/tcpport.h
index a64c5eeab9..cfc6245601 100644
--- a/webrtc/p2p/base/tcpport.h
+++ b/webrtc/p2p/base/tcpport.h
@@ -45,16 +45,19 @@ class TCPPort : public Port {
}
return port;
}
- virtual ~TCPPort();
+ ~TCPPort() override;
- virtual Connection* CreateConnection(const Candidate& address,
- CandidateOrigin origin);
+ Connection* CreateConnection(const Candidate& address,
+ CandidateOrigin origin) override;
- virtual void PrepareAddress();
+ void PrepareAddress() override;
- virtual int GetOption(rtc::Socket::Option opt, int* value);
- virtual int SetOption(rtc::Socket::Option opt, int value);
- virtual int GetError();
+ int GetOption(rtc::Socket::Option opt, int* value) override;
+ int SetOption(rtc::Socket::Option opt, int value) override;
+ int GetError() override;
+ bool SupportsProtocol(const std::string& protocol) const override {
+ return protocol == TCP_PROTOCOL_NAME || protocol == SSLTCP_PROTOCOL_NAME;
+ }
protected:
TCPPort(rtc::Thread* thread,
@@ -69,10 +72,11 @@ class TCPPort : public Port {
bool Init();
// Handles sending using the local TCP socket.
- virtual int SendTo(const void* data, size_t size,
- const rtc::SocketAddress& addr,
- const rtc::PacketOptions& options,
- bool payload);
+ int SendTo(const void* data,
+ size_t size,
+ const rtc::SocketAddress& addr,
+ const rtc::PacketOptions& options,
+ bool payload) override;
// Accepts incoming TCP connection.
void OnNewConnection(rtc::AsyncPacketSocket* socket,
@@ -93,6 +97,9 @@ class TCPPort : public Port {
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time);
+ void OnSentPacket(rtc::AsyncPacketSocket* socket,
+ const rtc::SentPacket& sent_packet) override;
+
void OnReadyToSend(rtc::AsyncPacketSocket* socket);
void OnAddressReady(rtc::AsyncPacketSocket* socket,
@@ -113,15 +120,16 @@ class TCPConnection : public Connection {
// Connection is outgoing unless socket is specified
TCPConnection(TCPPort* port, const Candidate& candidate,
rtc::AsyncPacketSocket* socket = 0);
- virtual ~TCPConnection();
+ ~TCPConnection() override;
- virtual int Send(const void* data, size_t size,
- const rtc::PacketOptions& options);
- virtual int GetError();
+ int Send(const void* data,
+ size_t size,
+ const rtc::PacketOptions& options) override;
+ int GetError() override;
rtc::AsyncPacketSocket* socket() { return socket_.get(); }
- void OnMessage(rtc::Message* pmsg);
+ void OnMessage(rtc::Message* pmsg) override;
// Allow test cases to overwrite the default timeout period.
int reconnection_timeout() const { return reconnection_timeout_; }
@@ -136,8 +144,8 @@ class TCPConnection : public Connection {
// Set waiting_for_stun_binding_complete_ to false to allow data packets in
// addition to what Port::OnConnectionRequestResponse does.
- virtual void OnConnectionRequestResponse(ConnectionRequest* req,
- StunMessage* response);
+ void OnConnectionRequestResponse(ConnectionRequest* req,
+ StunMessage* response) override;
private:
// Helper function to handle the case when Ping or Send fails with error
diff --git a/webrtc/p2p/base/transport.cc b/webrtc/p2p/base/transport.cc
index 2328e4587c..eff10aa0a9 100644
--- a/webrtc/p2p/base/transport.cc
+++ b/webrtc/p2p/base/transport.cc
@@ -305,8 +305,8 @@ bool Transport::GetStats(TransportStats* stats) {
TransportChannelImpl* channel = kv.second;
TransportChannelStats substats;
substats.component = channel->component();
- channel->GetSrtpCryptoSuite(&substats.srtp_cipher);
- channel->GetSslCipherSuite(&substats.ssl_cipher);
+ channel->GetSrtpCryptoSuite(&substats.srtp_crypto_suite);
+ channel->GetSslCipherSuite(&substats.ssl_cipher_suite);
if (!channel->GetStats(&substats.connection_infos)) {
return false;
}
diff --git a/webrtc/p2p/base/transport.h b/webrtc/p2p/base/transport.h
index 955eb42098..6b4b37d4c5 100644
--- a/webrtc/p2p/base/transport.h
+++ b/webrtc/p2p/base/transport.h
@@ -123,8 +123,8 @@ typedef std::vector<ConnectionInfo> ConnectionInfos;
struct TransportChannelStats {
int component = 0;
ConnectionInfos connection_infos;
- std::string srtp_cipher;
- int ssl_cipher = 0;
+ int srtp_crypto_suite = rtc::SRTP_INVALID_CRYPTO_SUITE;
+ int ssl_cipher_suite = rtc::TLS_NULL_WITH_NULL_NULL;
};
// Information about all the channels of a transport.
@@ -140,7 +140,11 @@ struct TransportStats {
// Information about ICE configuration.
struct IceConfig {
// The ICE connection receiving timeout value.
+ // TODO(honghaiz): Remove suffix _ms to be consistent.
int receiving_timeout_ms = -1;
+ // Time interval in milliseconds to ping a backup connection when the ICE
+ // channel is strongly connected.
+ int backup_connection_ping_interval = -1;
// If true, the most recent port allocator session will keep on running.
bool gather_continually = false;
};
diff --git a/webrtc/p2p/base/transportchannel.cc b/webrtc/p2p/base/transportchannel.cc
index 63d84494e5..6cbe2b7583 100644
--- a/webrtc/p2p/base/transportchannel.cc
+++ b/webrtc/p2p/base/transportchannel.cc
@@ -51,7 +51,20 @@ void TransportChannel::set_dtls_state(DtlsTransportState state) {
LOG_J(LS_VERBOSE, this) << "set_dtls_state from:" << dtls_state_ << " to "
<< state;
dtls_state_ = state;
- SignalDtlsState(this);
+ SignalDtlsState(this, state);
+}
+
+bool TransportChannel::SetSrtpCryptoSuites(const std::vector<int>& ciphers) {
+ return false;
+}
+
+// TODO(guoweis): Remove this function once everything is moved away.
+bool TransportChannel::SetSrtpCiphers(const std::vector<std::string>& ciphers) {
+ std::vector<int> crypto_suites;
+ for (const auto cipher : ciphers) {
+ crypto_suites.push_back(rtc::SrtpCryptoSuiteFromName(cipher));
+ }
+ return SetSrtpCryptoSuites(crypto_suites);
}
} // namespace cricket
diff --git a/webrtc/p2p/base/transportchannel.h b/webrtc/p2p/base/transportchannel.h
index 767a5f68bf..b91af139b7 100644
--- a/webrtc/p2p/base/transportchannel.h
+++ b/webrtc/p2p/base/transportchannel.h
@@ -79,8 +79,9 @@ class TransportChannel : public sigslot::has_slots<> {
// Emitted when the TransportChannel's ability to send has changed.
sigslot::signal1<TransportChannel*> SignalReadyToSend;
sigslot::signal1<TransportChannel*> SignalReceivingState;
- // Emitted when the DtlsTransportState has changed.
- sigslot::signal1<TransportChannel*> SignalDtlsState;
+ // Emitted whenever DTLS-SRTP is setup which will require setting up a new
+ // SRTP context.
+ sigslot::signal2<TransportChannel*, DtlsTransportState> SignalDtlsState;
// Attempts to send the given packet. The return value is < 0 on failure.
// TODO: Remove the default argument once channel code is updated.
@@ -107,14 +108,17 @@ class TransportChannel : public sigslot::has_slots<> {
// Default implementation.
virtual bool GetSslRole(rtc::SSLRole* role) const = 0;
- // Sets up the ciphers to use for DTLS-SRTP.
- virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers) = 0;
+ // Sets up the ciphers to use for DTLS-SRTP. TODO(guoweis): Make this pure
+ // virtual once all dependencies have implementation.
+ virtual bool SetSrtpCryptoSuites(const std::vector<int>& ciphers);
+
+ // Keep the original one for backward compatibility until all dependencies
+ // move away. TODO(guoweis): Remove this function.
+ virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers);
// Finds out which DTLS-SRTP cipher was negotiated.
// TODO(guoweis): Remove this once all dependencies implement this.
- virtual bool GetSrtpCryptoSuite(std::string* cipher) {
- return false;
- }
+ virtual bool GetSrtpCryptoSuite(int* cipher) { return false; }
// Finds out which DTLS cipher was negotiated.
// TODO(guoweis): Remove this once all dependencies implement this.
@@ -154,9 +158,6 @@ class TransportChannel : public sigslot::has_slots<> {
std::string ToString() const;
protected:
- // TODO(honghaiz): Remove this once chromium's unit tests no longer call it.
- void set_readable(bool readable) { set_receiving(readable); }
-
// Sets the writable state, signaling if necessary.
void set_writable(bool writable);
diff --git a/webrtc/p2p/base/transportcontroller.cc b/webrtc/p2p/base/transportcontroller.cc
index 22b827a1a5..053388eeb8 100644
--- a/webrtc/p2p/base/transportcontroller.cc
+++ b/webrtc/p2p/base/transportcontroller.cc
@@ -66,9 +66,10 @@ void TransportController::SetIceRole(IceRole ice_role) {
rtc::Bind(&TransportController::SetIceRole_w, this, ice_role));
}
-bool TransportController::GetSslRole(rtc::SSLRole* role) {
- return worker_thread_->Invoke<bool>(
- rtc::Bind(&TransportController::GetSslRole_w, this, role));
+bool TransportController::GetSslRole(const std::string& transport_name,
+ rtc::SSLRole* role) {
+ return worker_thread_->Invoke<bool>(rtc::Bind(
+ &TransportController::GetSslRole_w, this, transport_name, role));
}
bool TransportController::SetLocalCertificate(
@@ -343,13 +344,16 @@ void TransportController::SetIceRole_w(IceRole ice_role) {
}
}
-bool TransportController::GetSslRole_w(rtc::SSLRole* role) {
+bool TransportController::GetSslRole_w(const std::string& transport_name,
+ rtc::SSLRole* role) {
RTC_DCHECK(worker_thread()->IsCurrent());
- if (transports_.empty()) {
+ Transport* t = GetTransport_w(transport_name);
+ if (!t) {
return false;
}
- return transports_.begin()->second->GetSslRole(role);
+
+ return t->GetSslRole(role);
}
bool TransportController::SetLocalCertificate_w(
diff --git a/webrtc/p2p/base/transportcontroller.h b/webrtc/p2p/base/transportcontroller.h
index 8d57b460e8..450e6b391f 100644
--- a/webrtc/p2p/base/transportcontroller.h
+++ b/webrtc/p2p/base/transportcontroller.h
@@ -48,11 +48,7 @@ class TransportController : public sigslot::has_slots<>,
void SetIceConfig(const IceConfig& config);
void SetIceRole(IceRole ice_role);
- // TODO(deadbeef) - Return role of each transport, as role may differ from
- // one another.
- // In current implementaion we just return the role of the first transport
- // alphabetically.
- bool GetSslRole(rtc::SSLRole* role);
+ bool GetSslRole(const std::string& transport_name, rtc::SSLRole* role);
// Specifies the identity to use in this session.
// Can only be called once.
@@ -160,7 +156,7 @@ class TransportController : public sigslot::has_slots<>,
bool SetSslMaxProtocolVersion_w(rtc::SSLProtocolVersion version);
void SetIceConfig_w(const IceConfig& config);
void SetIceRole_w(IceRole ice_role);
- bool GetSslRole_w(rtc::SSLRole* role);
+ bool GetSslRole_w(const std::string& transport_name, rtc::SSLRole* role);
bool SetLocalCertificate_w(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
bool GetLocalCertificate_w(
@@ -202,7 +198,7 @@ class TransportController : public sigslot::has_slots<>,
std::vector<RefCountedChannel> channels_;
PortAllocator* const port_allocator_ = nullptr;
- rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10;
+ rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12;
// Aggregate state for TransportChannelImpls.
IceConnectionState connection_state_ = kIceConnectionConnecting;
diff --git a/webrtc/p2p/base/transportcontroller_unittest.cc b/webrtc/p2p/base/transportcontroller_unittest.cc
index 23e4dc8067..6ff158e8fc 100644
--- a/webrtc/p2p/base/transportcontroller_unittest.cc
+++ b/webrtc/p2p/base/transportcontroller_unittest.cc
@@ -262,21 +262,18 @@ TEST_F(TransportControllerTest, TestGetSslRole) {
ASSERT_NE(nullptr, channel);
ASSERT_TRUE(channel->SetSslRole(rtc::SSL_CLIENT));
rtc::SSLRole role;
- EXPECT_TRUE(transport_controller_->GetSslRole(&role));
+ EXPECT_FALSE(transport_controller_->GetSslRole("video", &role));
+ EXPECT_TRUE(transport_controller_->GetSslRole("audio", &role));
EXPECT_EQ(rtc::SSL_CLIENT, role);
}
TEST_F(TransportControllerTest, TestSetAndGetLocalCertificate) {
rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
- rtc::RTCCertificate::Create(
- rtc::scoped_ptr<rtc::SSLIdentity>(
- rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT))
- .Pass());
+ rtc::RTCCertificate::Create(rtc::scoped_ptr<rtc::SSLIdentity>(
+ rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
- rtc::RTCCertificate::Create(
- rtc::scoped_ptr<rtc::SSLIdentity>(
- rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT))
- .Pass());
+ rtc::RTCCertificate::Create(rtc::scoped_ptr<rtc::SSLIdentity>(
+ rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT)));
rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
diff --git a/webrtc/p2p/base/transportdescription.cc b/webrtc/p2p/base/transportdescription.cc
index 52033ec9c3..b8f14eaa98 100644
--- a/webrtc/p2p/base/transportdescription.cc
+++ b/webrtc/p2p/base/transportdescription.cc
@@ -10,7 +10,7 @@
#include "webrtc/p2p/base/transportdescription.h"
-#include "webrtc/base/basicdefs.h"
+#include "webrtc/base/arraysize.h"
#include "webrtc/base/stringutils.h"
#include "webrtc/p2p/base/constants.h"
@@ -24,7 +24,7 @@ bool StringToConnectionRole(const std::string& role_str, ConnectionRole* role) {
CONNECTIONROLE_HOLDCONN_STR
};
- for (size_t i = 0; i < ARRAY_SIZE(roles); ++i) {
+ for (size_t i = 0; i < arraysize(roles); ++i) {
if (_stricmp(roles[i], role_str.c_str()) == 0) {
*role = static_cast<ConnectionRole>(CONNECTIONROLE_ACTIVE + i);
return true;
diff --git a/webrtc/p2p/base/transportdescriptionfactory_unittest.cc b/webrtc/p2p/base/transportdescriptionfactory_unittest.cc
index e3992dfdd3..a52d9ed95a 100644
--- a/webrtc/p2p/base/transportdescriptionfactory_unittest.cc
+++ b/webrtc/p2p/base/transportdescriptionfactory_unittest.cc
@@ -26,11 +26,10 @@ using cricket::TransportOptions;
class TransportDescriptionFactoryTest : public testing::Test {
public:
TransportDescriptionFactoryTest()
- : cert1_(rtc::RTCCertificate::Create(scoped_ptr<rtc::SSLIdentity>(
- new rtc::FakeSSLIdentity("User1")).Pass())),
- cert2_(rtc::RTCCertificate::Create(scoped_ptr<rtc::SSLIdentity>(
- new rtc::FakeSSLIdentity("User2")).Pass())) {
- }
+ : cert1_(rtc::RTCCertificate::Create(
+ scoped_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("User1")))),
+ cert2_(rtc::RTCCertificate::Create(
+ scoped_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("User2")))) {}
void CheckDesc(const TransportDescription* desc,
const std::string& opt, const std::string& ice_ufrag,
diff --git a/webrtc/p2p/base/turnport.cc b/webrtc/p2p/base/turnport.cc
index 3fdcac5f31..5ed93dd1d8 100644
--- a/webrtc/p2p/base/turnport.cc
+++ b/webrtc/p2p/base/turnport.cc
@@ -38,6 +38,8 @@ static const size_t TURN_CHANNEL_HEADER_SIZE = 4U;
// STUN_ERROR_ALLOCATION_MISMATCH error per rfc5766.
static const size_t MAX_ALLOCATE_MISMATCH_RETRIES = 2;
+static const int TURN_SUCCESS_RESULT_CODE = 0;
+
inline bool IsTurnChannelData(uint16_t msg_type) {
return ((msg_type & 0xC000) == 0x4000); // MSB are 0b01
}
@@ -137,11 +139,19 @@ class TurnEntry : public sigslot::has_slots<> {
TurnPort* port() { return port_; }
int channel_id() const { return channel_id_; }
+ // For testing only.
+ void set_channel_id(int channel_id) { channel_id_ = channel_id; }
+
const rtc::SocketAddress& address() const { return ext_addr_; }
BindState state() const { return state_; }
+ uint32_t destruction_timestamp() { return destruction_timestamp_; }
+ void set_destruction_timestamp(uint32_t destruction_timestamp) {
+ destruction_timestamp_ = destruction_timestamp;
+ }
+
// Helper methods to send permission and channel bind requests.
- void SendCreatePermissionRequest();
+ void SendCreatePermissionRequest(int delay);
void SendChannelBindRequest(int delay);
// Sends a packet to the given destination address.
// This will wrap the packet in STUN if necessary.
@@ -150,8 +160,10 @@ class TurnEntry : public sigslot::has_slots<> {
void OnCreatePermissionSuccess();
void OnCreatePermissionError(StunMessage* response, int code);
+ void OnCreatePermissionTimeout();
void OnChannelBindSuccess();
void OnChannelBindError(StunMessage* response, int code);
+ void OnChannelBindTimeout();
// Signal sent when TurnEntry is destroyed.
sigslot::signal1<TurnEntry*> SignalDestroyed;
@@ -160,6 +172,11 @@ class TurnEntry : public sigslot::has_slots<> {
int channel_id_;
rtc::SocketAddress ext_addr_;
BindState state_;
+ // A non-zero value indicates that this entry is scheduled to be destroyed.
+ // It is also used as an ID of the event scheduling. When the destruction
+ // event actually fires, the TurnEntry will be destroyed only if the
+ // timestamp here matches the one in the firing event.
+ uint32_t destruction_timestamp_ = 0;
};
TurnPort::TurnPort(rtc::Thread* thread,
@@ -239,7 +256,7 @@ TurnPort::~TurnPort() {
}
while (!entries_.empty()) {
- DestroyEntry(entries_.front()->address());
+ DestroyEntry(entries_.front());
}
if (resolver_) {
resolver_->Destroy(false);
@@ -267,7 +284,7 @@ void TurnPort::PrepareAddress() {
server_address_.address.SetPort(TURN_DEFAULT_PORT);
}
- if (server_address_.address.IsUnresolved()) {
+ if (server_address_.address.IsUnresolvedIP()) {
ResolveTurnAddress(server_address_.address);
} else {
// If protocol family of server address doesn't match with local, return.
@@ -334,6 +351,8 @@ bool TurnPort::CreateTurnClientSocket() {
socket_->SignalReadyToSend.connect(this, &TurnPort::OnReadyToSend);
+ socket_->SignalSentPacket.connect(this, &TurnPort::OnSentPacket);
+
// TCP port is ready to send stun requests after the socket is connected,
// while UDP port is ready to do so once the socket is created.
if (server_address_.proto == PROTO_TCP) {
@@ -380,7 +399,7 @@ void TurnPort::OnSocketConnect(rtc::AsyncPacketSocket* socket) {
}
state_ = STATE_CONNECTED; // It is ready to send stun requests.
- if (server_address_.address.IsUnresolved()) {
+ if (server_address_.address.IsUnresolvedIP()) {
server_address_.address = socket_->GetRemoteAddress();
}
@@ -392,11 +411,7 @@ void TurnPort::OnSocketConnect(rtc::AsyncPacketSocket* socket) {
void TurnPort::OnSocketClose(rtc::AsyncPacketSocket* socket, int error) {
LOG_J(LS_WARNING, this) << "Connection with server failed, error=" << error;
ASSERT(socket == socket_);
- if (!ready()) {
- OnAllocateError();
- }
- request_manager_.Clear();
- state_ = STATE_DISCONNECTED;
+ Close();
}
void TurnPort::OnAllocateMismatch() {
@@ -425,7 +440,7 @@ void TurnPort::OnAllocateMismatch() {
Connection* TurnPort::CreateConnection(const Candidate& address,
CandidateOrigin origin) {
// TURN-UDP can only connect to UDP candidates.
- if (address.protocol() != UDP_PROTOCOL_NAME) {
+ if (!SupportsProtocol(address.protocol())) {
return NULL;
}
@@ -438,7 +453,7 @@ Connection* TurnPort::CreateConnection(const Candidate& address,
}
// Create an entry, if needed, so we can get our permissions set up correctly.
- CreateEntry(address.address());
+ CreateOrRefreshEntry(address.address());
// A TURN port will have two candiates, STUN and TURN. STUN may not
// present in all cases. If present stun candidate will be added first
@@ -454,6 +469,15 @@ Connection* TurnPort::CreateConnection(const Candidate& address,
return NULL;
}
+bool TurnPort::DestroyConnection(const rtc::SocketAddress& address) {
+ Connection* conn = GetConnection(address);
+ if (conn != nullptr) {
+ conn->Destroy();
+ return true;
+ }
+ return false;
+}
+
int TurnPort::SetOption(rtc::Socket::Option opt, int value) {
if (!socket_) {
// If socket is not created yet, these options will be applied during socket
@@ -560,6 +584,11 @@ void TurnPort::OnReadPacket(
}
}
+void TurnPort::OnSentPacket(rtc::AsyncPacketSocket* socket,
+ const rtc::SentPacket& sent_packet) {
+ PortInterface::SignalSentPacket(sent_packet);
+}
+
void TurnPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
if (ready()) {
Port::OnReadyToSend();
@@ -602,6 +631,8 @@ void TurnPort::ResolveTurnAddress(const rtc::SocketAddress& address) {
if (resolver_)
return;
+ LOG_J(LS_INFO, this) << "Starting TURN host lookup for "
+ << address.ToSensitiveString();
resolver_ = socket_factory()->CreateAsyncResolver();
resolver_->SignalDone.connect(this, &TurnPort::OnResolveResult);
resolver_->Start(address);
@@ -686,35 +717,59 @@ void TurnPort::OnAllocateError() {
// We will send SignalPortError asynchronously as this can be sent during
// port initialization. This way it will not be blocking other port
// creation.
- thread()->Post(this, MSG_ERROR);
+ thread()->Post(this, MSG_ALLOCATE_ERROR);
}
-void TurnPort::OnMessage(rtc::Message* message) {
- if (message->message_id == MSG_ERROR) {
- SignalPortError(this);
- return;
- } else if (message->message_id == MSG_ALLOCATE_MISMATCH) {
- OnAllocateMismatch();
- return;
- } else if (message->message_id == MSG_TRY_ALTERNATE_SERVER) {
- if (server_address().proto == PROTO_UDP) {
- // Send another allocate request to alternate server, with the received
- // realm and nonce values.
- SendRequest(new TurnAllocateRequest(this), 0);
- } else {
- // Since it's TCP, we have to delete the connected socket and reconnect
- // with the alternate server. PrepareAddress will send stun binding once
- // the new socket is connected.
- ASSERT(server_address().proto == PROTO_TCP);
- ASSERT(!SharedSocket());
- delete socket_;
- socket_ = NULL;
- PrepareAddress();
- }
- return;
+void TurnPort::OnTurnRefreshError() {
+ // Need to Close the port asynchronously because otherwise, the refresh
+ // request may be deleted twice: once at the end of the message processing
+ // and the other in Close().
+ thread()->Post(this, MSG_REFRESH_ERROR);
+}
+
+void TurnPort::Close() {
+ if (!ready()) {
+ OnAllocateError();
+ }
+ request_manager_.Clear();
+ // Stop the port from creating new connections.
+ state_ = STATE_DISCONNECTED;
+ // Delete all existing connections; stop sending data.
+ for (auto kv : connections()) {
+ kv.second->Destroy();
}
+}
- Port::OnMessage(message);
+void TurnPort::OnMessage(rtc::Message* message) {
+ switch (message->message_id) {
+ case MSG_ALLOCATE_ERROR:
+ SignalPortError(this);
+ break;
+ case MSG_ALLOCATE_MISMATCH:
+ OnAllocateMismatch();
+ break;
+ case MSG_REFRESH_ERROR:
+ Close();
+ break;
+ case MSG_TRY_ALTERNATE_SERVER:
+ if (server_address().proto == PROTO_UDP) {
+ // Send another allocate request to alternate server, with the received
+ // realm and nonce values.
+ SendRequest(new TurnAllocateRequest(this), 0);
+ } else {
+ // Since it's TCP, we have to delete the connected socket and reconnect
+ // with the alternate server. PrepareAddress will send stun binding once
+ // the new socket is connected.
+ ASSERT(server_address().proto == PROTO_TCP);
+ ASSERT(!SharedSocket());
+ delete socket_;
+ socket_ = NULL;
+ PrepareAddress();
+ }
+ break;
+ default:
+ Port::OnMessage(message);
+ }
}
void TurnPort::OnAllocateRequestTimeout() {
@@ -898,24 +953,73 @@ TurnEntry* TurnPort::FindEntry(int channel_id) const {
return (it != entries_.end()) ? *it : NULL;
}
-TurnEntry* TurnPort::CreateEntry(const rtc::SocketAddress& addr) {
- ASSERT(FindEntry(addr) == NULL);
- TurnEntry* entry = new TurnEntry(this, next_channel_number_++, addr);
- entries_.push_back(entry);
- return entry;
+bool TurnPort::EntryExists(TurnEntry* e) {
+ auto it = std::find(entries_.begin(), entries_.end(), e);
+ return it != entries_.end();
}
-void TurnPort::DestroyEntry(const rtc::SocketAddress& addr) {
+void TurnPort::CreateOrRefreshEntry(const rtc::SocketAddress& addr) {
TurnEntry* entry = FindEntry(addr);
+ if (entry == nullptr) {
+ entry = new TurnEntry(this, next_channel_number_++, addr);
+ entries_.push_back(entry);
+ } else {
+ // The channel binding request for the entry will be refreshed automatically
+ // until the entry is destroyed.
+ CancelEntryDestruction(entry);
+ }
+}
+
+void TurnPort::DestroyEntry(TurnEntry* entry) {
ASSERT(entry != NULL);
entry->SignalDestroyed(entry);
entries_.remove(entry);
delete entry;
}
+void TurnPort::DestroyEntryIfNotCancelled(TurnEntry* entry,
+ uint32_t timestamp) {
+ if (!EntryExists(entry)) {
+ return;
+ }
+ bool cancelled = timestamp != entry->destruction_timestamp();
+ if (!cancelled) {
+ DestroyEntry(entry);
+ }
+}
+
void TurnPort::OnConnectionDestroyed(Connection* conn) {
- // Destroying TurnEntry for the connection, which is already destroyed.
- DestroyEntry(conn->remote_candidate().address());
+ // Schedule an event to destroy TurnEntry for the connection, which is
+ // already destroyed.
+ const rtc::SocketAddress& remote_address = conn->remote_candidate().address();
+ TurnEntry* entry = FindEntry(remote_address);
+ ASSERT(entry != NULL);
+ ScheduleEntryDestruction(entry);
+}
+
+void TurnPort::ScheduleEntryDestruction(TurnEntry* entry) {
+ ASSERT(entry->destruction_timestamp() == 0);
+ uint32_t timestamp = rtc::Time();
+ entry->set_destruction_timestamp(timestamp);
+ invoker_.AsyncInvokeDelayed<void>(
+ thread(),
+ rtc::Bind(&TurnPort::DestroyEntryIfNotCancelled, this, entry, timestamp),
+ TURN_PERMISSION_TIMEOUT);
+}
+
+void TurnPort::CancelEntryDestruction(TurnEntry* entry) {
+ ASSERT(entry->destruction_timestamp() != 0);
+ entry->set_destruction_timestamp(0);
+}
+
+bool TurnPort::SetEntryChannelId(const rtc::SocketAddress& address,
+ int channel_id) {
+ TurnEntry* entry = FindEntry(address);
+ if (!entry) {
+ return false;
+ }
+ entry->set_channel_id(channel_id);
+ return true;
}
TurnAllocateRequest::TurnAllocateRequest(TurnPort* port)
@@ -1131,16 +1235,12 @@ void TurnRefreshRequest::OnResponse(StunMessage* response) {
// Schedule a refresh based on the returned lifetime value.
port_->ScheduleRefresh(lifetime_attr->value());
+ port_->SignalTurnRefreshResult(port_, TURN_SUCCESS_RESULT_CODE);
}
void TurnRefreshRequest::OnErrorResponse(StunMessage* response) {
const StunErrorCodeAttribute* error_code = response->GetErrorCode();
- LOG_J(LS_INFO, port_) << "Received TURN refresh error response"
- << ", id=" << rtc::hex_encode(id())
- << ", code=" << error_code->code()
- << ", rtt=" << Elapsed();
-
if (error_code->code() == STUN_ERROR_STALE_NONCE) {
if (port_->UpdateNonce(response)) {
// Send RefreshRequest immediately.
@@ -1151,11 +1251,14 @@ void TurnRefreshRequest::OnErrorResponse(StunMessage* response) {
<< ", id=" << rtc::hex_encode(id())
<< ", code=" << error_code->code()
<< ", rtt=" << Elapsed();
+ port_->OnTurnRefreshError();
+ port_->SignalTurnRefreshResult(port_, error_code->code());
}
}
void TurnRefreshRequest::OnTimeout() {
LOG_J(LS_WARNING, port_) << "TURN refresh timeout " << rtc::hex_encode(id());
+ port_->OnTurnRefreshError();
}
TurnCreatePermissionRequest::TurnCreatePermissionRequest(
@@ -1208,6 +1311,9 @@ void TurnCreatePermissionRequest::OnErrorResponse(StunMessage* response) {
void TurnCreatePermissionRequest::OnTimeout() {
LOG_J(LS_WARNING, port_) << "TURN create permission timeout "
<< rtc::hex_encode(id());
+ if (entry_) {
+ entry_->OnCreatePermissionTimeout();
+ }
}
void TurnCreatePermissionRequest::OnEntryDestroyed(TurnEntry* entry) {
@@ -1275,6 +1381,9 @@ void TurnChannelBindRequest::OnErrorResponse(StunMessage* response) {
void TurnChannelBindRequest::OnTimeout() {
LOG_J(LS_WARNING, port_) << "TURN channel bind timeout "
<< rtc::hex_encode(id());
+ if (entry_) {
+ entry_->OnChannelBindTimeout();
+ }
}
void TurnChannelBindRequest::OnEntryDestroyed(TurnEntry* entry) {
@@ -1289,12 +1398,12 @@ TurnEntry::TurnEntry(TurnPort* port, int channel_id,
ext_addr_(ext_addr),
state_(STATE_UNBOUND) {
// Creating permission for |ext_addr_|.
- SendCreatePermissionRequest();
+ SendCreatePermissionRequest(0);
}
-void TurnEntry::SendCreatePermissionRequest() {
- port_->SendRequest(new TurnCreatePermissionRequest(
- port_, this, ext_addr_), 0);
+void TurnEntry::SendCreatePermissionRequest(int delay) {
+ port_->SendRequest(new TurnCreatePermissionRequest(port_, this, ext_addr_),
+ delay);
}
void TurnEntry::SendChannelBindRequest(int delay) {
@@ -1335,21 +1444,43 @@ void TurnEntry::OnCreatePermissionSuccess() {
LOG_J(LS_INFO, port_) << "Create permission for "
<< ext_addr_.ToSensitiveString()
<< " succeeded";
- // For success result code will be 0.
- port_->SignalCreatePermissionResult(port_, ext_addr_, 0);
+ port_->SignalCreatePermissionResult(port_, ext_addr_,
+ TURN_SUCCESS_RESULT_CODE);
+
+ // If |state_| is STATE_BOUND, the permission will be refreshed
+ // by ChannelBindRequest.
+ if (state_ != STATE_BOUND) {
+ // Refresh the permission request about 1 minute before the permission
+ // times out.
+ int delay = TURN_PERMISSION_TIMEOUT - 60000;
+ SendCreatePermissionRequest(delay);
+ LOG_J(LS_INFO, port_) << "Scheduled create-permission-request in "
+ << delay << "ms.";
+ }
}
void TurnEntry::OnCreatePermissionError(StunMessage* response, int code) {
if (code == STUN_ERROR_STALE_NONCE) {
if (port_->UpdateNonce(response)) {
- SendCreatePermissionRequest();
+ SendCreatePermissionRequest(0);
}
} else {
+ port_->DestroyConnection(ext_addr_);
// Send signal with error code.
port_->SignalCreatePermissionResult(port_, ext_addr_, code);
+ Connection* c = port_->GetConnection(ext_addr_);
+ if (c) {
+ LOG_J(LS_ERROR, c) << "Received TURN CreatePermission error response, "
+ << "code=" << code << "; killing connection.";
+ c->FailAndDestroy();
+ }
}
}
+void TurnEntry::OnCreatePermissionTimeout() {
+ port_->DestroyConnection(ext_addr_);
+}
+
void TurnEntry::OnChannelBindSuccess() {
LOG_J(LS_INFO, port_) << "Channel bind for " << ext_addr_.ToSensitiveString()
<< " succeeded";
@@ -1358,14 +1489,21 @@ void TurnEntry::OnChannelBindSuccess() {
}
void TurnEntry::OnChannelBindError(StunMessage* response, int code) {
- // TODO(mallinath) - Implement handling of error response for channel
- // bind request as per http://tools.ietf.org/html/rfc5766#section-11.3
+ // If the channel bind fails due to errors other than STATE_NONCE,
+ // we just destroy the connection and rely on ICE restart to re-establish
+ // the connection.
if (code == STUN_ERROR_STALE_NONCE) {
if (port_->UpdateNonce(response)) {
// Send channel bind request with fresh nonce.
SendChannelBindRequest(0);
}
+ } else {
+ state_ = STATE_UNBOUND;
+ port_->DestroyConnection(ext_addr_);
}
}
-
+void TurnEntry::OnChannelBindTimeout() {
+ state_ = STATE_UNBOUND;
+ port_->DestroyConnection(ext_addr_);
+}
} // namespace cricket
diff --git a/webrtc/p2p/base/turnport.h b/webrtc/p2p/base/turnport.h
index 3bca727346..4d83806a37 100644
--- a/webrtc/p2p/base/turnport.h
+++ b/webrtc/p2p/base/turnport.h
@@ -16,9 +16,10 @@
#include <set>
#include <string>
+#include "webrtc/base/asyncinvoker.h"
+#include "webrtc/base/asyncpacketsocket.h"
#include "webrtc/p2p/base/port.h"
#include "webrtc/p2p/client/basicportallocator.h"
-#include "webrtc/base/asyncpacketsocket.h"
namespace rtc {
class AsyncResolver;
@@ -105,7 +106,13 @@ class TurnPort : public Port {
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time);
+ virtual void OnSentPacket(rtc::AsyncPacketSocket* socket,
+ const rtc::SentPacket& sent_packet);
virtual void OnReadyToSend(rtc::AsyncPacketSocket* socket);
+ virtual bool SupportsProtocol(const std::string& protocol) const {
+ // Turn port only connects to UDP candidates.
+ return protocol == UDP_PROTOCOL_NAME;
+ }
void OnSocketConnect(rtc::AsyncPacketSocket* socket);
void OnSocketClose(rtc::AsyncPacketSocket* socket, int error);
@@ -122,6 +129,9 @@ class TurnPort : public Port {
return socket_;
}
+ // For testing only.
+ rtc::AsyncInvoker* invoker() { return &invoker_; }
+
// Signal with resolved server address.
// Parameters are port, server address and resolved server address.
// This signal will be sent only if server address is resolved successfully.
@@ -129,9 +139,18 @@ class TurnPort : public Port {
const rtc::SocketAddress&,
const rtc::SocketAddress&> SignalResolvedServerAddress;
- // This signal is only for testing purpose.
+ // All public methods/signals below are for testing only.
+ sigslot::signal2<TurnPort*, int> SignalTurnRefreshResult;
sigslot::signal3<TurnPort*, const rtc::SocketAddress&, int>
SignalCreatePermissionResult;
+ void FlushRequests(int msg_type) { request_manager_.Flush(msg_type); }
+ bool HasRequests() { return !request_manager_.empty(); }
+ void set_credentials(RelayCredentials& credentials) {
+ credentials_ = credentials;
+ }
+ // Finds the turn entry with |address| and sets its channel id.
+ // Returns true if the entry is found.
+ bool SetEntryChannelId(const rtc::SocketAddress& address, int channel_id);
protected:
TurnPort(rtc::Thread* thread,
@@ -160,9 +179,10 @@ class TurnPort : public Port {
private:
enum {
- MSG_ERROR = MSG_FIRST_AVAILABLE,
+ MSG_ALLOCATE_ERROR = MSG_FIRST_AVAILABLE,
MSG_ALLOCATE_MISMATCH,
- MSG_TRY_ALTERNATE_SERVER
+ MSG_TRY_ALTERNATE_SERVER,
+ MSG_REFRESH_ERROR
};
typedef std::list<TurnEntry*> EntryList;
@@ -181,6 +201,9 @@ class TurnPort : public Port {
}
}
+ // Shuts down the turn port, usually because of some fatal errors.
+ void Close();
+ void OnTurnRefreshError();
bool SetAlternateServer(const rtc::SocketAddress& address);
void ResolveTurnAddress(const rtc::SocketAddress& address);
void OnResolveResult(rtc::AsyncResolverInterface* resolver);
@@ -213,10 +236,20 @@ class TurnPort : public Port {
bool HasPermission(const rtc::IPAddress& ipaddr) const;
TurnEntry* FindEntry(const rtc::SocketAddress& address) const;
TurnEntry* FindEntry(int channel_id) const;
- TurnEntry* CreateEntry(const rtc::SocketAddress& address);
- void DestroyEntry(const rtc::SocketAddress& address);
+ bool EntryExists(TurnEntry* e);
+ void CreateOrRefreshEntry(const rtc::SocketAddress& address);
+ void DestroyEntry(TurnEntry* entry);
+ // Destroys the entry only if |timestamp| matches the destruction timestamp
+ // in |entry|.
+ void DestroyEntryIfNotCancelled(TurnEntry* entry, uint32_t timestamp);
+ void ScheduleEntryDestruction(TurnEntry* entry);
+ void CancelEntryDestruction(TurnEntry* entry);
void OnConnectionDestroyed(Connection* conn);
+ // Destroys the connection with remote address |address|. Returns true if
+ // a connection is found and destroyed.
+ bool DestroyConnection(const rtc::SocketAddress& address);
+
ProtocolAddress server_address_;
RelayCredentials credentials_;
AttemptedServerSet attempted_server_addresses_;
@@ -242,6 +275,8 @@ class TurnPort : public Port {
// The number of retries made due to allocate mismatch error.
size_t allocate_mismatch_retries_;
+ rtc::AsyncInvoker invoker_;
+
friend class TurnEntry;
friend class TurnAllocateRequest;
friend class TurnRefreshRequest;
diff --git a/webrtc/p2p/base/turnport_unittest.cc b/webrtc/p2p/base/turnport_unittest.cc
index 724485ddde..916162575f 100644
--- a/webrtc/p2p/base/turnport_unittest.cc
+++ b/webrtc/p2p/base/turnport_unittest.cc
@@ -13,6 +13,7 @@
#include "webrtc/p2p/base/basicpacketsocketfactory.h"
#include "webrtc/p2p/base/constants.h"
+#include "webrtc/p2p/base/portallocator.h"
#include "webrtc/p2p/base/tcpport.h"
#include "webrtc/p2p/base/testturnserver.h"
#include "webrtc/p2p/base/turnport.h"
@@ -100,6 +101,24 @@ class TurnPortTestVirtualSocketServer : public rtc::VirtualSocketServer {
using rtc::VirtualSocketServer::LookupBinding;
};
+class TestConnectionWrapper : public sigslot::has_slots<> {
+ public:
+ TestConnectionWrapper(Connection* conn) : connection_(conn) {
+ conn->SignalDestroyed.connect(
+ this, &TestConnectionWrapper::OnConnectionDestroyed);
+ }
+
+ Connection* connection() { return connection_; }
+
+ private:
+ void OnConnectionDestroyed(Connection* conn) {
+ ASSERT_TRUE(conn == connection_);
+ connection_ = nullptr;
+ }
+
+ Connection* connection_;
+};
+
class TurnPortTest : public testing::Test,
public sigslot::has_slots<>,
public rtc::MessageHandler {
@@ -154,12 +173,15 @@ class TurnPortTest : public testing::Test,
bool /*port_muxed*/) {
turn_unknown_address_ = true;
}
- void OnTurnCreatePermissionResult(TurnPort* port, const SocketAddress& addr,
- int code) {
+ void OnTurnCreatePermissionResult(TurnPort* port,
+ const SocketAddress& addr,
+ int code) {
// Ignoring the address.
- if (code == 0) {
- turn_create_permission_success_ = true;
- }
+ turn_create_permission_success_ = (code == 0);
+ }
+
+ void OnTurnRefreshResult(TurnPort* port, int code) {
+ turn_refresh_success_ = (code == 0);
}
void OnTurnReadPacket(Connection* conn, const char* data, size_t size,
const rtc::PacketTime& packet_time) {
@@ -172,6 +194,7 @@ class TurnPortTest : public testing::Test,
const rtc::PacketTime& packet_time) {
udp_packets_.push_back(rtc::Buffer(data, size));
}
+ void OnConnectionDestroyed(Connection* conn) { connection_destroyed_ = true; }
void OnSocketReadPacket(rtc::AsyncPacketSocket* socket,
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
@@ -255,18 +278,42 @@ class TurnPortTest : public testing::Test,
&TurnPortTest::OnTurnUnknownAddress);
turn_port_->SignalCreatePermissionResult.connect(this,
&TurnPortTest::OnTurnCreatePermissionResult);
+ turn_port_->SignalTurnRefreshResult.connect(
+ this, &TurnPortTest::OnTurnRefreshResult);
}
- void CreateUdpPort() {
+ void ConnectConnectionDestroyedSignal(Connection* conn) {
+ conn->SignalDestroyed.connect(this, &TurnPortTest::OnConnectionDestroyed);
+ }
+
+ void CreateUdpPort() { CreateUdpPort(kLocalAddr2); }
+
+ void CreateUdpPort(const SocketAddress& address) {
udp_port_.reset(UDPPort::Create(main_, &socket_factory_, &network_,
- kLocalAddr2.ipaddr(), 0, 0,
- kIceUfrag2, kIcePwd2,
- std::string(), false));
+ address.ipaddr(), 0, 0, kIceUfrag2,
+ kIcePwd2, std::string(), false));
// UDP port will be controlled.
udp_port_->SetIceRole(cricket::ICEROLE_CONTROLLED);
udp_port_->SignalPortComplete.connect(
this, &TurnPortTest::OnUdpPortComplete);
}
+ void PrepareTurnAndUdpPorts() {
+ // turn_port_ should have been created.
+ ASSERT_TRUE(turn_port_ != nullptr);
+ turn_port_->PrepareAddress();
+ ASSERT_TRUE_WAIT(turn_ready_, kTimeout);
+
+ CreateUdpPort();
+ udp_port_->PrepareAddress();
+ ASSERT_TRUE_WAIT(udp_ready_, kTimeout);
+ }
+
+ bool CheckConnectionDestroyed() {
+ turn_port_->FlushRequests(cricket::kAllRequests);
+ rtc::Thread::Current()->ProcessMessages(50);
+ return connection_destroyed_;
+ }
+
void TestTurnAlternateServer(cricket::ProtocolType protocol_type) {
std::vector<rtc::SocketAddress> redirect_addresses;
redirect_addresses.push_back(kTurnAlternateIntAddr);
@@ -350,12 +397,7 @@ class TurnPortTest : public testing::Test,
void TestTurnConnection() {
// Create ports and prepare addresses.
- ASSERT_TRUE(turn_port_ != NULL);
- turn_port_->PrepareAddress();
- ASSERT_TRUE_WAIT(turn_ready_, kTimeout);
- CreateUdpPort();
- udp_port_->PrepareAddress();
- ASSERT_TRUE_WAIT(udp_ready_, kTimeout);
+ PrepareTurnAndUdpPorts();
// Send ping from UDP to TURN.
Connection* conn1 = udp_port_->CreateConnection(
@@ -385,12 +427,46 @@ class TurnPortTest : public testing::Test,
EXPECT_TRUE(conn2->receiving());
}
+ void TestDestroyTurnConnection() {
+ PrepareTurnAndUdpPorts();
+
+ // Create connections on both ends.
+ Connection* conn1 = udp_port_->CreateConnection(turn_port_->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ Connection* conn2 = turn_port_->CreateConnection(udp_port_->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ ASSERT_TRUE(conn2 != NULL);
+ ASSERT_TRUE_WAIT(turn_create_permission_success_, kTimeout);
+ // Make sure turn connection can receive.
+ conn1->Ping(0);
+ EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, conn1->write_state(), kTimeout);
+ EXPECT_FALSE(turn_unknown_address_);
+
+ // Destroy the connection on the turn port. The TurnEntry is still
+ // there. So the turn port gets ping from unknown address if it is pinged.
+ conn2->Destroy();
+ conn1->Ping(0);
+ EXPECT_TRUE_WAIT(turn_unknown_address_, kTimeout);
+
+ // Flush all requests in the invoker to destroy the TurnEntry.
+ // Now the turn port cannot receive the ping.
+ turn_unknown_address_ = false;
+ turn_port_->invoker()->Flush(rtc::Thread::Current());
+ conn1->Ping(0);
+ rtc::Thread::Current()->ProcessMessages(500);
+ EXPECT_FALSE(turn_unknown_address_);
+
+ // If the connection is created again, it will start to receive pings.
+ conn2 = turn_port_->CreateConnection(udp_port_->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ conn1->Ping(0);
+ EXPECT_TRUE_WAIT(conn2->receiving(), kTimeout);
+ EXPECT_FALSE(turn_unknown_address_);
+ }
+
void TestTurnSendData() {
- turn_port_->PrepareAddress();
- EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
- CreateUdpPort();
- udp_port_->PrepareAddress();
- EXPECT_TRUE_WAIT(udp_ready_, kTimeout);
+ PrepareTurnAndUdpPorts();
+
// Create connections and send pings.
Connection* conn1 = turn_port_->CreateConnection(
udp_port_->Candidates()[0], Port::ORIGIN_MESSAGE);
@@ -446,6 +522,8 @@ class TurnPortTest : public testing::Test,
bool turn_create_permission_success_;
bool udp_ready_;
bool test_finish_;
+ bool turn_refresh_success_ = false;
+ bool connection_destroyed_ = false;
std::vector<rtc::Buffer> turn_packets_;
std::vector<rtc::Buffer> udp_packets_;
rtc::PacketOptions options;
@@ -613,16 +691,33 @@ TEST_F(TurnPortTest, TestTurnTcpAllocateMismatch) {
EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress());
}
+TEST_F(TurnPortTest, TestRefreshRequestGetsErrorResponse) {
+ CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+ PrepareTurnAndUdpPorts();
+ turn_port_->CreateConnection(udp_port_->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ // Set bad credentials.
+ cricket::RelayCredentials bad_credentials("bad_user", "bad_pwd");
+ turn_port_->set_credentials(bad_credentials);
+ turn_refresh_success_ = false;
+ // This sends out the first RefreshRequest with correct credentials.
+ // When this succeeds, it will schedule a new RefreshRequest with the bad
+ // credential.
+ turn_port_->FlushRequests(cricket::TURN_REFRESH_REQUEST);
+ EXPECT_TRUE_WAIT(turn_refresh_success_, kTimeout);
+ // Flush it again, it will receive a bad response.
+ turn_port_->FlushRequests(cricket::TURN_REFRESH_REQUEST);
+ EXPECT_TRUE_WAIT(!turn_refresh_success_, kTimeout);
+ EXPECT_TRUE_WAIT(!turn_port_->connected(), kTimeout);
+ EXPECT_TRUE_WAIT(turn_port_->connections().empty(), kTimeout);
+ EXPECT_FALSE(turn_port_->HasRequests());
+}
+
// Test that CreateConnection will return null if port becomes disconnected.
TEST_F(TurnPortTest, TestCreateConnectionWhenSocketClosed) {
turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
- turn_port_->PrepareAddress();
- ASSERT_TRUE_WAIT(turn_ready_, kTimeout);
-
- CreateUdpPort();
- udp_port_->PrepareAddress();
- ASSERT_TRUE_WAIT(udp_ready_, kTimeout);
+ PrepareTurnAndUdpPorts();
// Create a connection.
Connection* conn1 = turn_port_->CreateConnection(udp_port_->Candidates()[0],
Port::ORIGIN_MESSAGE);
@@ -694,6 +789,20 @@ TEST_F(TurnPortTest, TestTurnTcpConnection) {
TestTurnConnection();
}
+// Test that if a connection on a TURN port is destroyed, the TURN port can
+// still receive ping on that connection as if it is from an unknown address.
+// If the connection is created again, it will be used to receive ping.
+TEST_F(TurnPortTest, TestDestroyTurnConnection) {
+ CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+ TestDestroyTurnConnection();
+}
+
+// Similar to above, except that this test will use the shared socket.
+TEST_F(TurnPortTest, TestDestroyTurnConnectionUsingSharedSocket) {
+ CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+ TestDestroyTurnConnection();
+}
+
// Test that we fail to create a connection when we want to use TLS over TCP.
// This test should be removed once we have TLS support.
TEST_F(TurnPortTest, TestTurnTlsTcpConnectionFails) {
@@ -715,6 +824,54 @@ TEST_F(TurnPortTest, TestTurnConnectionUsingOTUNonce) {
TestTurnConnection();
}
+// Test that CreatePermissionRequest will be scheduled after the success
+// of the first create permission request and the request will get an
+// ErrorResponse if the ufrag and pwd are incorrect.
+TEST_F(TurnPortTest, TestRefreshCreatePermissionRequest) {
+ CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+ PrepareTurnAndUdpPorts();
+
+ Connection* conn = turn_port_->CreateConnection(udp_port_->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ ConnectConnectionDestroyedSignal(conn);
+ ASSERT_TRUE(conn != NULL);
+ ASSERT_TRUE_WAIT(turn_create_permission_success_, kTimeout);
+ turn_create_permission_success_ = false;
+ // A create-permission-request should be pending.
+ // After the next create-permission-response is received, it will schedule
+ // another request with bad_ufrag and bad_pwd.
+ cricket::RelayCredentials bad_credentials("bad_user", "bad_pwd");
+ turn_port_->set_credentials(bad_credentials);
+ turn_port_->FlushRequests(cricket::kAllRequests);
+ ASSERT_TRUE_WAIT(turn_create_permission_success_, kTimeout);
+ // Flush the requests again; the create-permission-request will fail.
+ turn_port_->FlushRequests(cricket::kAllRequests);
+ EXPECT_TRUE_WAIT(!turn_create_permission_success_, kTimeout);
+ EXPECT_TRUE_WAIT(connection_destroyed_, kTimeout);
+}
+
+TEST_F(TurnPortTest, TestChannelBindGetErrorResponse) {
+ CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+ PrepareTurnAndUdpPorts();
+ Connection* conn1 = turn_port_->CreateConnection(udp_port_->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ ASSERT_TRUE(conn1 != nullptr);
+ Connection* conn2 = udp_port_->CreateConnection(turn_port_->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ ASSERT_TRUE(conn2 != nullptr);
+ ConnectConnectionDestroyedSignal(conn1);
+ conn1->Ping(0);
+ ASSERT_TRUE_WAIT(conn1->writable(), kTimeout);
+
+ std::string data = "ABC";
+ conn1->Send(data.data(), data.length(), options);
+ bool success =
+ turn_port_->SetEntryChannelId(udp_port_->Candidates()[0].address(), -1);
+ ASSERT_TRUE(success);
+ // Next time when the binding request is sent, it will get an ErrorResponse.
+ EXPECT_TRUE_WAIT(CheckConnectionDestroyed(), kTimeout);
+}
+
// Do a TURN allocation, establish a UDP connection, and send some data.
TEST_F(TurnPortTest, TestTurnSendDataTurnUdpToUdp) {
// Create ports and prepare addresses.
@@ -771,6 +928,29 @@ TEST_F(TurnPortTest, TestOriginHeader) {
EXPECT_EQ(kTestOrigin, turn_server_.FindAllocation(local_address)->origin());
}
+// Test that a CreatePermission failure will result in the connection being
+// destroyed.
+TEST_F(TurnPortTest, TestConnectionDestroyedOnCreatePermissionFailure) {
+ turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
+ turn_server_.server()->set_reject_private_addresses(true);
+ CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
+ turn_port_->PrepareAddress();
+ ASSERT_TRUE_WAIT(turn_ready_, kTimeout);
+
+ CreateUdpPort(SocketAddress("10.0.0.10", 0));
+ udp_port_->PrepareAddress();
+ ASSERT_TRUE_WAIT(udp_ready_, kTimeout);
+ // Create a connection.
+ TestConnectionWrapper conn(turn_port_->CreateConnection(
+ udp_port_->Candidates()[0], Port::ORIGIN_MESSAGE));
+ ASSERT_TRUE(conn.connection() != nullptr);
+
+ // Asynchronously, CreatePermission request should be sent and fail, closing
+ // the connection.
+ EXPECT_TRUE_WAIT(conn.connection() == nullptr, kTimeout);
+ EXPECT_FALSE(turn_create_permission_success_);
+}
+
// Test that a TURN allocation is released when the port is closed.
TEST_F(TurnPortTest, TestTurnReleaseAllocation) {
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
@@ -797,6 +977,10 @@ TEST_F(TurnPortTest, DISABLED_TestTurnTCPReleaseAllocation) {
// This test verifies any FD's are not leaked after TurnPort is destroyed.
// https://code.google.com/p/webrtc/issues/detail?id=2651
#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+// 1 second is not always enough for getaddrinfo().
+// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=5191
+static const unsigned int kResolverTimeout = 10000;
+
TEST_F(TurnPortTest, TestResolverShutdown) {
turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP);
int last_fd_count = GetFDCount();
@@ -805,7 +989,7 @@ TEST_F(TurnPortTest, TestResolverShutdown) {
cricket::ProtocolAddress(rtc::SocketAddress(
"www.google.invalid", 3478), cricket::PROTO_UDP));
turn_port_->PrepareAddress();
- ASSERT_TRUE_WAIT(turn_error_, kTimeout);
+ ASSERT_TRUE_WAIT(turn_error_, kResolverTimeout);
EXPECT_TRUE(turn_port_->Candidates().empty());
turn_port_.reset();
rtc::Thread::Current()->Post(this, MSG_TESTFINISH);
diff --git a/webrtc/p2p/base/turnserver.cc b/webrtc/p2p/base/turnserver.cc
index 8d40a9030c..1502cdd52e 100644
--- a/webrtc/p2p/base/turnserver.cc
+++ b/webrtc/p2p/base/turnserver.cc
@@ -698,6 +698,12 @@ void TurnServerAllocation::HandleCreatePermissionRequest(
return;
}
+ if (server_->reject_private_addresses_ &&
+ rtc::IPIsPrivate(peer_attr->GetAddress().ipaddr())) {
+ SendErrorResponse(msg, STUN_ERROR_FORBIDDEN, STUN_ERROR_REASON_FORBIDDEN);
+ return;
+ }
+
// Add this permission.
AddPermission(peer_attr->GetAddress().ipaddr());
diff --git a/webrtc/p2p/base/turnserver.h b/webrtc/p2p/base/turnserver.h
index d3bd77a866..113bd4c462 100644
--- a/webrtc/p2p/base/turnserver.h
+++ b/webrtc/p2p/base/turnserver.h
@@ -183,6 +183,11 @@ class TurnServer : public sigslot::has_slots<> {
void set_enable_otu_nonce(bool enable) { enable_otu_nonce_ = enable; }
+ // If set to true, reject CreatePermission requests to RFC1918 addresses.
+ void set_reject_private_addresses(bool filter) {
+ reject_private_addresses_ = filter;
+ }
+
// Starts listening for packets from internal clients.
void AddInternalSocket(rtc::AsyncPacketSocket* socket,
ProtocolType proto);
@@ -255,6 +260,7 @@ class TurnServer : public sigslot::has_slots<> {
// otu - one-time-use. Server will respond with 438 if it's
// sees the same nonce in next transaction.
bool enable_otu_nonce_;
+ bool reject_private_addresses_ = false;
InternalSocketMap server_sockets_;
ServerSocketMap server_listen_sockets_;