Encoding and Decoding of TCP candidates as defined in RFC 6544.
R=juberti@chromium.org, jiayl@webrtc.org, juberti@webrtc.org
BUG=2204
Review URL: https://webrtc-codereview.appspot.com/21479004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@6857 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/app/webrtc/webrtcsdp.cc b/talk/app/webrtc/webrtcsdp.cc
index 4f774a7..64f8e5e 100644
--- a/talk/app/webrtc/webrtcsdp.cc
+++ b/talk/app/webrtc/webrtcsdp.cc
@@ -168,6 +168,7 @@
// TODO: How to map the prflx with circket candidate type
// static const char kCandidatePrflx[] = "prflx";
static const char kCandidateRelay[] = "relay";
+static const char kTcpCandidateType[] = "tcptype";
static const char kSdpDelimiterEqual = '=';
static const char kSdpDelimiterSpace = ' ';
@@ -1042,6 +1043,25 @@
++current_position;
}
+ // If this is a TCP candidate, it has additional extension as defined in
+ // RFC 6544.
+ std::string tcptype;
+ if (fields.size() >= (current_position + 2) &&
+ fields[current_position] == kTcpCandidateType) {
+ tcptype = fields[++current_position];
+ ++current_position;
+
+ if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
+ tcptype != cricket::TCPTYPE_PASSIVE_STR &&
+ tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
+ return ParseFailed(first_line, "Invalid TCP candidate type.", error);
+ }
+
+ if (protocol != cricket::PROTO_TCP) {
+ return ParseFailed(first_line, "Invalid non-TCP candidate", error);
+ }
+ }
+
// Extension
// Empty string as the candidate username and password.
// Will be updated later with the ice-ufrag and ice-pwd.
@@ -1074,6 +1094,7 @@
address, priority, username, password, candidate_type, network_name,
generation, foundation);
candidate->set_related_address(related_address);
+ candidate->set_tcptype(tcptype);
return true;
}
@@ -1694,11 +1715,14 @@
InitAttrLine(kAttributeCandidate, &os);
os << kSdpDelimiterColon
- << it->foundation() << " " << it->component() << " "
- << it->protocol() << " " << it->priority() << " "
+ << it->foundation() << " "
+ << it->component() << " "
+ << it->protocol() << " "
+ << it->priority() << " "
<< it->address().ipaddr().ToString() << " "
<< it->address().PortAsString() << " "
- << kAttributeCandidateTyp << " " << type << " ";
+ << kAttributeCandidateTyp << " "
+ << type << " ";
// Related address
if (!it->related_address().IsNil()) {
@@ -1708,6 +1732,17 @@
<< it->related_address().PortAsString() << " ";
}
+ if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
+ // In case of WebRTC, candidate must be always "active" only. That means
+ // it should have port number either 0 or 9.
+ ASSERT(it->address().port() == 0 ||
+ it->address().port() == cricket::DISCARD_PORT);
+ ASSERT(it->tcptype() == cricket::TCPTYPE_ACTIVE_STR);
+ // TODO(mallinath) : Uncomment below line once WebRTCSdp capable of
+ // parsing RFC 6544.
+ // os << kTcpCandidateType << " " << it->tcptype() << " ";
+ }
+
// Extensions
os << kAttributeCandidateGeneration << " " << it->generation();
diff --git a/talk/app/webrtc/webrtcsdp_unittest.cc b/talk/app/webrtc/webrtcsdp_unittest.cc
index 71ebe0d..f0546f8 100644
--- a/talk/app/webrtc/webrtcsdp_unittest.cc
+++ b/talk/app/webrtc/webrtcsdp_unittest.cc
@@ -354,6 +354,19 @@
static const char kSdpOneCandidate[] =
"a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
"generation 2\r\n";
+static const char kSdpTcpActiveCandidate[] =
+ "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9 typ host "
+ "tcptype active generation 2";
+static const char kSdpTcpPassiveCandidate[] =
+ "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9 typ host "
+ "tcptype passive generation 2";
+static const char kSdpTcpSOCandidate[] =
+ "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9 typ host "
+ "tcptype so generation 2";
+static const char kSdpTcpInvalidCandidate[] =
+ "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9 typ host "
+ "tcptype invalid generation 2";
+
// One candidate reference string.
static const char kSdpOneCandidateOldFormat[] =
@@ -1611,6 +1624,22 @@
EXPECT_EQ(std::string(kRawCandidate), message);
}
+// TODO(mallinath) : Enable this test once WebRTCSdp capable of parsing
+// RFC 6544.
+TEST_F(WebRtcSdpTest, DISABLED_SerializeTcpCandidates) {
+ Candidate candidate(
+ "", ICE_CANDIDATE_COMPONENT_RTP, "tcp",
+ rtc::SocketAddress("192.168.1.5", 9), kCandidatePriority,
+ "", "", LOCAL_PORT_TYPE,
+ "", kCandidateGeneration, kCandidateFoundation1);
+ candidate.set_tcptype(cricket::TCPTYPE_ACTIVE_STR);
+ rtc::scoped_ptr<IceCandidateInterface> jcandidate(
+ new JsepIceCandidate(std::string("audio_content_name"), 0, candidate));
+
+ std::string message = webrtc::SdpSerializeCandidate(*jcandidate);
+ EXPECT_EQ(std::string(kSdpTcpActiveCandidate), message);
+}
+
TEST_F(WebRtcSdpTest, DeserializeSessionDescription) {
JsepSessionDescription jdesc(kDummyString);
// Deserialize
@@ -1897,6 +1926,25 @@
EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
+
+ sdp = kSdpTcpActiveCandidate;
+ EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
+ // Make a cricket::Candidate equivalent to kSdpTcpCandidate string.
+ Candidate candidate(
+ "", ICE_CANDIDATE_COMPONENT_RTP, "tcp",
+ rtc::SocketAddress("192.168.1.5", 9), kCandidatePriority,
+ "", "", LOCAL_PORT_TYPE,
+ "", kCandidateGeneration, kCandidateFoundation1);
+ rtc::scoped_ptr<IceCandidateInterface> jcandidate_template(
+ new JsepIceCandidate(std::string("audio_content_name"), 0, candidate));
+ EXPECT_TRUE(jcandidate.candidate().IsEquivalent(
+ jcandidate_template->candidate()));
+ sdp = kSdpTcpPassiveCandidate;
+ EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
+ sdp = kSdpTcpSOCandidate;
+ EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
+ sdp = kSdpTcpInvalidCandidate;
+ EXPECT_FALSE(SdpDeserializeCandidate(sdp, &jcandidate));
}
// This test verifies the deserialization of candidate-attribute
diff --git a/talk/p2p/base/candidate.h b/talk/p2p/base/candidate.h
index 56174bd..9c1198d 100644
--- a/talk/p2p/base/candidate.h
+++ b/talk/p2p/base/candidate.h
@@ -139,6 +139,10 @@
const rtc::SocketAddress & related_address) {
related_address_ = related_address;
}
+ const std::string& tcptype() const { return tcptype_; }
+ void set_tcptype(const std::string& tcptype){
+ tcptype_ = tcptype;
+ }
// Determines whether this candidate is equivalent to the given one.
bool IsEquivalent(const Candidate& c) const {
@@ -217,6 +221,7 @@
uint32 generation_;
std::string foundation_;
rtc::SocketAddress related_address_;
+ std::string tcptype_;
};
} // namespace cricket
diff --git a/talk/p2p/base/port.cc b/talk/p2p/base/port.cc
index 0d3a5cd..42f7a95 100644
--- a/talk/p2p/base/port.cc
+++ b/talk/p2p/base/port.cc
@@ -147,6 +147,12 @@
return false;
}
+// RFC 6544, TCP candidate encoding rules.
+const int DISCARD_PORT = 9;
+const char TCPTYPE_ACTIVE_STR[] = "active";
+const char TCPTYPE_PASSIVE_STR[] = "passive";
+const char TCPTYPE_SIMOPEN_STR[] = "so";
+
// Foundation: An arbitrary string that is the same for two candidates
// that have the same type, base IP address, protocol (UDP, TCP,
// etc.), and STUN or TURN server. If any of these are different,
@@ -250,26 +256,21 @@
const rtc::SocketAddress& base_address,
const rtc::SocketAddress& related_address,
const std::string& protocol,
- const std::string& type,
- uint32 type_preference,
- bool final) {
- AddAddress(address, base_address, related_address, protocol,
- type, type_preference, 0, final);
-}
-
-void Port::AddAddress(const rtc::SocketAddress& address,
- const rtc::SocketAddress& base_address,
- const rtc::SocketAddress& related_address,
- const std::string& protocol,
+ const std::string& tcptype,
const std::string& type,
uint32 type_preference,
uint32 relay_preference,
bool final) {
+ if (protocol == TCP_PROTOCOL_NAME && type == LOCAL_PORT_TYPE) {
+ ASSERT(!tcptype.empty());
+ }
+
Candidate c;
c.set_id(rtc::CreateRandomString(8));
c.set_component(component_);
c.set_type(type);
c.set_protocol(protocol);
+ c.set_tcptype(tcptype);
c.set_address(address);
c.set_priority(c.GetPriority(type_preference, network_->preference(),
relay_preference));
diff --git a/talk/p2p/base/port.h b/talk/p2p/base/port.h
index 0071a03..a417c0a 100644
--- a/talk/p2p/base/port.h
+++ b/talk/p2p/base/port.h
@@ -61,6 +61,12 @@
extern const char TCP_PROTOCOL_NAME[];
extern const char SSLTCP_PROTOCOL_NAME[];
+// RFC 6544, TCP candidate encoding rules.
+extern const int DISCARD_PORT;
+extern const char TCPTYPE_ACTIVE_STR[];
+extern const char TCPTYPE_PASSIVE_STR[];
+extern const char TCPTYPE_SIMOPEN_STR[];
+
// The length of time we wait before timing out readability on a connection.
const uint32 CONNECTION_READ_TIMEOUT = 30 * 1000; // 30 seconds
@@ -308,18 +314,13 @@
};
void set_type(const std::string& type) { type_ = type; }
- // Fills in the local address of the port.
- void AddAddress(const rtc::SocketAddress& address,
- const rtc::SocketAddress& base_address,
- const rtc::SocketAddress& related_address,
- const std::string& protocol, const std::string& type,
- uint32 type_preference, bool final);
void AddAddress(const rtc::SocketAddress& address,
const rtc::SocketAddress& base_address,
const rtc::SocketAddress& related_address,
- const std::string& protocol, const std::string& type,
- uint32 type_preference, uint32 relay_preference, bool final);
+ const std::string& protocol, const std::string& tcptype,
+ const std::string& type, uint32 type_preference,
+ uint32 relay_preference, bool final);
// Adds the given connection to the list. (Deleting removes them.)
void AddConnection(Connection* conn);
diff --git a/talk/p2p/base/port_unittest.cc b/talk/p2p/base/port_unittest.cc
index e4f37a9..9b9568c 100644
--- a/talk/p2p/base/port_unittest.cc
+++ b/talk/p2p/base/port_unittest.cc
@@ -146,22 +146,22 @@
virtual void PrepareAddress() {
rtc::SocketAddress addr(ip(), min_port());
- AddAddress(addr, addr, rtc::SocketAddress(), "udp", Type(),
- ICE_TYPE_PREFERENCE_HOST, true);
+ AddAddress(addr, addr, rtc::SocketAddress(), "udp", "", Type(),
+ ICE_TYPE_PREFERENCE_HOST, 0, true);
}
// Exposed for testing candidate building.
void AddCandidateAddress(const rtc::SocketAddress& addr) {
- AddAddress(addr, addr, rtc::SocketAddress(), "udp", Type(),
- type_preference_, false);
+ AddAddress(addr, addr, rtc::SocketAddress(), "udp", "", Type(),
+ type_preference_, 0, false);
}
void AddCandidateAddress(const rtc::SocketAddress& addr,
const rtc::SocketAddress& base_address,
const std::string& type,
int type_preference,
bool final) {
- AddAddress(addr, base_address, rtc::SocketAddress(), "udp", type,
- type_preference, final);
+ AddAddress(addr, base_address, rtc::SocketAddress(), "udp", "", type,
+ type_preference, 0, final);
}
virtual Connection* CreateConnection(const Candidate& remote_candidate,
diff --git a/talk/p2p/base/relayport.cc b/talk/p2p/base/relayport.cc
index 78bf65a..9de0897 100644
--- a/talk/p2p/base/relayport.cc
+++ b/talk/p2p/base/relayport.cc
@@ -244,7 +244,8 @@
// This is due to as mapped address stun attribute is used for allocated
// address.
AddAddress(iter->address, iter->address, rtc::SocketAddress(),
- proto_name, RELAY_PORT_TYPE, ICE_TYPE_PREFERENCE_RELAY, false);
+ proto_name, "", RELAY_PORT_TYPE,
+ ICE_TYPE_PREFERENCE_RELAY, 0, false);
}
ready_ = true;
SignalPortComplete(this);
diff --git a/talk/p2p/base/stunport.cc b/talk/p2p/base/stunport.cc
index 57c7850..2ed0fa7 100644
--- a/talk/p2p/base/stunport.cc
+++ b/talk/p2p/base/stunport.cc
@@ -289,8 +289,8 @@
void UDPPort::OnLocalAddressReady(rtc::AsyncPacketSocket* socket,
const rtc::SocketAddress& address) {
AddAddress(address, address, rtc::SocketAddress(),
- UDP_PROTOCOL_NAME, LOCAL_PORT_TYPE,
- ICE_TYPE_PREFERENCE_HOST, false);
+ UDP_PROTOCOL_NAME, "", LOCAL_PORT_TYPE,
+ ICE_TYPE_PREFERENCE_HOST, 0, false);
MaybePrepareStunCandidate();
}
@@ -394,8 +394,8 @@
// address then discarding the stun address.
// For STUN related address is local socket address.
AddAddress(stun_reflected_addr, socket_->GetLocalAddress(),
- socket_->GetLocalAddress(), UDP_PROTOCOL_NAME,
- STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, false);
+ socket_->GetLocalAddress(), UDP_PROTOCOL_NAME, "",
+ STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, 0, false);
}
MaybeSetPortCompleteOrError();
}
diff --git a/talk/p2p/base/tcpport.cc b/talk/p2p/base/tcpport.cc
index f6d9ae6..74d566d 100644
--- a/talk/p2p/base/tcpport.cc
+++ b/talk/p2p/base/tcpport.cc
@@ -81,6 +81,13 @@
return NULL;
}
+ if (address.tcptype() == TCPTYPE_ACTIVE_STR ||
+ (address.tcptype().empty() && address.address().port() == 0)) {
+ // It's active only candidate, we should not try to create connections
+ // for these candidates.
+ return NULL;
+ }
+
// We can't accept TCP connections incoming on other ports
if (origin == ORIGIN_OTHER_PORT)
return NULL;
@@ -115,23 +122,23 @@
if (socket_) {
// If socket isn't bound yet the address will be added in
// OnAddressReady(). Socket may be in the CLOSED state if Listen()
- // failed, we still want ot add the socket address.
+ // failed, we still want to add the socket address.
LOG(LS_VERBOSE) << "Preparing TCP address, current state: "
<< socket_->GetState();
if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND ||
socket_->GetState() == rtc::AsyncPacketSocket::STATE_CLOSED)
AddAddress(socket_->GetLocalAddress(), socket_->GetLocalAddress(),
rtc::SocketAddress(),
- TCP_PROTOCOL_NAME, LOCAL_PORT_TYPE,
- ICE_TYPE_PREFERENCE_HOST_TCP, true);
+ TCP_PROTOCOL_NAME, TCPTYPE_PASSIVE_STR, LOCAL_PORT_TYPE,
+ ICE_TYPE_PREFERENCE_HOST_TCP, 0, true);
} 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, LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP,
- true);
+ TCP_PROTOCOL_NAME, TCPTYPE_ACTIVE_STR, LOCAL_PORT_TYPE,
+ ICE_TYPE_PREFERENCE_HOST_TCP, 0, true);
}
}
@@ -223,9 +230,9 @@
void TCPPort::OnAddressReady(rtc::AsyncPacketSocket* socket,
const rtc::SocketAddress& address) {
- AddAddress(address, address, rtc::SocketAddress(), "tcp",
- LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP,
- true);
+ AddAddress(address, address, rtc::SocketAddress(),
+ TCP_PROTOCOL_NAME, TCPTYPE_PASSIVE_STR, LOCAL_PORT_TYPE,
+ ICE_TYPE_PREFERENCE_HOST_TCP, 0, true);
}
TCPConnection::TCPConnection(TCPPort* port, const Candidate& candidate,
diff --git a/talk/p2p/base/transport.cc b/talk/p2p/base/transport.cc
index 825142a..6ca3628 100644
--- a/talk/p2p/base/transport.cc
+++ b/talk/p2p/base/transport.cc
@@ -34,6 +34,7 @@
#include "talk/p2p/base/constants.h"
#include "talk/p2p/base/sessionmanager.h"
#include "talk/p2p/base/parsing.h"
+#include "talk/p2p/base/port.h"
#include "talk/p2p/base/transportchannelimpl.h"
#include "talk/xmllite/xmlelement.h"
#include "talk/xmpp/constants.h"
@@ -438,11 +439,12 @@
// Disallow all ports below 1024, except for 80 and 443 on public addresses.
int port = cand.address().port();
- if (port == 0) {
+ if (cand.protocol() == TCP_PROTOCOL_NAME &&
+ (cand.tcptype() == TCPTYPE_ACTIVE_STR || port == 0)) {
// Expected for active-only candidates per
// http://tools.ietf.org/html/rfc6544#section-4.5 so no error.
- *error = "";
- return false;
+ // Libjingle clients emit port 0, in "active" mode.
+ return true;
}
if (port < 1024) {
if ((port != 80) && (port != 443)) {
diff --git a/talk/p2p/base/turnport.cc b/talk/p2p/base/turnport.cc
index 7255a2b..3fa6829 100644
--- a/talk/p2p/base/turnport.cc
+++ b/talk/p2p/base/turnport.cc
@@ -526,6 +526,7 @@
address, // Base address.
stun_address, // Related address.
UDP_PROTOCOL_NAME,
+ "", // TCP canddiate type, empty for turn candidates.
RELAY_PORT_TYPE,
GetRelayPreference(server_address_.proto, server_address_.secure),
server_priority_,
diff --git a/talk/p2p/client/connectivitychecker_unittest.cc b/talk/p2p/client/connectivitychecker_unittest.cc
index d1a6525..51bdfef 100644
--- a/talk/p2p/client/connectivitychecker_unittest.cc
+++ b/talk/p2p/client/connectivitychecker_unittest.cc
@@ -73,8 +73,8 @@
// Just set external address and signal that we are done.
virtual void PrepareAddress() {
- AddAddress(kExternalAddr, kExternalAddr, rtc::SocketAddress(), "udp",
- STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, true);
+ AddAddress(kExternalAddr, kExternalAddr, rtc::SocketAddress(), "udp", "",
+ STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, 0, true);
SignalPortComplete(this);
}
};