Enable shared socket for TurnPort.
In AllocationSequence::OnReadPacket, we now hand the packet to both the TurnPort and StunPort if the remote address matches the server address.
TESTED=AppRtc loopback call generates both turn and stun candidates.
BUG=1746
R=juberti@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/19189004
git-svn-id: http://webrtc.googlecode.com/svn/trunk/talk@7138 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/p2p/base/portallocator.h b/p2p/base/portallocator.h
index a3ff031..5bc389e 100644
--- a/p2p/base/portallocator.h
+++ b/p2p/base/portallocator.h
@@ -55,7 +55,6 @@
PORTALLOCATOR_ENABLE_SHARED_UFRAG = 0x80,
PORTALLOCATOR_ENABLE_SHARED_SOCKET = 0x100,
PORTALLOCATOR_ENABLE_STUN_RETRANSMIT_ATTRIBUTE = 0x200,
- PORTALLOCATOR_ENABLE_TURN_SHARED_SOCKET = 0x400,
};
const uint32 kDefaultPortAllocatorFlags = 0;
@@ -170,7 +169,6 @@
uint32 step_delay() const { return step_delay_; }
void set_step_delay(uint32 delay) {
- ASSERT(delay >= kMinimumStepDelay);
step_delay_ = delay;
}
diff --git a/p2p/base/stunport.h b/p2p/base/stunport.h
index 0a49b67..b3b6d5b 100644
--- a/p2p/base/stunport.h
+++ b/p2p/base/stunport.h
@@ -82,7 +82,7 @@
return socket_->GetLocalAddress();
}
- const ServerAddresses server_addresses() const {
+ const ServerAddresses& server_addresses() const {
return server_addresses_;
}
void
diff --git a/p2p/client/basicportallocator.cc b/p2p/client/basicportallocator.cc
index 6c184b4..eda0b4b 100644
--- a/p2p/client/basicportallocator.cc
+++ b/p2p/client/basicportallocator.cc
@@ -149,9 +149,6 @@
const rtc::PacketTime& packet_time);
void OnPortDestroyed(PortInterface* port);
- void OnResolvedTurnServerAddress(
- TurnPort* port, const rtc::SocketAddress& server_address,
- const rtc::SocketAddress& resolved_server_address);
BasicPortAllocatorSession* session_;
rtc::Network* network_;
@@ -163,8 +160,7 @@
rtc::scoped_ptr<rtc::AsyncPacketSocket> udp_socket_;
// There will be only one udp port per AllocationSequence.
UDPPort* udp_port_;
- // Keeping a map for turn ports keyed with server addresses.
- std::map<rtc::SocketAddress, Port*> turn_ports_;
+ std::vector<TurnPort*> turn_ports_;
int phase_;
};
@@ -1054,26 +1050,15 @@
// don't pass shared socket for ports which will create TCP sockets.
// TODO(mallinath) - Enable shared socket mode for TURN ports. Disabled
// due to webrtc bug https://code.google.com/p/webrtc/issues/detail?id=3537
- if (IsFlagSet(PORTALLOCATOR_ENABLE_TURN_SHARED_SOCKET) &&
+ if (IsFlagSet(PORTALLOCATOR_ENABLE_SHARED_SOCKET) &&
relay_port->proto == PROTO_UDP) {
port = TurnPort::Create(session_->network_thread(),
session_->socket_factory(),
network_, udp_socket_.get(),
session_->username(), session_->password(),
*relay_port, config.credentials, config.priority);
- // If we are using shared socket for TURN and udp ports, we need to
- // find a way to demux the packets to the correct port when received.
- // Mapping against server_address is one way of doing this. When packet
- // is received the remote_address will be checked against the map.
- // If server address is not resolved, a signal will be sent from the port
- // after the address is resolved. The map entry will updated with the
- // resolved address when the signal is received from the port.
- if ((*relay_port).address.IsUnresolved()) {
- // If server address is not resolved then listen for signal from port.
- port->SignalResolvedServerAddress.connect(
- this, &AllocationSequence::OnResolvedTurnServerAddress);
- }
- turn_ports_[(*relay_port).address] = port;
+
+ turn_ports_.push_back(port);
// Listen to the port destroyed signal, to allow AllocationSequence to
// remove entrt from it's map.
port->SignalDestroyed.connect(this, &AllocationSequence::OnPortDestroyed);
@@ -1097,51 +1082,45 @@
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
ASSERT(socket == udp_socket_.get());
- // If the packet is received from one of the TURN server in the config, then
- // pass down the packet to that port, otherwise it will be handed down to
- // the local udp port.
- Port* port = NULL;
- std::map<rtc::SocketAddress, Port*>::iterator iter =
- turn_ports_.find(remote_addr);
- if (iter != turn_ports_.end()) {
- port = iter->second;
- } else if (udp_port_) {
- port = udp_port_;
+
+ bool turn_port_found = false;
+
+ // Try to find the TurnPort that matches the remote address. Note that the
+ // message could be a STUN binding response if the TURN server is also used as
+ // a STUN server. We don't want to parse every message here to check if it is
+ // a STUN binding response, so we pass the message to TurnPort regardless of
+ // the message type. The TurnPort will just ignore the message since it will
+ // not find any request by transaction ID.
+ for (std::vector<TurnPort*>::const_iterator it = turn_ports_.begin();
+ it != turn_ports_.end(); ++it) {
+ TurnPort* port = *it;
+ if (port->server_address().address == remote_addr) {
+ port->HandleIncomingPacket(socket, data, size, remote_addr, packet_time);
+ turn_port_found = true;
+ break;
+ }
}
- ASSERT(port != NULL);
- if (port) {
- port->HandleIncomingPacket(socket, data, size, remote_addr, packet_time);
+
+ if (udp_port_) {
+ const ServerAddresses& stun_servers = udp_port_->server_addresses();
+
+ // Pass the packet to the UdpPort if there is no matching TurnPort, or if
+ // the TURN server is also a STUN server.
+ if (!turn_port_found ||
+ stun_servers.find(remote_addr) != stun_servers.end()) {
+ udp_port_->HandleIncomingPacket(
+ socket, data, size, remote_addr, packet_time);
+ }
}
}
void AllocationSequence::OnPortDestroyed(PortInterface* port) {
if (udp_port_ == port) {
udp_port_ = NULL;
- } else {
- std::map<rtc::SocketAddress, Port*>::iterator iter;
- for (iter = turn_ports_.begin(); iter != turn_ports_.end(); ++iter) {
- if (iter->second == port) {
- turn_ports_.erase(iter);
- break;
- }
- }
- }
-}
-
-void AllocationSequence::OnResolvedTurnServerAddress(
- TurnPort* port, const rtc::SocketAddress& server_address,
- const rtc::SocketAddress& resolved_server_address) {
- std::map<rtc::SocketAddress, Port*>::iterator iter;
- iter = turn_ports_.find(server_address);
- if (iter == turn_ports_.end()) {
- LOG(LS_INFO) << "TurnPort entry is not found in the map.";
return;
}
- ASSERT(iter->second == port);
- // Remove old entry and then insert using the resolved address as key.
- turn_ports_.erase(iter);
- turn_ports_[resolved_server_address] = port;
+ turn_ports_.erase(std::find(turn_ports_.begin(), turn_ports_.end(), port));
}
// PortConfiguration
diff --git a/p2p/client/portallocator_unittest.cc b/p2p/client/portallocator_unittest.cc
index c383032..e793064 100644
--- a/p2p/client/portallocator_unittest.cc
+++ b/p2p/client/portallocator_unittest.cc
@@ -133,9 +133,32 @@
bool SetPortRange(int min_port, int max_port) {
return allocator_->SetPortRange(min_port, max_port);
}
- rtc::NATServer* CreateNatServer(const SocketAddress& addr,
- rtc::NATType type) {
- return new rtc::NATServer(type, vss_.get(), addr, vss_.get(), addr);
+ void ResetWithNatServer(const rtc::SocketAddress& stun_server) {
+ nat_server_.reset(new rtc::NATServer(
+ rtc::NAT_OPEN_CONE, vss_.get(), kNatAddr, vss_.get(), kNatAddr));
+
+ ServerAddresses stun_servers;
+ stun_servers.insert(stun_server);
+ allocator_.reset(new cricket::BasicPortAllocator(
+ &network_manager_, &nat_socket_factory_, stun_servers));
+ allocator().set_step_delay(cricket::kMinimumStepDelay);
+ }
+
+ void AddTurnServers(const rtc::SocketAddress& udp_turn,
+ const rtc::SocketAddress& tcp_turn) {
+ cricket::RelayServerConfig relay_server(cricket::RELAY_TURN);
+ cricket::RelayCredentials credentials(kTurnUsername, kTurnPassword);
+ relay_server.credentials = credentials;
+
+ if (!udp_turn.IsNil()) {
+ relay_server.ports.push_back(cricket::ProtocolAddress(
+ kTurnUdpIntAddr, cricket::PROTO_UDP, false));
+ }
+ if (!tcp_turn.IsNil()) {
+ relay_server.ports.push_back(cricket::ProtocolAddress(
+ kTurnTcpIntAddr, cricket::PROTO_TCP, false));
+ }
+ allocator_->AddRelay(relay_server);
}
bool CreateSession(int component) {
@@ -254,6 +277,7 @@
rtc::scoped_ptr<rtc::VirtualSocketServer> vss_;
rtc::scoped_ptr<rtc::FirewallSocketServer> fss_;
rtc::SocketServerScope ss_scope_;
+ rtc::scoped_ptr<rtc::NATServer> nat_server_;
rtc::NATSocketFactory nat_factory_;
rtc::BasicPacketSocketFactory nat_socket_factory_;
cricket::TestStunServer stun_server_;
@@ -580,13 +604,8 @@
// Host is behind the NAT.
TEST_F(PortAllocatorTest, TestCandidateFilterWithReflexiveOnly) {
AddInterface(kPrivateAddr);
- rtc::scoped_ptr<rtc::NATServer> nat_server(
- CreateNatServer(kNatAddr, rtc::NAT_OPEN_CONE));
- ServerAddresses stun_servers;
- stun_servers.insert(kStunAddr);
- allocator_.reset(new cricket::BasicPortAllocator(
- &network_manager_, &nat_socket_factory_, stun_servers));
- allocator().set_step_delay(cricket::kMinimumStepDelay);
+ ResetWithNatServer(kStunAddr);
+
allocator().set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET);
allocator().set_candidate_filter(cricket::CF_REFLEXIVE);
@@ -771,13 +790,8 @@
// local candidates as client behind a nat.
TEST_F(PortAllocatorTest, TestSharedSocketWithNat) {
AddInterface(kClientAddr);
- rtc::scoped_ptr<rtc::NATServer> nat_server(
- CreateNatServer(kNatAddr, rtc::NAT_OPEN_CONE));
- ServerAddresses stun_servers;
- stun_servers.insert(kStunAddr);
- allocator_.reset(new cricket::BasicPortAllocator(
- &network_manager_, &nat_socket_factory_, stun_servers));
- allocator_->set_step_delay(cricket::kMinimumStepDelay);
+ ResetWithNatServer(kStunAddr);
+
allocator_->set_flags(allocator().flags() |
cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET);
@@ -799,14 +813,8 @@
turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
AddInterface(kClientAddr);
allocator_.reset(new cricket::BasicPortAllocator(&network_manager_));
- cricket::RelayServerConfig relay_server(cricket::RELAY_TURN);
- cricket::RelayCredentials credentials(kTurnUsername, kTurnPassword);
- relay_server.credentials = credentials;
- relay_server.ports.push_back(cricket::ProtocolAddress(
- kTurnUdpIntAddr, cricket::PROTO_UDP, false));
- relay_server.ports.push_back(cricket::ProtocolAddress(
- kTurnTcpIntAddr, cricket::PROTO_TCP, false));
- allocator_->AddRelay(relay_server);
+
+ AddTurnServers(kTurnUdpIntAddr, kTurnTcpIntAddr);
allocator_->set_step_delay(cricket::kMinimumStepDelay);
allocator_->set_flags(allocator().flags() |
@@ -863,20 +871,10 @@
// stun and turn candidates.
TEST_F(PortAllocatorTest, TestSharedSocketWithNatUsingTurn) {
AddInterface(kClientAddr);
- rtc::scoped_ptr<rtc::NATServer> nat_server(
- CreateNatServer(kNatAddr, rtc::NAT_OPEN_CONE));
- ServerAddresses stun_servers;
- stun_servers.insert(kStunAddr);
- allocator_.reset(new cricket::BasicPortAllocator(
- &network_manager_, &nat_socket_factory_, stun_servers));
- cricket::RelayServerConfig relay_server(cricket::RELAY_TURN);
- cricket::RelayCredentials credentials(kTurnUsername, kTurnPassword);
- relay_server.credentials = credentials;
- relay_server.ports.push_back(cricket::ProtocolAddress(
- kTurnUdpIntAddr, cricket::PROTO_UDP, false));
- allocator_->AddRelay(relay_server);
+ ResetWithNatServer(kStunAddr);
- allocator_->set_step_delay(cricket::kMinimumStepDelay);
+ AddTurnServers(kTurnUdpIntAddr, rtc::SocketAddress());
+
allocator_->set_flags(allocator().flags() |
cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET |
@@ -902,6 +900,45 @@
EXPECT_EQ(1U, ports_[1]->Candidates().size());
}
+// Test that when PORTALLOCATOR_ENABLE_SHARED_SOCKET is enabled and the TURN
+// server is also used as the STUN server, we should get 'local', 'stun', and
+// 'relay' candidates.
+TEST_F(PortAllocatorTest, TestSharedSocketWithNatUsingTurnAsStun) {
+ AddInterface(kClientAddr);
+ ResetWithNatServer(kTurnUdpIntAddr);
+ AddTurnServers(kTurnUdpIntAddr, rtc::SocketAddress());
+
+ // Must set the step delay to 0 to make sure the relay allocation phase is
+ // started before the STUN candidates are obtained, so that the STUN binding
+ // response is processed when both StunPort and TurnPort exist to reproduce
+ // webrtc issue 3537.
+ allocator_->set_step_delay(0);
+ allocator_->set_flags(allocator().flags() |
+ cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
+ cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET |
+ cricket::PORTALLOCATOR_DISABLE_TCP);
+
+ EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+ session_->StartGettingPorts();
+
+ ASSERT_EQ_WAIT(3U, candidates_.size(), kDefaultAllocationTimeout);
+ EXPECT_PRED5(CheckCandidate, candidates_[0],
+ cricket::ICE_CANDIDATE_COMPONENT_RTP, "local", "udp", kClientAddr);
+ EXPECT_PRED5(CheckCandidate, candidates_[1],
+ cricket::ICE_CANDIDATE_COMPONENT_RTP, "stun", "udp",
+ rtc::SocketAddress(kNatAddr.ipaddr(), 0));
+ EXPECT_PRED5(CheckCandidate, candidates_[2],
+ cricket::ICE_CANDIDATE_COMPONENT_RTP, "relay", "udp",
+ rtc::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0));
+ EXPECT_EQ(candidates_[2].related_address(), candidates_[1].address());
+
+ EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout);
+ EXPECT_EQ(3U, candidates_.size());
+ // Local port will be created first and then TURN port.
+ EXPECT_EQ(2U, ports_[0]->Candidates().size());
+ EXPECT_EQ(1U, ports_[1]->Candidates().size());
+}
+
// This test verifies when PORTALLOCATOR_ENABLE_SHARED_SOCKET flag is enabled
// and fail to generate STUN candidate, local UDP candidate is generated
// properly.