diff options
Diffstat (limited to 'webrtc/p2p/base/turnport_unittest.cc')
-rw-r--r-- | webrtc/p2p/base/turnport_unittest.cc | 816 |
1 files changed, 816 insertions, 0 deletions
diff --git a/webrtc/p2p/base/turnport_unittest.cc b/webrtc/p2p/base/turnport_unittest.cc new file mode 100644 index 0000000000..724485ddde --- /dev/null +++ b/webrtc/p2p/base/turnport_unittest.cc @@ -0,0 +1,816 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#if defined(WEBRTC_POSIX) +#include <dirent.h> +#endif + +#include "webrtc/p2p/base/basicpacketsocketfactory.h" +#include "webrtc/p2p/base/constants.h" +#include "webrtc/p2p/base/tcpport.h" +#include "webrtc/p2p/base/testturnserver.h" +#include "webrtc/p2p/base/turnport.h" +#include "webrtc/p2p/base/udpport.h" +#include "webrtc/base/asynctcpsocket.h" +#include "webrtc/base/buffer.h" +#include "webrtc/base/dscp.h" +#include "webrtc/base/firewallsocketserver.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/helpers.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/socketaddress.h" +#include "webrtc/base/ssladapter.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/virtualsocketserver.h" + +using rtc::SocketAddress; +using cricket::Connection; +using cricket::Port; +using cricket::PortInterface; +using cricket::TurnPort; +using cricket::UDPPort; + +static const SocketAddress kLocalAddr1("11.11.11.11", 0); +static const SocketAddress kLocalAddr2("22.22.22.22", 0); +static const SocketAddress kLocalIPv6Addr( + "2401:fa00:4:1000:be30:5bff:fee5:c3", 0); +static const SocketAddress kTurnUdpIntAddr("99.99.99.3", + cricket::TURN_SERVER_PORT); +static const SocketAddress kTurnTcpIntAddr("99.99.99.4", + cricket::TURN_SERVER_PORT); +static const SocketAddress kTurnUdpExtAddr("99.99.99.5", 0); +static const SocketAddress kTurnAlternateIntAddr("99.99.99.6", + cricket::TURN_SERVER_PORT); +static const SocketAddress kTurnIntAddr("99.99.99.7", + cricket::TURN_SERVER_PORT); +static const SocketAddress kTurnIPv6IntAddr( + "2400:4030:2:2c00:be30:abcd:efab:cdef", + cricket::TURN_SERVER_PORT); +static const SocketAddress kTurnUdpIPv6IntAddr( + "2400:4030:1:2c00:be30:abcd:efab:cdef", cricket::TURN_SERVER_PORT); +static const SocketAddress kTurnUdpIPv6ExtAddr( + "2620:0:1000:1b03:2e41:38ff:fea6:f2a4", 0); + +static const char kIceUfrag1[] = "TESTICEUFRAG0001"; +static const char kIceUfrag2[] = "TESTICEUFRAG0002"; +static const char kIcePwd1[] = "TESTICEPWD00000000000001"; +static const char kIcePwd2[] = "TESTICEPWD00000000000002"; +static const char kTurnUsername[] = "test"; +static const char kTurnPassword[] = "test"; +static const char kTestOrigin[] = "http://example.com"; +static const unsigned int kTimeout = 1000; + +static const cricket::ProtocolAddress kTurnUdpProtoAddr( + kTurnUdpIntAddr, cricket::PROTO_UDP); +static const cricket::ProtocolAddress kTurnTcpProtoAddr( + kTurnTcpIntAddr, cricket::PROTO_TCP); +static const cricket::ProtocolAddress kTurnUdpIPv6ProtoAddr( + kTurnUdpIPv6IntAddr, cricket::PROTO_UDP); + +static const unsigned int MSG_TESTFINISH = 0; + +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) +static int GetFDCount() { + struct dirent *dp; + int fd_count = 0; + DIR *dir = opendir("/proc/self/fd/"); + while ((dp = readdir(dir)) != NULL) { + if (dp->d_name[0] == '.') + continue; + ++fd_count; + } + closedir(dir); + return fd_count; +} +#endif + +class TurnPortTestVirtualSocketServer : public rtc::VirtualSocketServer { + public: + explicit TurnPortTestVirtualSocketServer(SocketServer* ss) + : VirtualSocketServer(ss) {} + + using rtc::VirtualSocketServer::LookupBinding; +}; + +class TurnPortTest : public testing::Test, + public sigslot::has_slots<>, + public rtc::MessageHandler { + public: + TurnPortTest() + : main_(rtc::Thread::Current()), + pss_(new rtc::PhysicalSocketServer), + ss_(new TurnPortTestVirtualSocketServer(pss_.get())), + ss_scope_(ss_.get()), + network_("unittest", "unittest", rtc::IPAddress(INADDR_ANY), 32), + socket_factory_(rtc::Thread::Current()), + turn_server_(main_, kTurnUdpIntAddr, kTurnUdpExtAddr), + turn_ready_(false), + turn_error_(false), + turn_unknown_address_(false), + turn_create_permission_success_(false), + udp_ready_(false), + test_finish_(false) { + network_.AddIP(rtc::IPAddress(INADDR_ANY)); + } + + virtual void OnMessage(rtc::Message* msg) { + ASSERT(msg->message_id == MSG_TESTFINISH); + if (msg->message_id == MSG_TESTFINISH) + test_finish_ = true; + } + + void ConnectSignalAddressReadyToSetLocalhostAsAltenertativeLocalAddress() { + rtc::AsyncPacketSocket* socket = turn_port_->socket(); + rtc::VirtualSocket* virtual_socket = + ss_->LookupBinding(socket->GetLocalAddress()); + virtual_socket->SignalAddressReady.connect( + this, &TurnPortTest::SetLocalhostAsAltenertativeLocalAddress); + } + + void SetLocalhostAsAltenertativeLocalAddress( + rtc::VirtualSocket* socket, + const rtc::SocketAddress& address) { + SocketAddress local_address("127.0.0.1", 2000); + socket->SetAlternativeLocalAddress(local_address); + } + + void OnTurnPortComplete(Port* port) { + turn_ready_ = true; + } + void OnTurnPortError(Port* port) { + turn_error_ = true; + } + void OnTurnUnknownAddress(PortInterface* port, const SocketAddress& addr, + cricket::ProtocolType proto, + cricket::IceMessage* msg, const std::string& rf, + bool /*port_muxed*/) { + turn_unknown_address_ = true; + } + void OnTurnCreatePermissionResult(TurnPort* port, const SocketAddress& addr, + int code) { + // Ignoring the address. + if (code == 0) { + turn_create_permission_success_ = true; + } + } + void OnTurnReadPacket(Connection* conn, const char* data, size_t size, + const rtc::PacketTime& packet_time) { + turn_packets_.push_back(rtc::Buffer(data, size)); + } + void OnUdpPortComplete(Port* port) { + udp_ready_ = true; + } + void OnUdpReadPacket(Connection* conn, const char* data, size_t size, + const rtc::PacketTime& packet_time) { + udp_packets_.push_back(rtc::Buffer(data, size)); + } + void OnSocketReadPacket(rtc::AsyncPacketSocket* socket, + const char* data, size_t size, + const rtc::SocketAddress& remote_addr, + const rtc::PacketTime& packet_time) { + turn_port_->HandleIncomingPacket(socket, data, size, remote_addr, + packet_time); + } + rtc::AsyncSocket* CreateServerSocket(const SocketAddress addr) { + rtc::AsyncSocket* socket = ss_->CreateAsyncSocket(SOCK_STREAM); + EXPECT_GE(socket->Bind(addr), 0); + EXPECT_GE(socket->Listen(5), 0); + return socket; + } + + void CreateTurnPort(const std::string& username, + const std::string& password, + const cricket::ProtocolAddress& server_address) { + CreateTurnPort(kLocalAddr1, username, password, server_address); + } + void CreateTurnPort(const rtc::SocketAddress& local_address, + const std::string& username, + const std::string& password, + const cricket::ProtocolAddress& server_address) { + cricket::RelayCredentials credentials(username, password); + turn_port_.reset(TurnPort::Create(main_, &socket_factory_, &network_, + local_address.ipaddr(), 0, 0, + kIceUfrag1, kIcePwd1, + server_address, credentials, 0, + std::string())); + // This TURN port will be the controlling. + turn_port_->SetIceRole(cricket::ICEROLE_CONTROLLING); + ConnectSignals(); + } + + // Should be identical to CreateTurnPort but specifies an origin value + // when creating the instance of TurnPort. + void CreateTurnPortWithOrigin(const rtc::SocketAddress& local_address, + const std::string& username, + const std::string& password, + const cricket::ProtocolAddress& server_address, + const std::string& origin) { + cricket::RelayCredentials credentials(username, password); + turn_port_.reset(TurnPort::Create(main_, &socket_factory_, &network_, + local_address.ipaddr(), 0, 0, + kIceUfrag1, kIcePwd1, + server_address, credentials, 0, + origin)); + // This TURN port will be the controlling. + turn_port_->SetIceRole(cricket::ICEROLE_CONTROLLING); + ConnectSignals(); + } + + void CreateSharedTurnPort(const std::string& username, + const std::string& password, + const cricket::ProtocolAddress& server_address) { + ASSERT(server_address.proto == cricket::PROTO_UDP); + + if (!socket_) { + socket_.reset(socket_factory_.CreateUdpSocket( + rtc::SocketAddress(kLocalAddr1.ipaddr(), 0), 0, 0)); + ASSERT_TRUE(socket_ != NULL); + socket_->SignalReadPacket.connect( + this, &TurnPortTest::OnSocketReadPacket); + } + + cricket::RelayCredentials credentials(username, password); + turn_port_.reset(cricket::TurnPort::Create( + main_, &socket_factory_, &network_, socket_.get(), + kIceUfrag1, kIcePwd1, server_address, credentials, 0, std::string())); + // This TURN port will be the controlling. + turn_port_->SetIceRole(cricket::ICEROLE_CONTROLLING); + ConnectSignals(); + } + + void ConnectSignals() { + turn_port_->SignalPortComplete.connect(this, + &TurnPortTest::OnTurnPortComplete); + turn_port_->SignalPortError.connect(this, + &TurnPortTest::OnTurnPortError); + turn_port_->SignalUnknownAddress.connect(this, + &TurnPortTest::OnTurnUnknownAddress); + turn_port_->SignalCreatePermissionResult.connect(this, + &TurnPortTest::OnTurnCreatePermissionResult); + } + void CreateUdpPort() { + udp_port_.reset(UDPPort::Create(main_, &socket_factory_, &network_, + kLocalAddr2.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 TestTurnAlternateServer(cricket::ProtocolType protocol_type) { + std::vector<rtc::SocketAddress> redirect_addresses; + redirect_addresses.push_back(kTurnAlternateIntAddr); + + cricket::TestTurnRedirector redirector(redirect_addresses); + + turn_server_.AddInternalSocket(kTurnIntAddr, protocol_type); + turn_server_.AddInternalSocket(kTurnAlternateIntAddr, protocol_type); + turn_server_.set_redirect_hook(&redirector); + CreateTurnPort(kTurnUsername, kTurnPassword, + cricket::ProtocolAddress(kTurnIntAddr, protocol_type)); + + // Retrieve the address before we run the state machine. + const SocketAddress old_addr = turn_port_->server_address().address; + + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout * 100); + // Retrieve the address again, the turn port's address should be + // changed. + const SocketAddress new_addr = turn_port_->server_address().address; + EXPECT_NE(old_addr, new_addr); + ASSERT_EQ(1U, turn_port_->Candidates().size()); + EXPECT_EQ(kTurnUdpExtAddr.ipaddr(), + turn_port_->Candidates()[0].address().ipaddr()); + EXPECT_NE(0, turn_port_->Candidates()[0].address().port()); + } + + void TestTurnAlternateServerV4toV6(cricket::ProtocolType protocol_type) { + std::vector<rtc::SocketAddress> redirect_addresses; + redirect_addresses.push_back(kTurnIPv6IntAddr); + + cricket::TestTurnRedirector redirector(redirect_addresses); + turn_server_.AddInternalSocket(kTurnIntAddr, protocol_type); + turn_server_.set_redirect_hook(&redirector); + CreateTurnPort(kTurnUsername, kTurnPassword, + cricket::ProtocolAddress(kTurnIntAddr, protocol_type)); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_error_, kTimeout); + } + + void TestTurnAlternateServerPingPong(cricket::ProtocolType protocol_type) { + std::vector<rtc::SocketAddress> redirect_addresses; + redirect_addresses.push_back(kTurnAlternateIntAddr); + redirect_addresses.push_back(kTurnIntAddr); + + cricket::TestTurnRedirector redirector(redirect_addresses); + + turn_server_.AddInternalSocket(kTurnIntAddr, protocol_type); + turn_server_.AddInternalSocket(kTurnAlternateIntAddr, protocol_type); + turn_server_.set_redirect_hook(&redirector); + CreateTurnPort(kTurnUsername, kTurnPassword, + cricket::ProtocolAddress(kTurnIntAddr, protocol_type)); + + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_error_, kTimeout); + ASSERT_EQ(0U, turn_port_->Candidates().size()); + rtc::SocketAddress address; + // Verify that we have exhausted all alternate servers instead of + // failure caused by other errors. + EXPECT_FALSE(redirector.ShouldRedirect(address, &address)); + } + + void TestTurnAlternateServerDetectRepetition( + cricket::ProtocolType protocol_type) { + std::vector<rtc::SocketAddress> redirect_addresses; + redirect_addresses.push_back(kTurnAlternateIntAddr); + redirect_addresses.push_back(kTurnAlternateIntAddr); + + cricket::TestTurnRedirector redirector(redirect_addresses); + + turn_server_.AddInternalSocket(kTurnIntAddr, protocol_type); + turn_server_.AddInternalSocket(kTurnAlternateIntAddr, protocol_type); + turn_server_.set_redirect_hook(&redirector); + CreateTurnPort(kTurnUsername, kTurnPassword, + cricket::ProtocolAddress(kTurnIntAddr, protocol_type)); + + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_error_, kTimeout); + ASSERT_EQ(0U, turn_port_->Candidates().size()); + } + + 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); + + // Send ping from UDP to TURN. + Connection* conn1 = udp_port_->CreateConnection( + turn_port_->Candidates()[0], Port::ORIGIN_MESSAGE); + ASSERT_TRUE(conn1 != NULL); + conn1->Ping(0); + WAIT(!turn_unknown_address_, kTimeout); + EXPECT_FALSE(turn_unknown_address_); + EXPECT_FALSE(conn1->receiving()); + EXPECT_EQ(Connection::STATE_WRITE_INIT, conn1->write_state()); + + // Send ping from TURN to UDP. + Connection* conn2 = turn_port_->CreateConnection( + udp_port_->Candidates()[0], Port::ORIGIN_MESSAGE); + ASSERT_TRUE(conn2 != NULL); + ASSERT_TRUE_WAIT(turn_create_permission_success_, kTimeout); + conn2->Ping(0); + + EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, conn2->write_state(), kTimeout); + EXPECT_TRUE(conn1->receiving()); + EXPECT_TRUE(conn2->receiving()); + EXPECT_EQ(Connection::STATE_WRITE_INIT, conn1->write_state()); + + // Send another ping from UDP to TURN. + conn1->Ping(0); + EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, conn1->write_state(), kTimeout); + EXPECT_TRUE(conn2->receiving()); + } + + void TestTurnSendData() { + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + CreateUdpPort(); + udp_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(udp_ready_, kTimeout); + // Create connections and send pings. + Connection* conn1 = turn_port_->CreateConnection( + udp_port_->Candidates()[0], Port::ORIGIN_MESSAGE); + Connection* conn2 = udp_port_->CreateConnection( + turn_port_->Candidates()[0], Port::ORIGIN_MESSAGE); + ASSERT_TRUE(conn1 != NULL); + ASSERT_TRUE(conn2 != NULL); + conn1->SignalReadPacket.connect(static_cast<TurnPortTest*>(this), + &TurnPortTest::OnTurnReadPacket); + conn2->SignalReadPacket.connect(static_cast<TurnPortTest*>(this), + &TurnPortTest::OnUdpReadPacket); + conn1->Ping(0); + EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, conn1->write_state(), kTimeout); + conn2->Ping(0); + EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, conn2->write_state(), kTimeout); + + // Send some data. + size_t num_packets = 256; + for (size_t i = 0; i < num_packets; ++i) { + unsigned char buf[256] = { 0 }; + for (size_t j = 0; j < i + 1; ++j) { + buf[j] = 0xFF - static_cast<unsigned char>(j); + } + conn1->Send(buf, i + 1, options); + conn2->Send(buf, i + 1, options); + main_->ProcessMessages(0); + } + + // Check the data. + ASSERT_EQ_WAIT(num_packets, turn_packets_.size(), kTimeout); + ASSERT_EQ_WAIT(num_packets, udp_packets_.size(), kTimeout); + for (size_t i = 0; i < num_packets; ++i) { + EXPECT_EQ(i + 1, turn_packets_[i].size()); + EXPECT_EQ(i + 1, udp_packets_[i].size()); + EXPECT_EQ(turn_packets_[i], udp_packets_[i]); + } + } + + protected: + rtc::Thread* main_; + rtc::scoped_ptr<rtc::PhysicalSocketServer> pss_; + rtc::scoped_ptr<TurnPortTestVirtualSocketServer> ss_; + rtc::SocketServerScope ss_scope_; + rtc::Network network_; + rtc::BasicPacketSocketFactory socket_factory_; + rtc::scoped_ptr<rtc::AsyncPacketSocket> socket_; + cricket::TestTurnServer turn_server_; + rtc::scoped_ptr<TurnPort> turn_port_; + rtc::scoped_ptr<UDPPort> udp_port_; + bool turn_ready_; + bool turn_error_; + bool turn_unknown_address_; + bool turn_create_permission_success_; + bool udp_ready_; + bool test_finish_; + std::vector<rtc::Buffer> turn_packets_; + std::vector<rtc::Buffer> udp_packets_; + rtc::PacketOptions options; +}; + +// Do a normal TURN allocation. +TEST_F(TurnPortTest, TestTurnAllocate) { + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + EXPECT_EQ(0, turn_port_->SetOption(rtc::Socket::OPT_SNDBUF, 10*1024)); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + ASSERT_EQ(1U, turn_port_->Candidates().size()); + EXPECT_EQ(kTurnUdpExtAddr.ipaddr(), + turn_port_->Candidates()[0].address().ipaddr()); + EXPECT_NE(0, turn_port_->Candidates()[0].address().port()); +} + +// Testing a normal UDP allocation using TCP connection. +TEST_F(TurnPortTest, TestTurnTcpAllocate) { + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); + EXPECT_EQ(0, turn_port_->SetOption(rtc::Socket::OPT_SNDBUF, 10*1024)); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + ASSERT_EQ(1U, turn_port_->Candidates().size()); + EXPECT_EQ(kTurnUdpExtAddr.ipaddr(), + turn_port_->Candidates()[0].address().ipaddr()); + EXPECT_NE(0, turn_port_->Candidates()[0].address().port()); +} + +// Test case for WebRTC issue 3927 where a proxy binds to the local host address +// instead the address that TurnPort originally bound to. The candidate pair +// impacted by this behavior should still be used. +TEST_F(TurnPortTest, TestTurnTcpAllocationWhenProxyChangesAddressToLocalHost) { + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); + EXPECT_EQ(0, turn_port_->SetOption(rtc::Socket::OPT_SNDBUF, 10 * 1024)); + turn_port_->PrepareAddress(); + ConnectSignalAddressReadyToSetLocalhostAsAltenertativeLocalAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + ASSERT_EQ(1U, turn_port_->Candidates().size()); + EXPECT_EQ(kTurnUdpExtAddr.ipaddr(), + turn_port_->Candidates()[0].address().ipaddr()); + EXPECT_NE(0, turn_port_->Candidates()[0].address().port()); +} + +// Testing turn port will attempt to create TCP socket on address resolution +// failure. +TEST_F(TurnPortTest, DISABLED_TestTurnTcpOnAddressResolveFailure) { + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); + CreateTurnPort(kTurnUsername, kTurnPassword, cricket::ProtocolAddress( + rtc::SocketAddress("www.webrtc-blah-blah.com", 3478), + cricket::PROTO_TCP)); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_error_, kTimeout); + // As VSS doesn't provide a DNS resolution, name resolve will fail. TurnPort + // will proceed in creating a TCP socket which will fail as there is no + // server on the above domain and error will be set to SOCKET_ERROR. + EXPECT_EQ(SOCKET_ERROR, turn_port_->error()); +} + +// In case of UDP on address resolve failure, TurnPort will not create socket +// and return allocate failure. +TEST_F(TurnPortTest, DISABLED_TestTurnUdpOnAdressResolveFailure) { + CreateTurnPort(kTurnUsername, kTurnPassword, cricket::ProtocolAddress( + rtc::SocketAddress("www.webrtc-blah-blah.com", 3478), + cricket::PROTO_UDP)); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_error_, kTimeout); + // Error from turn port will not be socket error. + EXPECT_NE(SOCKET_ERROR, turn_port_->error()); +} + +// Try to do a TURN allocation with an invalid password. +TEST_F(TurnPortTest, TestTurnAllocateBadPassword) { + CreateTurnPort(kTurnUsername, "bad", kTurnUdpProtoAddr); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_error_, kTimeout); + ASSERT_EQ(0U, turn_port_->Candidates().size()); +} + +// Tests that a new local address is created after +// STUN_ERROR_ALLOCATION_MISMATCH. +TEST_F(TurnPortTest, TestTurnAllocateMismatch) { + // Do a normal allocation first. + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress()); + + // Clear connected_ flag on turnport to suppress the release of + // the allocation. + turn_port_->OnSocketClose(turn_port_->socket(), 0); + + // Forces the socket server to assign the same port. + ss_->SetNextPortForTesting(first_addr.port()); + + turn_ready_ = false; + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + turn_port_->PrepareAddress(); + + // Verifies that the new port has the same address. + EXPECT_EQ(first_addr, turn_port_->socket()->GetLocalAddress()); + + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + + // Verifies that the new port has a different address now. + EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress()); +} + +// Tests that a shared-socket-TurnPort creates its own socket after +// STUN_ERROR_ALLOCATION_MISMATCH. +TEST_F(TurnPortTest, TestSharedSocketAllocateMismatch) { + // Do a normal allocation first. + CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress()); + + // Clear connected_ flag on turnport to suppress the release of + // the allocation. + turn_port_->OnSocketClose(turn_port_->socket(), 0); + + turn_ready_ = false; + CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + + // Verifies that the new port has the same address. + EXPECT_EQ(first_addr, turn_port_->socket()->GetLocalAddress()); + EXPECT_TRUE(turn_port_->SharedSocket()); + + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + + // Verifies that the new port has a different address now. + EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress()); + EXPECT_FALSE(turn_port_->SharedSocket()); +} + +TEST_F(TurnPortTest, TestTurnTcpAllocateMismatch) { + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); + + // Do a normal allocation first. + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress()); + + // Clear connected_ flag on turnport to suppress the release of + // the allocation. + turn_port_->OnSocketClose(turn_port_->socket(), 0); + + // Forces the socket server to assign the same port. + ss_->SetNextPortForTesting(first_addr.port()); + + turn_ready_ = false; + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); + turn_port_->PrepareAddress(); + + // Verifies that the new port has the same address. + EXPECT_EQ(first_addr, turn_port_->socket()->GetLocalAddress()); + + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + + // Verifies that the new port has a different address now. + EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress()); +} + +// 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); + // Create a connection. + Connection* conn1 = turn_port_->CreateConnection(udp_port_->Candidates()[0], + Port::ORIGIN_MESSAGE); + ASSERT_TRUE(conn1 != NULL); + + // Close the socket and create a connection again. + turn_port_->OnSocketClose(turn_port_->socket(), 1); + conn1 = turn_port_->CreateConnection(udp_port_->Candidates()[0], + Port::ORIGIN_MESSAGE); + ASSERT_TRUE(conn1 == NULL); +} + +// Test try-alternate-server feature. +TEST_F(TurnPortTest, TestTurnAlternateServerUDP) { + TestTurnAlternateServer(cricket::PROTO_UDP); +} + +TEST_F(TurnPortTest, TestTurnAlternateServerTCP) { + TestTurnAlternateServer(cricket::PROTO_TCP); +} + +// Test that we fail when we redirect to an address different from +// current IP family. +TEST_F(TurnPortTest, TestTurnAlternateServerV4toV6UDP) { + TestTurnAlternateServerV4toV6(cricket::PROTO_UDP); +} + +TEST_F(TurnPortTest, TestTurnAlternateServerV4toV6TCP) { + TestTurnAlternateServerV4toV6(cricket::PROTO_TCP); +} + +// Test try-alternate-server catches the case of pingpong. +TEST_F(TurnPortTest, TestTurnAlternateServerPingPongUDP) { + TestTurnAlternateServerPingPong(cricket::PROTO_UDP); +} + +TEST_F(TurnPortTest, TestTurnAlternateServerPingPongTCP) { + TestTurnAlternateServerPingPong(cricket::PROTO_TCP); +} + +// Test try-alternate-server catch the case of repeated server. +TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetitionUDP) { + TestTurnAlternateServerDetectRepetition(cricket::PROTO_UDP); +} + +TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetitionTCP) { + TestTurnAlternateServerDetectRepetition(cricket::PROTO_TCP); +} + +// Do a TURN allocation and try to send a packet to it from the outside. +// The packet should be dropped. Then, try to send a packet from TURN to the +// outside. It should reach its destination. Finally, try again from the +// outside. It should now work as well. +TEST_F(TurnPortTest, TestTurnConnection) { + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + TestTurnConnection(); +} + +// Similar to above, except that this test will use the shared socket. +TEST_F(TurnPortTest, TestTurnConnectionUsingSharedSocket) { + CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + TestTurnConnection(); +} + +// Test that we can establish a TCP connection with TURN server. +TEST_F(TurnPortTest, TestTurnTcpConnection) { + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); + TestTurnConnection(); +} + +// 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) { + cricket::ProtocolAddress secure_addr(kTurnTcpProtoAddr.address, + kTurnTcpProtoAddr.proto, + true); + CreateTurnPort(kTurnUsername, kTurnPassword, secure_addr); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_error_, kTimeout); + ASSERT_EQ(0U, turn_port_->Candidates().size()); +} + +// Run TurnConnectionTest with one-time-use nonce feature. +// Here server will send a 438 STALE_NONCE error message for +// every TURN transaction. +TEST_F(TurnPortTest, TestTurnConnectionUsingOTUNonce) { + turn_server_.set_enable_otu_nonce(true); + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + TestTurnConnection(); +} + +// Do a TURN allocation, establish a UDP connection, and send some data. +TEST_F(TurnPortTest, TestTurnSendDataTurnUdpToUdp) { + // Create ports and prepare addresses. + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + TestTurnSendData(); + EXPECT_EQ(cricket::UDP_PROTOCOL_NAME, + turn_port_->Candidates()[0].relay_protocol()); +} + +// Do a TURN allocation, establish a TCP connection, and send some data. +TEST_F(TurnPortTest, TestTurnSendDataTurnTcpToUdp) { + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); + // Create ports and prepare addresses. + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); + TestTurnSendData(); + EXPECT_EQ(cricket::TCP_PROTOCOL_NAME, + turn_port_->Candidates()[0].relay_protocol()); +} + +// Test TURN fails to make a connection from IPv6 address to a server which has +// IPv4 address. +TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv4) { + turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP); + CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, + kTurnUdpProtoAddr); + turn_port_->PrepareAddress(); + ASSERT_TRUE_WAIT(turn_error_, kTimeout); + EXPECT_TRUE(turn_port_->Candidates().empty()); +} + +// Test TURN make a connection from IPv6 address to a server which has +// IPv6 intenal address. But in this test external address is a IPv4 address, +// hence allocated address will be a IPv4 address. +TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv6ExtenalIPv4) { + turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP); + CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, + kTurnUdpIPv6ProtoAddr); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + ASSERT_EQ(1U, turn_port_->Candidates().size()); + EXPECT_EQ(kTurnUdpExtAddr.ipaddr(), + turn_port_->Candidates()[0].address().ipaddr()); + EXPECT_NE(0, turn_port_->Candidates()[0].address().port()); +} + +TEST_F(TurnPortTest, TestOriginHeader) { + CreateTurnPortWithOrigin(kLocalAddr1, kTurnUsername, kTurnPassword, + kTurnUdpProtoAddr, kTestOrigin); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + ASSERT_GT(turn_server_.server()->allocations().size(), 0U); + SocketAddress local_address = turn_port_->GetLocalAddress(); + ASSERT_TRUE(turn_server_.FindAllocation(local_address) != NULL); + EXPECT_EQ(kTestOrigin, turn_server_.FindAllocation(local_address)->origin()); +} + +// Test that a TURN allocation is released when the port is closed. +TEST_F(TurnPortTest, TestTurnReleaseAllocation) { + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + + ASSERT_GT(turn_server_.server()->allocations().size(), 0U); + turn_port_.reset(); + EXPECT_EQ_WAIT(0U, turn_server_.server()->allocations().size(), kTimeout); +} + +// Test that a TURN TCP allocation is released when the port is closed. +TEST_F(TurnPortTest, DISABLED_TestTurnTCPReleaseAllocation) { + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + + ASSERT_GT(turn_server_.server()->allocations().size(), 0U); + turn_port_.reset(); + EXPECT_EQ_WAIT(0U, turn_server_.server()->allocations().size(), kTimeout); +} + +// 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) +TEST_F(TurnPortTest, TestResolverShutdown) { + turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP); + int last_fd_count = GetFDCount(); + // Need to supply unresolved address to kick off resolver. + CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, + cricket::ProtocolAddress(rtc::SocketAddress( + "www.google.invalid", 3478), cricket::PROTO_UDP)); + turn_port_->PrepareAddress(); + ASSERT_TRUE_WAIT(turn_error_, kTimeout); + EXPECT_TRUE(turn_port_->Candidates().empty()); + turn_port_.reset(); + rtc::Thread::Current()->Post(this, MSG_TESTFINISH); + // Waiting for above message to be processed. + ASSERT_TRUE_WAIT(test_finish_, kTimeout); + EXPECT_EQ(last_fd_count, GetFDCount()); +} +#endif |