Fix the duplicated candidate problem when using multiple STUN servers.
BUG=3723
R=pthatcher@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/26469004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@7312 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/app/webrtc/webrtcsession_unittest.cc b/talk/app/webrtc/webrtcsession_unittest.cc
index 7aa87fb..388fff7 100644
--- a/talk/app/webrtc/webrtcsession_unittest.cc
+++ b/talk/app/webrtc/webrtcsession_unittest.cc
@@ -309,7 +309,8 @@
ss_scope_(fss_.get()),
stun_socket_addr_(rtc::SocketAddress(kStunAddrHost,
cricket::STUN_SERVER_PORT)),
- stun_server_(Thread::Current(), stun_socket_addr_),
+ stun_server_(cricket::TestStunServer::Create(Thread::Current(),
+ stun_socket_addr_)),
turn_server_(Thread::Current(), kTurnUdpIntAddr, kTurnUdpExtAddr),
mediastream_signaling_(channel_manager_.get()),
ice_type_(PeerConnectionInterface::kAll) {
@@ -1109,7 +1110,7 @@
rtc::scoped_ptr<rtc::FirewallSocketServer> fss_;
rtc::SocketServerScope ss_scope_;
rtc::SocketAddress stun_socket_addr_;
- cricket::TestStunServer stun_server_;
+ rtc::scoped_ptr<cricket::TestStunServer> stun_server_;
cricket::TestTurnServer turn_server_;
rtc::FakeNetworkManager network_manager_;
rtc::scoped_ptr<cricket::BasicPortAllocator> allocator_;
diff --git a/talk/p2p/base/p2ptransportchannel_unittest.cc b/talk/p2p/base/p2ptransportchannel_unittest.cc
index 0b7f882..9bfa84f 100644
--- a/talk/p2p/base/p2ptransportchannel_unittest.cc
+++ b/talk/p2p/base/p2ptransportchannel_unittest.cc
@@ -139,7 +139,7 @@
nss_(new rtc::NATSocketServer(vss_.get())),
ss_(new rtc::FirewallSocketServer(nss_.get())),
ss_scope_(ss_.get()),
- stun_server_(main_, kStunAddr),
+ stun_server_(cricket::TestStunServer::Create(main_, kStunAddr)),
turn_server_(main_, kTurnUdpIntAddr, kTurnUdpExtAddr),
relay_server_(main_, kRelayUdpIntAddr, kRelayUdpExtAddr,
kRelayTcpIntAddr, kRelayTcpExtAddr,
@@ -745,7 +745,7 @@
rtc::scoped_ptr<rtc::NATSocketServer> nss_;
rtc::scoped_ptr<rtc::FirewallSocketServer> ss_;
rtc::SocketServerScope ss_scope_;
- cricket::TestStunServer stun_server_;
+ rtc::scoped_ptr<cricket::TestStunServer> stun_server_;
cricket::TestTurnServer turn_server_;
cricket::TestRelayServer relay_server_;
rtc::SocksProxyServer socks_server1_;
diff --git a/talk/p2p/base/port_unittest.cc b/talk/p2p/base/port_unittest.cc
index 04becfc..a44a12e 100644
--- a/talk/p2p/base/port_unittest.cc
+++ b/talk/p2p/base/port_unittest.cc
@@ -343,7 +343,7 @@
nat_factory2_(ss_.get(), kNatAddr2),
nat_socket_factory1_(&nat_factory1_),
nat_socket_factory2_(&nat_factory2_),
- stun_server_(main_, kStunAddr),
+ stun_server_(TestStunServer::Create(main_, kStunAddr)),
turn_server_(main_, kTurnUdpIntAddr, kTurnUdpExtAddr),
relay_server_(main_, kRelayUdpIntAddr, kRelayUdpExtAddr,
kRelayTcpIntAddr, kRelayTcpExtAddr,
@@ -617,7 +617,7 @@
rtc::NATSocketFactory nat_factory2_;
rtc::BasicPacketSocketFactory nat_socket_factory1_;
rtc::BasicPacketSocketFactory nat_socket_factory2_;
- TestStunServer stun_server_;
+ scoped_ptr<TestStunServer> stun_server_;
TestTurnServer turn_server_;
TestRelayServer relay_server_;
std::string username_;
diff --git a/talk/p2p/base/stunport.cc b/talk/p2p/base/stunport.cc
index a178eb4..a185a35 100644
--- a/talk/p2p/base/stunport.cc
+++ b/talk/p2p/base/stunport.cc
@@ -389,10 +389,12 @@
}
bind_request_succeeded_servers_.insert(stun_server_addr);
- if (!SharedSocket() || stun_reflected_addr != socket_->GetLocalAddress()) {
- // If socket is shared and |stun_reflected_addr| is equal to local socket
- // address then discarding the stun address.
- // For STUN related address is local socket address.
+ // If socket is shared and |stun_reflected_addr| is equal to local socket
+ // address, or if the same address has been added by another STUN server,
+ // then discarding the stun address.
+ // For STUN, related address is the local socket address.
+ if ((!SharedSocket() || stun_reflected_addr != socket_->GetLocalAddress()) &&
+ !HasCandidateWithAddress(stun_reflected_addr)) {
AddAddress(stun_reflected_addr, socket_->GetLocalAddress(),
socket_->GetLocalAddress(), UDP_PROTOCOL_NAME, "",
STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, 0, false);
@@ -443,4 +445,14 @@
PLOG(LERROR, socket_->GetError()) << "sendto";
}
+bool UDPPort::HasCandidateWithAddress(const rtc::SocketAddress& addr) const {
+ const std::vector<Candidate>& existing_candidates = Candidates();
+ std::vector<Candidate>::const_iterator it = existing_candidates.begin();
+ for (; it != existing_candidates.end(); ++it) {
+ if (it->address() == addr)
+ return true;
+ }
+ return false;
+}
+
} // namespace cricket
diff --git a/talk/p2p/base/stunport.h b/talk/p2p/base/stunport.h
index b3b6d5b..1fc1f64 100644
--- a/talk/p2p/base/stunport.h
+++ b/talk/p2p/base/stunport.h
@@ -194,6 +194,8 @@
// changed to SignalPortReady.
void MaybeSetPortCompleteOrError();
+ bool HasCandidateWithAddress(const rtc::SocketAddress& addr) const;
+
ServerAddresses server_addresses_;
ServerAddresses bind_request_succeeded_servers_;
ServerAddresses bind_request_failed_servers_;
diff --git a/talk/p2p/base/stunport_unittest.cc b/talk/p2p/base/stunport_unittest.cc
index 6506181..c5abc48 100644
--- a/talk/p2p/base/stunport_unittest.cc
+++ b/talk/p2p/base/stunport_unittest.cc
@@ -61,9 +61,9 @@
ss_scope_(ss_.get()),
network_("unittest", "unittest", rtc::IPAddress(INADDR_ANY), 32),
socket_factory_(rtc::Thread::Current()),
- stun_server_1_(new cricket::TestStunServer(
+ stun_server_1_(cricket::TestStunServer::Create(
rtc::Thread::Current(), kStunAddr1)),
- stun_server_2_(new cricket::TestStunServer(
+ stun_server_2_(cricket::TestStunServer::Create(
rtc::Thread::Current(), kStunAddr2)),
done_(false), error_(false), stun_keepalive_delay_(0) {
}
@@ -150,6 +150,13 @@
stun_keepalive_delay_ = delay;
}
+ cricket::TestStunServer* stun_server_1() {
+ return stun_server_1_.get();
+ }
+ cricket::TestStunServer* stun_server_2() {
+ return stun_server_2_.get();
+ }
+
private:
rtc::scoped_ptr<rtc::PhysicalSocketServer> pss_;
rtc::scoped_ptr<rtc::VirtualSocketServer> ss_;
@@ -253,8 +260,8 @@
// No crash is success.
}
-// Test that candidates can be allocated for multiple STUN servers.
-TEST_F(StunPortTest, TestMultipleGoodStunServers) {
+// Test that the same address is added only once if two STUN servers are in use.
+TEST_F(StunPortTest, TestNoDuplicatedAddressWithTwoStunServers) {
ServerAddresses stun_servers;
stun_servers.insert(kStunAddr1);
stun_servers.insert(kStunAddr2);
@@ -262,7 +269,7 @@
EXPECT_EQ("stun", port()->Type());
PrepareAddress();
EXPECT_TRUE_WAIT(done(), kTimeoutMs);
- EXPECT_EQ(2U, port()->Candidates().size());
+ EXPECT_EQ(1U, port()->Candidates().size());
}
// Test that candidates can be allocated for multiple STUN servers, one of which
@@ -277,3 +284,21 @@
EXPECT_TRUE_WAIT(done(), kTimeoutMs);
EXPECT_EQ(1U, port()->Candidates().size());
}
+
+// Test that two candidates are allocated if the two STUN servers return
+// different mapped addresses.
+TEST_F(StunPortTest, TestTwoCandidatesWithTwoStunServersAcrossNat) {
+ const SocketAddress kStunMappedAddr1("77.77.77.77", 0);
+ const SocketAddress kStunMappedAddr2("88.77.77.77", 0);
+ stun_server_1()->set_fake_stun_addr(kStunMappedAddr1);
+ stun_server_2()->set_fake_stun_addr(kStunMappedAddr2);
+
+ ServerAddresses stun_servers;
+ stun_servers.insert(kStunAddr1);
+ stun_servers.insert(kStunAddr2);
+ CreateStunPort(stun_servers);
+ EXPECT_EQ("stun", port()->Type());
+ PrepareAddress();
+ EXPECT_TRUE_WAIT(done(), kTimeoutMs);
+ EXPECT_EQ(2U, port()->Candidates().size());
+}
diff --git a/talk/p2p/base/stunserver.cc b/talk/p2p/base/stunserver.cc
index d9633f0..df428f9 100644
--- a/talk/p2p/base/stunserver.cc
+++ b/talk/p2p/base/stunserver.cc
@@ -68,19 +68,7 @@
void StunServer::OnBindingRequest(
StunMessage* msg, const rtc::SocketAddress& remote_addr) {
StunMessage response;
- response.SetType(STUN_BINDING_RESPONSE);
- response.SetTransactionID(msg->transaction_id());
-
- // Tell the user the address that we received their request from.
- StunAddressAttribute* mapped_addr;
- if (!msg->IsLegacy()) {
- mapped_addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
- } else {
- mapped_addr = StunAttribute::CreateXorAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
- }
- mapped_addr->SetAddress(remote_addr);
- response.AddAttribute(mapped_addr);
-
+ GetStunBindReqponse(msg, remote_addr, &response);
SendResponse(response, remote_addr);
}
@@ -108,4 +96,21 @@
LOG_ERR(LS_ERROR) << "sendto";
}
+void StunServer::GetStunBindReqponse(StunMessage* request,
+ const rtc::SocketAddress& remote_addr,
+ StunMessage* response) const {
+ response->SetType(STUN_BINDING_RESPONSE);
+ response->SetTransactionID(request->transaction_id());
+
+ // Tell the user the address that we received their request from.
+ StunAddressAttribute* mapped_addr;
+ if (!request->IsLegacy()) {
+ mapped_addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
+ } else {
+ mapped_addr = StunAttribute::CreateXorAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
+ }
+ mapped_addr->SetAddress(remote_addr);
+ response->AddAttribute(mapped_addr);
+}
+
} // namespace cricket
diff --git a/talk/p2p/base/stunserver.h b/talk/p2p/base/stunserver.h
index 1e14408..a141e5b 100644
--- a/talk/p2p/base/stunserver.h
+++ b/talk/p2p/base/stunserver.h
@@ -51,7 +51,7 @@
const rtc::PacketTime& packet_time);
// Handlers for the different types of STUN/TURN requests:
- void OnBindingRequest(StunMessage* msg,
+ virtual void OnBindingRequest(StunMessage* msg,
const rtc::SocketAddress& addr);
void OnAllocateRequest(StunMessage* msg,
const rtc::SocketAddress& addr);
@@ -69,6 +69,11 @@
void SendResponse(const StunMessage& msg,
const rtc::SocketAddress& addr);
+ // A helper method to compose a STUN binding response.
+ void GetStunBindReqponse(StunMessage* request,
+ const rtc::SocketAddress& remote_addr,
+ StunMessage* response) const;
+
private:
rtc::scoped_ptr<rtc::AsyncUDPSocket> socket_;
};
diff --git a/talk/p2p/base/teststunserver.h b/talk/p2p/base/teststunserver.h
index 6f85009..840150b 100644
--- a/talk/p2p/base/teststunserver.h
+++ b/talk/p2p/base/teststunserver.h
@@ -35,19 +35,39 @@
namespace cricket {
// A test STUN server. Useful for unit tests.
-class TestStunServer {
+class TestStunServer : StunServer {
public:
- TestStunServer(rtc::Thread* thread,
- const rtc::SocketAddress& addr)
- : socket_(thread->socketserver()->CreateAsyncSocket(addr.family(),
- SOCK_DGRAM)),
- udp_socket_(rtc::AsyncUDPSocket::Create(socket_, addr)),
- server_(udp_socket_) {
+ static TestStunServer* Create(rtc::Thread* thread,
+ const rtc::SocketAddress& addr) {
+ rtc::AsyncSocket* socket =
+ thread->socketserver()->CreateAsyncSocket(addr.family(), SOCK_DGRAM);
+ rtc::AsyncUDPSocket* udp_socket =
+ rtc::AsyncUDPSocket::Create(socket, addr);
+
+ return new TestStunServer(udp_socket);
}
+
+ // Set a fake STUN address to return to the client.
+ void set_fake_stun_addr(const rtc::SocketAddress& addr) {
+ fake_stun_addr_ = addr;
+ }
+
private:
- rtc::AsyncSocket* socket_;
- rtc::AsyncUDPSocket* udp_socket_;
- cricket::StunServer server_;
+ explicit TestStunServer(rtc::AsyncUDPSocket* socket) : StunServer(socket) {}
+
+ void OnBindingRequest(StunMessage* msg,
+ const rtc::SocketAddress& remote_addr) OVERRIDE {
+ if (fake_stun_addr_.IsNil()) {
+ StunServer::OnBindingRequest(msg, remote_addr);
+ } else {
+ StunMessage response;
+ GetStunBindReqponse(msg, fake_stun_addr_, &response);
+ SendResponse(response, remote_addr);
+ }
+ }
+
+ private:
+ rtc::SocketAddress fake_stun_addr_;
};
} // namespace cricket
diff --git a/talk/p2p/client/portallocator_unittest.cc b/talk/p2p/client/portallocator_unittest.cc
index e793064..b1feca2 100644
--- a/talk/p2p/client/portallocator_unittest.cc
+++ b/talk/p2p/client/portallocator_unittest.cc
@@ -112,7 +112,8 @@
ss_scope_(fss_.get()),
nat_factory_(vss_.get(), kNatAddr),
nat_socket_factory_(&nat_factory_),
- stun_server_(Thread::Current(), kStunAddr),
+ stun_server_(cricket::TestStunServer::Create(Thread::Current(),
+ kStunAddr)),
relay_server_(Thread::Current(), kRelayUdpIntAddr, kRelayUdpExtAddr,
kRelayTcpIntAddr, kRelayTcpExtAddr,
kRelaySslTcpIntAddr, kRelaySslTcpExtAddr),
@@ -280,7 +281,7 @@
rtc::scoped_ptr<rtc::NATServer> nat_server_;
rtc::NATSocketFactory nat_factory_;
rtc::BasicPacketSocketFactory nat_socket_factory_;
- cricket::TestStunServer stun_server_;
+ rtc::scoped_ptr<cricket::TestStunServer> stun_server_;
cricket::TestRelayServer relay_server_;
cricket::TestTurnServer turn_server_;
rtc::FakeNetworkManager network_manager_;