summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/p2p/base/tcp_port_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/p2p/base/tcp_port_unittest.cc')
-rw-r--r--third_party/libwebrtc/p2p/base/tcp_port_unittest.cc259
1 files changed, 259 insertions, 0 deletions
diff --git a/third_party/libwebrtc/p2p/base/tcp_port_unittest.cc b/third_party/libwebrtc/p2p/base/tcp_port_unittest.cc
new file mode 100644
index 0000000000..1bb59811b8
--- /dev/null
+++ b/third_party/libwebrtc/p2p/base/tcp_port_unittest.cc
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#include "p2p/base/tcp_port.h"
+
+#include <list>
+#include <memory>
+#include <vector>
+
+#include "p2p/base/basic_packet_socket_factory.h"
+#include "p2p/base/p2p_constants.h"
+#include "p2p/base/transport_description.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/helpers.h"
+#include "rtc_base/ip_address.h"
+#include "rtc_base/third_party/sigslot/sigslot.h"
+#include "rtc_base/thread.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/virtual_socket_server.h"
+#include "test/gtest.h"
+#include "test/scoped_key_value_config.h"
+
+using cricket::Connection;
+using cricket::ICE_PWD_LENGTH;
+using cricket::ICE_UFRAG_LENGTH;
+using cricket::Port;
+using cricket::TCPPort;
+using rtc::SocketAddress;
+
+static int kTimeout = 1000;
+static const SocketAddress kLocalAddr("11.11.11.11", 0);
+static const SocketAddress kLocalIPv6Addr("2401:fa00:4:1000:be30:5bff:fee5:c3",
+ 0);
+static const SocketAddress kAlternateLocalAddr("1.2.3.4", 0);
+static const SocketAddress kRemoteAddr("22.22.22.22", 0);
+static const SocketAddress kRemoteIPv6Addr("2401:fa00:4:1000:be30:5bff:fee5:c4",
+ 0);
+
+constexpr uint64_t kTiebreakerDefault = 44444;
+
+class ConnectionObserver : public sigslot::has_slots<> {
+ public:
+ explicit ConnectionObserver(Connection* conn) : conn_(conn) {
+ conn->SignalDestroyed.connect(this, &ConnectionObserver::OnDestroyed);
+ }
+
+ ~ConnectionObserver() {
+ if (!connection_destroyed_) {
+ RTC_DCHECK(conn_);
+ conn_->SignalDestroyed.disconnect(this);
+ }
+ }
+
+ bool connection_destroyed() { return connection_destroyed_; }
+
+ private:
+ void OnDestroyed(Connection*) { connection_destroyed_ = true; }
+
+ Connection* const conn_;
+ bool connection_destroyed_ = false;
+};
+
+class TCPPortTest : public ::testing::Test, public sigslot::has_slots<> {
+ public:
+ TCPPortTest()
+ : ss_(new rtc::VirtualSocketServer()),
+ main_(ss_.get()),
+ socket_factory_(ss_.get()),
+ username_(rtc::CreateRandomString(ICE_UFRAG_LENGTH)),
+ password_(rtc::CreateRandomString(ICE_PWD_LENGTH)) {}
+
+ rtc::Network* MakeNetwork(const SocketAddress& addr) {
+ networks_.emplace_back("unittest", "unittest", addr.ipaddr(), 32);
+ networks_.back().AddIP(addr.ipaddr());
+ return &networks_.back();
+ }
+
+ std::unique_ptr<TCPPort> CreateTCPPort(const SocketAddress& addr) {
+ auto port = std::unique_ptr<TCPPort>(
+ TCPPort::Create(&main_, &socket_factory_, MakeNetwork(addr), 0, 0,
+ username_, password_, true, &field_trials_));
+ port->SetIceTiebreaker(kTiebreakerDefault);
+ return port;
+ }
+
+ std::unique_ptr<TCPPort> CreateTCPPort(const rtc::Network* network) {
+ auto port = std::unique_ptr<TCPPort>(
+ TCPPort::Create(&main_, &socket_factory_, network, 0, 0, username_,
+ password_, true, &field_trials_));
+ port->SetIceTiebreaker(kTiebreakerDefault);
+ return port;
+ }
+
+ protected:
+ // When a "create port" helper method is called with an IP, we create a
+ // Network with that IP and add it to this list. Using a list instead of a
+ // vector so that when it grows, pointers aren't invalidated.
+ std::list<rtc::Network> networks_;
+ std::unique_ptr<rtc::VirtualSocketServer> ss_;
+ rtc::AutoSocketServerThread main_;
+ rtc::BasicPacketSocketFactory socket_factory_;
+ std::string username_;
+ std::string password_;
+ webrtc::test::ScopedKeyValueConfig field_trials_;
+};
+
+TEST_F(TCPPortTest, TestTCPPortWithLocalhostAddress) {
+ SocketAddress local_address("127.0.0.1", 0);
+ // After calling this, when TCPPort attempts to get a socket bound to
+ // kLocalAddr, it will end up using localhost instead.
+ ss_->SetAlternativeLocalAddress(kLocalAddr.ipaddr(), local_address.ipaddr());
+ auto local_port = CreateTCPPort(kLocalAddr);
+ auto remote_port = CreateTCPPort(kRemoteAddr);
+ local_port->PrepareAddress();
+ remote_port->PrepareAddress();
+ Connection* conn = local_port->CreateConnection(remote_port->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ EXPECT_TRUE_WAIT(conn->connected(), kTimeout);
+ // Verify that the socket actually used localhost, otherwise this test isn't
+ // doing what it meant to.
+ ASSERT_EQ(local_address.ipaddr(),
+ local_port->Candidates()[0].address().ipaddr());
+}
+
+// If the address the socket ends up bound to does not match any address of the
+// TCPPort's Network, then the socket should be discarded and no candidates
+// should be signaled. In the context of ICE, where one TCPPort is created for
+// each Network, when this happens it's likely that the unexpected address is
+// associated with some other Network, which another TCPPort is already
+// covering.
+TEST_F(TCPPortTest, TCPPortDiscardedIfBoundAddressDoesNotMatchNetwork) {
+ // Sockets bound to kLocalAddr will actually end up with kAlternateLocalAddr.
+ ss_->SetAlternativeLocalAddress(kLocalAddr.ipaddr(),
+ kAlternateLocalAddr.ipaddr());
+
+ // Create ports (local_port is the one whose IP will end up reassigned).
+ auto local_port = CreateTCPPort(kLocalAddr);
+ auto remote_port = CreateTCPPort(kRemoteAddr);
+ local_port->PrepareAddress();
+ remote_port->PrepareAddress();
+
+ // Tell port to create a connection; it should be destroyed when it's
+ // realized that it's using an unexpected address.
+ Connection* conn = local_port->CreateConnection(remote_port->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ ConnectionObserver observer(conn);
+ EXPECT_TRUE_WAIT(observer.connection_destroyed(), kTimeout);
+}
+
+// A caveat for the above logic: if the socket ends up bound to one of the IPs
+// associated with the Network, just not the "best" one, this is ok.
+TEST_F(TCPPortTest, TCPPortNotDiscardedIfNotBoundToBestIP) {
+ // Sockets bound to kLocalAddr will actually end up with kAlternateLocalAddr.
+ ss_->SetAlternativeLocalAddress(kLocalAddr.ipaddr(),
+ kAlternateLocalAddr.ipaddr());
+
+ // Set up a network with kLocalAddr1 as the "best" IP, and kAlternateLocalAddr
+ // as an alternate.
+ rtc::Network* network = MakeNetwork(kLocalAddr);
+ network->AddIP(kAlternateLocalAddr.ipaddr());
+ ASSERT_EQ(kLocalAddr.ipaddr(), network->GetBestIP());
+
+ // Create ports (using our special 2-IP Network for local_port).
+ auto local_port = CreateTCPPort(network);
+ auto remote_port = CreateTCPPort(kRemoteAddr);
+ local_port->PrepareAddress();
+ remote_port->PrepareAddress();
+
+ // Expect connection to succeed.
+ Connection* conn = local_port->CreateConnection(remote_port->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ EXPECT_TRUE_WAIT(conn->connected(), kTimeout);
+
+ // Verify that the socket actually used the alternate address, otherwise this
+ // test isn't doing what it meant to.
+ ASSERT_EQ(kAlternateLocalAddr.ipaddr(),
+ local_port->Candidates()[0].address().ipaddr());
+}
+
+// Regression test for crbug.com/webrtc/8972, caused by buggy comparison
+// between rtc::IPAddress and rtc::InterfaceAddress.
+TEST_F(TCPPortTest, TCPPortNotDiscardedIfBoundToTemporaryIP) {
+ networks_.emplace_back("unittest", "unittest", kLocalIPv6Addr.ipaddr(), 32);
+ networks_.back().AddIP(rtc::InterfaceAddress(
+ kLocalIPv6Addr.ipaddr(), rtc::IPV6_ADDRESS_FLAG_TEMPORARY));
+
+ auto local_port = CreateTCPPort(&networks_.back());
+ auto remote_port = CreateTCPPort(kRemoteIPv6Addr);
+ local_port->PrepareAddress();
+ remote_port->PrepareAddress();
+
+ // Connection should succeed if the port isn't discarded.
+ Connection* conn = local_port->CreateConnection(remote_port->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ ASSERT_NE(nullptr, conn);
+ EXPECT_TRUE_WAIT(conn->connected(), kTimeout);
+}
+
+class SentPacketCounter : public sigslot::has_slots<> {
+ public:
+ explicit SentPacketCounter(TCPPort* p) {
+ p->SignalSentPacket.connect(this, &SentPacketCounter::OnSentPacket);
+ }
+
+ int sent_packets() const { return sent_packets_; }
+
+ private:
+ void OnSentPacket(const rtc::SentPacket&) { ++sent_packets_; }
+
+ int sent_packets_ = 0;
+};
+
+// Test that SignalSentPacket is fired when a packet is successfully sent, for
+// both TCP client and server sockets.
+TEST_F(TCPPortTest, SignalSentPacket) {
+ std::unique_ptr<TCPPort> client(CreateTCPPort(kLocalAddr));
+ std::unique_ptr<TCPPort> server(CreateTCPPort(kRemoteAddr));
+ client->SetIceRole(cricket::ICEROLE_CONTROLLING);
+ server->SetIceRole(cricket::ICEROLE_CONTROLLED);
+ client->PrepareAddress();
+ server->PrepareAddress();
+
+ Connection* client_conn =
+ client->CreateConnection(server->Candidates()[0], Port::ORIGIN_MESSAGE);
+ ASSERT_NE(nullptr, client_conn);
+ ASSERT_TRUE_WAIT(client_conn->connected(), kTimeout);
+
+ // Need to get the port of the actual outgoing socket, not the server socket..
+ cricket::Candidate client_candidate = client->Candidates()[0];
+ client_candidate.set_address(static_cast<cricket::TCPConnection*>(client_conn)
+ ->socket()
+ ->GetLocalAddress());
+ Connection* server_conn =
+ server->CreateConnection(client_candidate, Port::ORIGIN_THIS_PORT);
+ ASSERT_NE(nullptr, server_conn);
+ ASSERT_TRUE_WAIT(server_conn->connected(), kTimeout);
+
+ client_conn->Ping(rtc::TimeMillis());
+ server_conn->Ping(rtc::TimeMillis());
+ ASSERT_TRUE_WAIT(client_conn->writable(), kTimeout);
+ ASSERT_TRUE_WAIT(server_conn->writable(), kTimeout);
+
+ SentPacketCounter client_counter(client.get());
+ SentPacketCounter server_counter(server.get());
+ static const char kData[] = "hello";
+ for (int i = 0; i < 10; ++i) {
+ client_conn->Send(&kData, sizeof(kData), rtc::PacketOptions());
+ server_conn->Send(&kData, sizeof(kData), rtc::PacketOptions());
+ }
+ EXPECT_EQ_WAIT(10, client_counter.sent_packets(), kTimeout);
+ EXPECT_EQ_WAIT(10, server_counter.sent_packets(), kTimeout);
+}