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
This commit is contained in:
parent
8c01e59424
commit
2d60c5e8bc
@ -168,6 +168,7 @@ static const char kCandidateSrflx[] = "srflx";
|
||||
// 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 @@ bool ParseCandidate(const std::string& message, Candidate* candidate,
|
||||
++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 @@ bool ParseCandidate(const std::string& message, Candidate* candidate,
|
||||
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 @@ void BuildCandidate(const std::vector<Candidate>& candidates,
|
||||
|
||||
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 @@ void BuildCandidate(const std::vector<Candidate>& candidates,
|
||||
<< 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();
|
||||
|
||||
|
@ -354,6 +354,19 @@ static const char kRawCandidate[] =
|
||||
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 @@ TEST_F(WebRtcSdpTest, SerializeCandidates) {
|
||||
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 @@ TEST_F(WebRtcSdpTest, DeserializeCandidate) {
|
||||
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
|
||||
|
@ -139,6 +139,10 @@ class Candidate {
|
||||
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 @@ class Candidate {
|
||||
uint32 generation_;
|
||||
std::string foundation_;
|
||||
rtc::SocketAddress related_address_;
|
||||
std::string tcptype_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
@ -147,6 +147,12 @@ bool StringToProto(const char* value, ProtocolType* proto) {
|
||||
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 @@ void Port::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) {
|
||||
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));
|
||||
|
@ -61,6 +61,12 @@ extern const char UDP_PROTOCOL_NAME[];
|
||||
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 @@ class Port : public PortInterface, public rtc::MessageHandler,
|
||||
};
|
||||
|
||||
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);
|
||||
|
@ -146,22 +146,22 @@ class TestPort : public Port {
|
||||
|
||||
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,
|
||||
|
@ -244,7 +244,8 @@ void RelayPort::SetReady() {
|
||||
// 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);
|
||||
|
@ -289,8 +289,8 @@ int UDPPort::GetError() {
|
||||
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 @@ void UDPPort::OnStunBindingRequestSucceeded(
|
||||
// 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();
|
||||
}
|
||||
|
@ -81,6 +81,13 @@ Connection* TCPPort::CreateConnection(const Candidate& address,
|
||||
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 @@ void TCPPort::PrepareAddress() {
|
||||
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::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
|
||||
|
||||
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,
|
||||
|
@ -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 @@ bool Transport::VerifyCandidate(const Candidate& cand, std::string* error) {
|
||||
|
||||
// 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)) {
|
||||
|
@ -526,6 +526,7 @@ void TurnPort::OnAllocateSuccess(const rtc::SocketAddress& address,
|
||||
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_,
|
||||
|
@ -73,8 +73,8 @@ class FakeStunPort : public StunPort {
|
||||
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user