| /* |
| * 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 <list> |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/types/optional.h" |
| #include "api/units/time_delta.h" |
| #include "p2p/base/basic_packet_socket_factory.h" |
| #include "p2p/base/connection.h" |
| #include "p2p/base/p2p_constants.h" |
| #include "p2p/base/port_allocator.h" |
| #include "p2p/base/stun_port.h" |
| #include "p2p/base/test_turn_customizer.h" |
| #include "p2p/base/test_turn_server.h" |
| #include "p2p/base/transport_description.h" |
| #include "p2p/base/turn_port.h" |
| #include "p2p/base/turn_server.h" |
| #include "rtc_base/async_socket.h" |
| #include "rtc_base/buffer.h" |
| #include "rtc_base/byte_buffer.h" |
| #include "rtc_base/checks.h" |
| #include "rtc_base/fake_clock.h" |
| #include "rtc_base/gunit.h" |
| #include "rtc_base/location.h" |
| #include "rtc_base/message_handler.h" |
| #include "rtc_base/net_helper.h" |
| #include "rtc_base/socket_address.h" |
| #include "rtc_base/thread.h" |
| #include "rtc_base/time_utils.h" |
| #include "rtc_base/virtual_socket_server.h" |
| #include "test/field_trial.h" |
| #include "test/gtest.h" |
| |
| using rtc::SocketAddress; |
| |
| 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 kLocalIPv6Addr2("2401:fa00:4:2000:be30:5bff:fee5:d4", |
| 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); |
| // Port for redirecting to a TCP Web server. Should not work. |
| static const SocketAddress kTurnDangerousAddr("99.99.99.7", 81); |
| // Port 53 (the DNS port); should work. |
| static const SocketAddress kTurnPort53Addr("99.99.99.7", 53); |
| // Port 80 (the HTTP port); should work. |
| static const SocketAddress kTurnPort80Addr("99.99.99.7", 80); |
| // Port 443 (the HTTPS port); should work. |
| static const SocketAddress kTurnPort443Addr("99.99.99.7", 443); |
| // The default 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 kTurnInvalidAddr("www.google.invalid", 3478); |
| |
| static const char kCandidateFoundation[] = "foundation"; |
| 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"; |
| // This test configures the virtual socket server to simulate delay so that we |
| // can verify operations take no more than the expected number of round trips. |
| static constexpr unsigned int kSimulatedRtt = 50; |
| // Connection destruction may happen asynchronously, but it should only |
| // take one simulated clock tick. |
| static constexpr unsigned int kConnectionDestructionDelay = 1; |
| // This used to be 1 second, but that's not always enough for getaddrinfo(). |
| // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=5191 |
| static constexpr unsigned int kResolverTimeout = 10000; |
| |
| static const cricket::ProtocolAddress kTurnUdpProtoAddr(kTurnUdpIntAddr, |
| cricket::PROTO_UDP); |
| static const cricket::ProtocolAddress kTurnTcpProtoAddr(kTurnTcpIntAddr, |
| cricket::PROTO_TCP); |
| static const cricket::ProtocolAddress kTurnTlsProtoAddr(kTurnTcpIntAddr, |
| cricket::PROTO_TLS); |
| static const cricket::ProtocolAddress kTurnUdpIPv6ProtoAddr(kTurnUdpIPv6IntAddr, |
| cricket::PROTO_UDP); |
| static const cricket::ProtocolAddress kTurnDangerousProtoAddr( |
| kTurnDangerousAddr, |
| cricket::PROTO_TCP); |
| static const cricket::ProtocolAddress kTurnPort53ProtoAddr(kTurnPort53Addr, |
| cricket::PROTO_TCP); |
| static const cricket::ProtocolAddress kTurnPort80ProtoAddr(kTurnPort80Addr, |
| cricket::PROTO_TCP); |
| static const cricket::ProtocolAddress kTurnPort443ProtoAddr(kTurnPort443Addr, |
| cricket::PROTO_TCP); |
| |
| 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 |
| |
| namespace cricket { |
| |
| class TurnPortTestVirtualSocketServer : public rtc::VirtualSocketServer { |
| public: |
| TurnPortTestVirtualSocketServer() { |
| // This configures the virtual socket server to always add a simulated |
| // delay of exactly half of kSimulatedRtt. |
| set_delay_mean(kSimulatedRtt / 2); |
| UpdateDelayDistribution(); |
| } |
| |
| using rtc::VirtualSocketServer::LookupBinding; |
| }; |
| |
| class TestConnectionWrapper : public sigslot::has_slots<> { |
| public: |
| explicit TestConnectionWrapper(Connection* conn) : connection_(conn) { |
| conn->SignalDestroyed.connect( |
| this, &TestConnectionWrapper::OnConnectionDestroyed); |
| } |
| |
| Connection* connection() { return connection_; } |
| |
| private: |
| void OnConnectionDestroyed(Connection* conn) { |
| ASSERT_TRUE(conn == connection_); |
| connection_ = nullptr; |
| } |
| |
| Connection* connection_; |
| }; |
| |
| // Note: This test uses a fake clock with a simulated network round trip |
| // (between local port and TURN server) of kSimulatedRtt. |
| class TurnPortTest : public ::testing::Test, |
| public sigslot::has_slots<>, |
| public rtc::MessageHandlerAutoCleanup { |
| public: |
| TurnPortTest() |
| : ss_(new TurnPortTestVirtualSocketServer()), |
| main_(ss_.get()), |
| 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), |
| turn_port_closed_(false), |
| turn_port_destroyed_(false), |
| udp_ready_(false), |
| test_finish_(false) { |
| // Some code uses "last received time == 0" to represent "nothing received |
| // so far", so we need to start the fake clock at a nonzero time... |
| // TODO(deadbeef): Fix this. |
| fake_clock_.AdvanceTime(webrtc::TimeDelta::Seconds(1)); |
| } |
| |
| virtual void OnMessage(rtc::Message* msg) { |
| RTC_CHECK(msg->message_id == MSG_TESTFINISH); |
| if (msg->message_id == MSG_TESTFINISH) |
| test_finish_ = true; |
| } |
| |
| void OnTurnPortComplete(Port* port) { turn_ready_ = true; } |
| void OnTurnPortError(Port* port) { turn_error_ = true; } |
| void OnCandidateError(Port* port, |
| const cricket::IceCandidateErrorEvent& event) { |
| error_event_ = event; |
| } |
| void OnTurnUnknownAddress(PortInterface* port, |
| const SocketAddress& addr, |
| ProtocolType proto, |
| 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. |
| turn_create_permission_success_ = (code == 0); |
| } |
| |
| void OnTurnRefreshResult(TurnPort* port, int code) { |
| turn_refresh_success_ = (code == 0); |
| } |
| void OnTurnReadPacket(Connection* conn, |
| const char* data, |
| size_t size, |
| int64_t packet_time_us) { |
| 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, |
| int64_t packet_time_us) { |
| 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 int64_t& packet_time_us) { |
| turn_port_->HandleIncomingPacket(socket, data, size, remote_addr, |
| packet_time_us); |
| } |
| void OnTurnPortClosed(TurnPort* port) { turn_port_closed_ = true; } |
| void OnTurnPortDestroyed(PortInterface* port) { turn_port_destroyed_ = true; } |
| |
| rtc::AsyncSocket* CreateServerSocket(const SocketAddress addr) { |
| rtc::AsyncSocket* socket = ss_->CreateAsyncSocket(AF_INET, SOCK_STREAM); |
| EXPECT_GE(socket->Bind(addr), 0); |
| EXPECT_GE(socket->Listen(5), 0); |
| return socket; |
| } |
| |
| rtc::Network* MakeNetwork(const SocketAddress& addr) { |
| networks_.emplace_back("unittest", "unittest", addr.ipaddr(), 32); |
| networks_.back().AddIP(addr.ipaddr()); |
| return &networks_.back(); |
| } |
| |
| bool CreateTurnPort(const std::string& username, |
| const std::string& password, |
| const ProtocolAddress& server_address) { |
| return CreateTurnPortWithAllParams(MakeNetwork(kLocalAddr1), username, |
| password, server_address, std::string()); |
| } |
| bool CreateTurnPort(const rtc::SocketAddress& local_address, |
| const std::string& username, |
| const std::string& password, |
| const ProtocolAddress& server_address) { |
| return CreateTurnPortWithAllParams(MakeNetwork(local_address), username, |
| password, server_address, std::string()); |
| } |
| |
| // Should be identical to CreateTurnPort but specifies an origin value |
| // when creating the instance of TurnPort. |
| bool CreateTurnPortWithOrigin(const rtc::SocketAddress& local_address, |
| const std::string& username, |
| const std::string& password, |
| const ProtocolAddress& server_address, |
| const std::string& origin) { |
| return CreateTurnPortWithAllParams(MakeNetwork(local_address), username, |
| password, server_address, origin); |
| } |
| |
| bool CreateTurnPortWithNetwork(rtc::Network* network, |
| const std::string& username, |
| const std::string& password, |
| const ProtocolAddress& server_address) { |
| return CreateTurnPortWithAllParams(network, username, password, |
| server_address, std::string()); |
| } |
| |
| // Version of CreateTurnPort that takes all possible parameters; all other |
| // helper methods call this, such that "SetIceRole" and "ConnectSignals" (and |
| // possibly other things in the future) only happen in one place. |
| bool CreateTurnPortWithAllParams(rtc::Network* network, |
| const std::string& username, |
| const std::string& password, |
| const ProtocolAddress& server_address, |
| const std::string& origin) { |
| RelayCredentials credentials(username, password); |
| turn_port_ = TurnPort::Create( |
| &main_, &socket_factory_, network, 0, 0, kIceUfrag1, kIcePwd1, |
| server_address, credentials, 0, origin, {}, {}, turn_customizer_.get()); |
| if (!turn_port_) { |
| return false; |
| } |
| // This TURN port will be the controlling. |
| turn_port_->SetIceRole(ICEROLE_CONTROLLING); |
| ConnectSignals(); |
| |
| if (server_address.proto == cricket::PROTO_TLS) { |
| // The test TURN server has a self-signed certificate so will not pass |
| // the normal client validation. Instruct the client to ignore certificate |
| // errors for testing only. |
| turn_port_->SetTlsCertPolicy( |
| TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK); |
| } |
| return true; |
| } |
| |
| void CreateSharedTurnPort(const std::string& username, |
| const std::string& password, |
| const ProtocolAddress& server_address) { |
| RTC_CHECK(server_address.proto == 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); |
| } |
| |
| RelayCredentials credentials(username, password); |
| turn_port_ = |
| TurnPort::Create(&main_, &socket_factory_, MakeNetwork(kLocalAddr1), |
| socket_.get(), kIceUfrag1, kIcePwd1, server_address, |
| credentials, 0, std::string(), nullptr); |
| // This TURN port will be the controlling. |
| turn_port_->SetIceRole(ICEROLE_CONTROLLING); |
| ConnectSignals(); |
| } |
| |
| void ConnectSignals() { |
| turn_port_->SignalPortComplete.connect(this, |
| &TurnPortTest::OnTurnPortComplete); |
| turn_port_->SignalPortError.connect(this, &TurnPortTest::OnTurnPortError); |
| turn_port_->SignalCandidateError.connect(this, |
| &TurnPortTest::OnCandidateError); |
| turn_port_->SignalUnknownAddress.connect( |
| this, &TurnPortTest::OnTurnUnknownAddress); |
| turn_port_->SignalCreatePermissionResult.connect( |
| this, &TurnPortTest::OnTurnCreatePermissionResult); |
| turn_port_->SignalTurnRefreshResult.connect( |
| this, &TurnPortTest::OnTurnRefreshResult); |
| turn_port_->SignalTurnPortClosed.connect(this, |
| &TurnPortTest::OnTurnPortClosed); |
| turn_port_->SubscribePortDestroyed( |
| [this](PortInterface* port) { OnTurnPortDestroyed(port); }); |
| } |
| |
| void CreateUdpPort() { CreateUdpPort(kLocalAddr2); } |
| |
| void CreateUdpPort(const SocketAddress& address) { |
| udp_port_ = UDPPort::Create(&main_, &socket_factory_, MakeNetwork(address), |
| 0, 0, kIceUfrag2, kIcePwd2, std::string(), |
| false, absl::nullopt); |
| // UDP port will be controlled. |
| udp_port_->SetIceRole(ICEROLE_CONTROLLED); |
| udp_port_->SignalPortComplete.connect(this, |
| &TurnPortTest::OnUdpPortComplete); |
| } |
| |
| void PrepareTurnAndUdpPorts(ProtocolType protocol_type) { |
| // turn_port_ should have been created. |
| ASSERT_TRUE(turn_port_ != nullptr); |
| turn_port_->PrepareAddress(); |
| ASSERT_TRUE_SIMULATED_WAIT( |
| turn_ready_, TimeToGetTurnCandidate(protocol_type), fake_clock_); |
| |
| CreateUdpPort(); |
| udp_port_->PrepareAddress(); |
| ASSERT_TRUE_SIMULATED_WAIT(udp_ready_, kSimulatedRtt, fake_clock_); |
| } |
| |
| // Returns the fake clock time to establish a connection over the given |
| // protocol. |
| int TimeToConnect(ProtocolType protocol_type) { |
| switch (protocol_type) { |
| case PROTO_TCP: |
| // The virtual socket server will delay by a fixed half a round trip |
| // for a TCP connection. |
| return kSimulatedRtt / 2; |
| case PROTO_TLS: |
| // TLS operates over TCP and additionally has a round of HELLO for |
| // negotiating ciphers and a round for exchanging certificates. |
| return 2 * kSimulatedRtt + TimeToConnect(PROTO_TCP); |
| case PROTO_UDP: |
| default: |
| // UDP requires no round trips to set up the connection. |
| return 0; |
| } |
| } |
| |
| // Returns the total fake clock time to establish a connection with a TURN |
| // server over the given protocol and to allocate a TURN candidate. |
| int TimeToGetTurnCandidate(ProtocolType protocol_type) { |
| // For a simple allocation, the first Allocate message will return with an |
| // error asking for credentials and will succeed after the second Allocate |
| // message. |
| return 2 * kSimulatedRtt + TimeToConnect(protocol_type); |
| } |
| |
| // Total fake clock time to do the following: |
| // 1. Connect to primary TURN server |
| // 2. Send Allocate and receive a redirect from the primary TURN server |
| // 3. Connect to alternate TURN server |
| // 4. Send Allocate and receive a request for credentials |
| // 5. Send Allocate with credentials and receive allocation |
| int TimeToGetAlternateTurnCandidate(ProtocolType protocol_type) { |
| return 3 * kSimulatedRtt + 2 * TimeToConnect(protocol_type); |
| } |
| |
| bool CheckConnectionFailedAndPruned(Connection* conn) { |
| return conn && !conn->active() && |
| conn->state() == IceCandidatePairState::FAILED; |
| } |
| |
| // Checks that `turn_port_` has a nonempty set of connections and they are all |
| // failed and pruned. |
| bool CheckAllConnectionsFailedAndPruned() { |
| auto& connections = turn_port_->connections(); |
| if (connections.empty()) { |
| return false; |
| } |
| for (const auto& kv : connections) { |
| if (!CheckConnectionFailedAndPruned(kv.second)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void TestReconstructedServerUrl(ProtocolType protocol_type, |
| const char* expected_url) { |
| turn_port_->PrepareAddress(); |
| ASSERT_TRUE_SIMULATED_WAIT( |
| turn_ready_, TimeToGetTurnCandidate(protocol_type), fake_clock_); |
| ASSERT_EQ(1U, turn_port_->Candidates().size()); |
| EXPECT_EQ(turn_port_->Candidates()[0].url(), expected_url); |
| } |
| |
| void TestTurnAlternateServer(ProtocolType protocol_type) { |
| std::vector<rtc::SocketAddress> redirect_addresses; |
| redirect_addresses.push_back(kTurnAlternateIntAddr); |
| |
| 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, |
| 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_SIMULATED_WAIT(turn_ready_, |
| TimeToGetAlternateTurnCandidate(protocol_type), |
| fake_clock_); |
| // 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(ProtocolType protocol_type) { |
| std::vector<rtc::SocketAddress> redirect_addresses; |
| redirect_addresses.push_back(kTurnIPv6IntAddr); |
| |
| TestTurnRedirector redirector(redirect_addresses); |
| turn_server_.AddInternalSocket(kTurnIntAddr, protocol_type); |
| turn_server_.set_redirect_hook(&redirector); |
| CreateTurnPort(kTurnUsername, kTurnPassword, |
| ProtocolAddress(kTurnIntAddr, protocol_type)); |
| turn_port_->PrepareAddress(); |
| // Need time to connect to TURN server, send Allocate request and receive |
| // redirect notice. |
| EXPECT_TRUE_SIMULATED_WAIT( |
| turn_error_, kSimulatedRtt + TimeToConnect(protocol_type), fake_clock_); |
| } |
| |
| void TestTurnAlternateServerPingPong(ProtocolType protocol_type) { |
| std::vector<rtc::SocketAddress> redirect_addresses; |
| redirect_addresses.push_back(kTurnAlternateIntAddr); |
| redirect_addresses.push_back(kTurnIntAddr); |
| |
| 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, |
| ProtocolAddress(kTurnIntAddr, protocol_type)); |
| |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_error_, |
| TimeToGetAlternateTurnCandidate(protocol_type), |
| fake_clock_); |
| 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(ProtocolType protocol_type) { |
| std::vector<rtc::SocketAddress> redirect_addresses; |
| redirect_addresses.push_back(kTurnAlternateIntAddr); |
| redirect_addresses.push_back(kTurnAlternateIntAddr); |
| |
| 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, |
| ProtocolAddress(kTurnIntAddr, protocol_type)); |
| |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_error_, |
| TimeToGetAlternateTurnCandidate(protocol_type), |
| fake_clock_); |
| ASSERT_EQ(0U, turn_port_->Candidates().size()); |
| } |
| |
| // A certain security exploit works by redirecting to a loopback address, |
| // which doesn't ever actually make sense. So redirects to loopback should |
| // be treated as errors. |
| // See: https://bugs.chromium.org/p/chromium/issues/detail?id=649118 |
| void TestTurnAlternateServerLoopback(ProtocolType protocol_type, bool ipv6) { |
| const SocketAddress& local_address = ipv6 ? kLocalIPv6Addr : kLocalAddr1; |
| const SocketAddress& server_address = |
| ipv6 ? kTurnIPv6IntAddr : kTurnIntAddr; |
| |
| std::vector<rtc::SocketAddress> redirect_addresses; |
| // Pick an unusual address in the 127.0.0.0/8 range to make sure more than |
| // 127.0.0.1 is covered. |
| SocketAddress loopback_address(ipv6 ? "::1" : "127.1.2.3", |
| TURN_SERVER_PORT); |
| redirect_addresses.push_back(loopback_address); |
| |
| // Make a socket and bind it to the local port, to make extra sure no |
| // packet is sent to this address. |
| std::unique_ptr<rtc::Socket> loopback_socket(ss_->CreateSocket( |
| AF_INET, protocol_type == PROTO_UDP ? SOCK_DGRAM : SOCK_STREAM)); |
| ASSERT_NE(nullptr, loopback_socket.get()); |
| ASSERT_EQ(0, loopback_socket->Bind(loopback_address)); |
| if (protocol_type == PROTO_TCP) { |
| ASSERT_EQ(0, loopback_socket->Listen(1)); |
| } |
| |
| TestTurnRedirector redirector(redirect_addresses); |
| |
| turn_server_.AddInternalSocket(server_address, protocol_type); |
| turn_server_.set_redirect_hook(&redirector); |
| CreateTurnPort(local_address, kTurnUsername, kTurnPassword, |
| ProtocolAddress(server_address, protocol_type)); |
| |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT( |
| turn_error_, TimeToGetTurnCandidate(protocol_type), fake_clock_); |
| |
| // Wait for some extra time, and make sure no packets were received on the |
| // loopback port we created (or in the case of TCP, no connection attempt |
| // occurred). |
| SIMULATED_WAIT(false, kSimulatedRtt, fake_clock_); |
| if (protocol_type == PROTO_UDP) { |
| char buf[1]; |
| EXPECT_EQ(-1, loopback_socket->Recv(&buf, 1, nullptr)); |
| } else { |
| std::unique_ptr<rtc::Socket> accepted_socket( |
| loopback_socket->Accept(nullptr)); |
| EXPECT_EQ(nullptr, accepted_socket.get()); |
| } |
| } |
| |
| void TestTurnConnection(ProtocolType protocol_type) { |
| // Create ports and prepare addresses. |
| PrepareTurnAndUdpPorts(protocol_type); |
| |
| // Send ping from UDP to TURN. |
| ASSERT_GE(turn_port_->Candidates().size(), 1U); |
| Connection* conn1 = udp_port_->CreateConnection(turn_port_->Candidates()[0], |
| Port::ORIGIN_MESSAGE); |
| ASSERT_TRUE(conn1 != NULL); |
| conn1->Ping(0); |
| SIMULATED_WAIT(!turn_unknown_address_, kSimulatedRtt * 2, fake_clock_); |
| 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_SIMULATED_WAIT(turn_create_permission_success_, kSimulatedRtt, |
| fake_clock_); |
| conn2->Ping(0); |
| |
| // Two hops from TURN port to UDP port through TURN server, thus two RTTs. |
| EXPECT_EQ_SIMULATED_WAIT(Connection::STATE_WRITABLE, conn2->write_state(), |
| kSimulatedRtt * 2, fake_clock_); |
| 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_SIMULATED_WAIT(Connection::STATE_WRITABLE, conn1->write_state(), |
| kSimulatedRtt * 2, fake_clock_); |
| EXPECT_TRUE(conn2->receiving()); |
| } |
| |
| void TestDestroyTurnConnection() { |
| PrepareTurnAndUdpPorts(PROTO_UDP); |
| |
| // Create connections on both ends. |
| Connection* conn1 = udp_port_->CreateConnection(turn_port_->Candidates()[0], |
| Port::ORIGIN_MESSAGE); |
| Connection* conn2 = turn_port_->CreateConnection(udp_port_->Candidates()[0], |
| Port::ORIGIN_MESSAGE); |
| |
| // Increased to 10 minutes, to ensure that the TurnEntry times out before |
| // the TurnPort. |
| turn_port_->set_timeout_delay(10 * 60 * 1000); |
| |
| ASSERT_TRUE(conn2 != NULL); |
| ASSERT_TRUE_SIMULATED_WAIT(turn_create_permission_success_, kSimulatedRtt, |
| fake_clock_); |
| // Make sure turn connection can receive. |
| conn1->Ping(0); |
| EXPECT_EQ_SIMULATED_WAIT(Connection::STATE_WRITABLE, conn1->write_state(), |
| kSimulatedRtt * 2, fake_clock_); |
| EXPECT_FALSE(turn_unknown_address_); |
| |
| // Destroy the connection on the TURN port. The TurnEntry still exists, so |
| // the TURN port should still process a ping from an unknown address. |
| conn2->Destroy(); |
| conn1->Ping(0); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_unknown_address_, kSimulatedRtt, |
| fake_clock_); |
| |
| // Wait for TurnEntry to expire. Timeout is 5 minutes. |
| // Expect that it still processes an incoming ping and signals the |
| // unknown address. |
| turn_unknown_address_ = false; |
| fake_clock_.AdvanceTime(webrtc::TimeDelta::Seconds(5 * 60)); |
| conn1->Ping(0); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_unknown_address_, kSimulatedRtt, |
| fake_clock_); |
| |
| // If the connection is created again, it will start to receive pings. |
| conn2 = turn_port_->CreateConnection(udp_port_->Candidates()[0], |
| Port::ORIGIN_MESSAGE); |
| conn1->Ping(0); |
| EXPECT_TRUE_SIMULATED_WAIT(conn2->receiving(), kSimulatedRtt, fake_clock_); |
| } |
| |
| void TestTurnSendData(ProtocolType protocol_type) { |
| PrepareTurnAndUdpPorts(protocol_type); |
| |
| // 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_SIMULATED_WAIT(Connection::STATE_WRITABLE, conn1->write_state(), |
| kSimulatedRtt * 2, fake_clock_); |
| conn2->Ping(0); |
| EXPECT_EQ_SIMULATED_WAIT(Connection::STATE_WRITABLE, conn2->write_state(), |
| kSimulatedRtt * 2, fake_clock_); |
| |
| // 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); |
| SIMULATED_WAIT(false, kSimulatedRtt, fake_clock_); |
| } |
| |
| // Check the data. |
| ASSERT_EQ(num_packets, turn_packets_.size()); |
| ASSERT_EQ(num_packets, udp_packets_.size()); |
| 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]); |
| } |
| } |
| |
| // Test that a TURN allocation is released when the port is closed. |
| void TestTurnReleaseAllocation(ProtocolType protocol_type) { |
| PrepareTurnAndUdpPorts(protocol_type); |
| turn_port_.reset(); |
| EXPECT_EQ_SIMULATED_WAIT(0U, turn_server_.server()->allocations().size(), |
| kSimulatedRtt, fake_clock_); |
| } |
| |
| // Test that the TURN allocation is released by sending a refresh request |
| // with lifetime 0 when Release is called. |
| void TestTurnGracefulReleaseAllocation(ProtocolType protocol_type) { |
| PrepareTurnAndUdpPorts(protocol_type); |
| |
| // 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_SIMULATED_WAIT(Connection::STATE_WRITABLE, conn1->write_state(), |
| kSimulatedRtt * 2, fake_clock_); |
| conn2->Ping(0); |
| EXPECT_EQ_SIMULATED_WAIT(Connection::STATE_WRITABLE, conn2->write_state(), |
| kSimulatedRtt * 2, fake_clock_); |
| |
| // Send some data from Udp to TurnPort. |
| unsigned char buf[256] = {0}; |
| conn2->Send(buf, sizeof(buf), options); |
| |
| // Now release the TurnPort allocation. |
| // This will send a REFRESH with lifetime 0 to server. |
| turn_port_->Release(); |
| |
| // Wait for the TurnPort to signal closed. |
| ASSERT_TRUE_SIMULATED_WAIT(turn_port_closed_, kSimulatedRtt, fake_clock_); |
| |
| // But the data should have arrived first. |
| ASSERT_EQ(1ul, turn_packets_.size()); |
| EXPECT_EQ(sizeof(buf), turn_packets_[0].size()); |
| |
| // The allocation is released at server. |
| EXPECT_EQ(0U, turn_server_.server()->allocations().size()); |
| } |
| |
| protected: |
| rtc::ScopedFakeClock fake_clock_; |
| // 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<TurnPortTestVirtualSocketServer> ss_; |
| rtc::AutoSocketServerThread main_; |
| rtc::BasicPacketSocketFactory socket_factory_; |
| std::unique_ptr<rtc::AsyncPacketSocket> socket_; |
| TestTurnServer turn_server_; |
| std::unique_ptr<TurnPort> turn_port_; |
| std::unique_ptr<UDPPort> udp_port_; |
| bool turn_ready_; |
| bool turn_error_; |
| bool turn_unknown_address_; |
| bool turn_create_permission_success_; |
| bool turn_port_closed_; |
| bool turn_port_destroyed_; |
| bool udp_ready_; |
| bool test_finish_; |
| bool turn_refresh_success_ = false; |
| std::vector<rtc::Buffer> turn_packets_; |
| std::vector<rtc::Buffer> udp_packets_; |
| rtc::PacketOptions options; |
| std::unique_ptr<webrtc::TurnCustomizer> turn_customizer_; |
| cricket::IceCandidateErrorEvent error_event_; |
| }; |
| |
| TEST_F(TurnPortTest, TestTurnPortType) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| EXPECT_EQ(cricket::RELAY_PORT_TYPE, turn_port_->Type()); |
| } |
| |
| // Tests that the URL of the servers can be correctly reconstructed when |
| // gathering the candidates. |
| TEST_F(TurnPortTest, TestReconstructedServerUrlForUdpIPv4) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| TestReconstructedServerUrl(PROTO_UDP, "turn:99.99.99.3:3478?transport=udp"); |
| } |
| |
| TEST_F(TurnPortTest, TestReconstructedServerUrlForUdpIPv6) { |
| turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, PROTO_UDP); |
| CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, |
| kTurnUdpIPv6ProtoAddr); |
| TestReconstructedServerUrl( |
| PROTO_UDP, |
| "turn:2400:4030:1:2c00:be30:abcd:efab:cdef:3478?transport=udp"); |
| } |
| |
| TEST_F(TurnPortTest, TestReconstructedServerUrlForTcp) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); |
| TestReconstructedServerUrl(PROTO_TCP, "turn:99.99.99.4:3478?transport=tcp"); |
| } |
| |
| TEST_F(TurnPortTest, TestReconstructedServerUrlForTls) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr); |
| TestReconstructedServerUrl(PROTO_TLS, "turns:99.99.99.4:3478?transport=tcp"); |
| } |
| |
| // 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_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_); |
| 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()); |
| } |
| |
| class TurnLoggingIdValidator : public StunMessageObserver { |
| public: |
| explicit TurnLoggingIdValidator(const char* expect_val) |
| : expect_val_(expect_val) {} |
| ~TurnLoggingIdValidator() {} |
| void ReceivedMessage(const TurnMessage* msg) override { |
| if (msg->type() == cricket::STUN_ALLOCATE_REQUEST) { |
| const StunByteStringAttribute* attr = |
| msg->GetByteString(cricket::STUN_ATTR_TURN_LOGGING_ID); |
| if (expect_val_) { |
| ASSERT_NE(nullptr, attr); |
| ASSERT_EQ(expect_val_, attr->GetString()); |
| } else { |
| EXPECT_EQ(nullptr, attr); |
| } |
| } |
| } |
| void ReceivedChannelData(const char* data, size_t size) override {} |
| |
| private: |
| const char* expect_val_; |
| }; |
| |
| TEST_F(TurnPortTest, TestTurnAllocateWithLoggingId) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| turn_port_->SetTurnLoggingId("KESO"); |
| turn_server_.server()->SetStunMessageObserver( |
| std::make_unique<TurnLoggingIdValidator>("KESO")); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_); |
| 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, TestTurnAllocateWithoutLoggingId) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| turn_server_.server()->SetStunMessageObserver( |
| std::make_unique<TurnLoggingIdValidator>(nullptr)); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_); |
| 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 bad credentials. |
| TEST_F(TurnPortTest, TestTurnBadCredentials) { |
| CreateTurnPort(kTurnUsername, "bad", kTurnUdpProtoAddr); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_error_, kSimulatedRtt * 3, fake_clock_); |
| ASSERT_EQ(0U, turn_port_->Candidates().size()); |
| EXPECT_EQ_SIMULATED_WAIT(error_event_.error_code, STUN_ERROR_UNAUTHORIZED, |
| kSimulatedRtt * 3, fake_clock_); |
| EXPECT_EQ(error_event_.error_text, "Unauthorized"); |
| } |
| |
| // Testing a normal UDP allocation using TCP connection. |
| TEST_F(TurnPortTest, TestTurnTcpAllocate) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); |
| EXPECT_EQ(0, turn_port_->SetOption(rtc::Socket::OPT_SNDBUF, 10 * 1024)); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 3, fake_clock_); |
| 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) { |
| SocketAddress local_address("127.0.0.1", 0); |
| // After calling this, when TurnPort attempts to get a socket bound to |
| // kLocalAddr, it will end up using localhost instead. |
| ss_->SetAlternativeLocalAddress(kLocalAddr1.ipaddr(), local_address.ipaddr()); |
| |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); |
| CreateTurnPort(kLocalAddr1, kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); |
| EXPECT_EQ(0, turn_port_->SetOption(rtc::Socket::OPT_SNDBUF, 10 * 1024)); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 3, fake_clock_); |
| 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()); |
| |
| // Verify that the socket actually used localhost, otherwise this test isn't |
| // doing what it meant to. |
| ASSERT_EQ(local_address.ipaddr(), |
| turn_port_->Candidates()[0].related_address().ipaddr()); |
| } |
| |
| // If the address the socket ends up bound to does not match any address of the |
| // TurnPort's Network, then the socket should be discarded and no candidates |
| // should be signaled. In the context of ICE, where one TurnPort is created for |
| // each Network, when this happens it's likely that the unexpected address is |
| // associated with some other Network, which another TurnPort is already |
| // covering. |
| TEST_F(TurnPortTest, |
| TurnTcpAllocationDiscardedIfBoundAddressDoesNotMatchNetwork) { |
| // Sockets bound to kLocalAddr1 will actually end up with kLocalAddr2. |
| ss_->SetAlternativeLocalAddress(kLocalAddr1.ipaddr(), kLocalAddr2.ipaddr()); |
| |
| // Set up TURN server to use TCP (this logic only exists for TCP). |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); |
| |
| // Create TURN port and tell it to start allocation. |
| CreateTurnPort(kLocalAddr1, kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); |
| turn_port_->PrepareAddress(); |
| |
| // Shouldn't take more than 1 RTT to realize the bound address isn't the one |
| // expected. |
| EXPECT_TRUE_SIMULATED_WAIT(turn_error_, kSimulatedRtt, fake_clock_); |
| EXPECT_EQ_SIMULATED_WAIT(error_event_.error_code, STUN_ERROR_GLOBAL_FAILURE, |
| kSimulatedRtt, fake_clock_); |
| ASSERT_NE(error_event_.error_text.find('.'), std::string::npos); |
| ASSERT_NE(error_event_.address.find(kLocalAddr2.HostAsSensitiveURIString()), |
| std::string::npos); |
| ASSERT_NE(error_event_.port, 0); |
| std::string server_url = |
| "turn:" + kTurnTcpIntAddr.ToString() + "?transport=tcp"; |
| ASSERT_EQ(error_event_.url, server_url); |
| } |
| |
| // 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(TurnPortTest, TurnTcpAllocationNotDiscardedIfNotBoundToBestIP) { |
| // Sockets bound to kLocalAddr1 will actually end up with kLocalAddr2. |
| ss_->SetAlternativeLocalAddress(kLocalAddr1.ipaddr(), kLocalAddr2.ipaddr()); |
| |
| // Set up a network with kLocalAddr1 as the "best" IP, and kLocalAddr2 as an |
| // alternate. |
| rtc::Network* network = MakeNetwork(kLocalAddr1); |
| network->AddIP(kLocalAddr2.ipaddr()); |
| ASSERT_EQ(kLocalAddr1.ipaddr(), network->GetBestIP()); |
| |
| // Set up TURN server to use TCP (this logic only exists for TCP). |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); |
| |
| // Create TURN port using our special Network, and tell it to start |
| // allocation. |
| CreateTurnPortWithNetwork(network, kTurnUsername, kTurnPassword, |
| kTurnTcpProtoAddr); |
| turn_port_->PrepareAddress(); |
| |
| // Candidate should be gathered as normally. |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 3, fake_clock_); |
| ASSERT_EQ(1U, turn_port_->Candidates().size()); |
| |
| // Verify that the socket actually used the alternate address, otherwise this |
| // test isn't doing what it meant to. |
| ASSERT_EQ(kLocalAddr2.ipaddr(), |
| turn_port_->Candidates()[0].related_address().ipaddr()); |
| } |
| |
| // Regression test for crbug.com/webrtc/8972, caused by buggy comparison |
| // between rtc::IPAddress and rtc::InterfaceAddress. |
| TEST_F(TurnPortTest, TCPPortNotDiscardedIfBoundToTemporaryIP) { |
| networks_.emplace_back("unittest", "unittest", kLocalIPv6Addr.ipaddr(), 32); |
| networks_.back().AddIP(rtc::InterfaceAddress( |
| kLocalIPv6Addr.ipaddr(), rtc::IPV6_ADDRESS_FLAG_TEMPORARY)); |
| |
| // Set up TURN server to use TCP (this logic only exists for TCP). |
| turn_server_.AddInternalSocket(kTurnIPv6IntAddr, PROTO_TCP); |
| |
| // Create TURN port using our special Network, and tell it to start |
| // allocation. |
| CreateTurnPortWithNetwork( |
| &networks_.back(), kTurnUsername, kTurnPassword, |
| cricket::ProtocolAddress(kTurnIPv6IntAddr, PROTO_TCP)); |
| turn_port_->PrepareAddress(); |
| |
| // Candidate should be gathered as normally. |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 3, fake_clock_); |
| ASSERT_EQ(1U, turn_port_->Candidates().size()); |
| } |
| |
| // Testing turn port will attempt to create TCP socket on address resolution |
| // failure. |
| TEST_F(TurnPortTest, TestTurnTcpOnAddressResolveFailure) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); |
| CreateTurnPort(kTurnUsername, kTurnPassword, |
| ProtocolAddress(kTurnInvalidAddr, PROTO_TCP)); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_WAIT(turn_error_, kResolverTimeout); |
| // 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()); |
| EXPECT_EQ_SIMULATED_WAIT(error_event_.error_code, SERVER_NOT_REACHABLE_ERROR, |
| kSimulatedRtt, fake_clock_); |
| std::string server_url = |
| "turn:" + kTurnInvalidAddr.ToString() + "?transport=tcp"; |
| ASSERT_EQ(error_event_.url, server_url); |
| } |
| |
| // Testing turn port will attempt to create TLS socket on address resolution |
| // failure. |
| TEST_F(TurnPortTest, TestTurnTlsOnAddressResolveFailure) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS); |
| CreateTurnPort(kTurnUsername, kTurnPassword, |
| ProtocolAddress(kTurnInvalidAddr, PROTO_TLS)); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_WAIT(turn_error_, kResolverTimeout); |
| 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, TestTurnUdpOnAddressResolveFailure) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, |
| ProtocolAddress(kTurnInvalidAddr, PROTO_UDP)); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_WAIT(turn_error_, kResolverTimeout); |
| // 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_SIMULATED_WAIT(turn_error_, kSimulatedRtt * 2, fake_clock_); |
| ASSERT_EQ(0U, turn_port_->Candidates().size()); |
| } |
| |
| // Tests that TURN port nonce will be reset when receiving an ALLOCATE MISMATCH |
| // error. |
| TEST_F(TurnPortTest, TestTurnAllocateNonceResetAfterAllocateMismatch) { |
| // Do a normal allocation first. |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_); |
| rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress()); |
| // Destroy the turnport while keeping the drop probability to 1 to |
| // suppress the release of the allocation at the server. |
| ss_->set_drop_probability(1.0); |
| turn_port_.reset(); |
| SIMULATED_WAIT(false, kSimulatedRtt, fake_clock_); |
| ss_->set_drop_probability(0.0); |
| |
| // Force the socket server to assign the same port. |
| ss_->SetNextPortForTesting(first_addr.port()); |
| turn_ready_ = false; |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| |
| // It is expected that the turn port will first get a nonce from the server |
| // using timestamp `ts_before` but then get an allocate mismatch error and |
| // receive an even newer nonce based on the system clock. `ts_before` is |
| // chosen so that the two NONCEs generated by the server will be different. |
| int64_t ts_before = rtc::TimeMillis() - 1; |
| std::string first_nonce = |
| turn_server_.server()->SetTimestampForNextNonce(ts_before); |
| turn_port_->PrepareAddress(); |
| |
| // Four round trips; first we'll get "stale nonce", then |
| // "allocate mismatch", then "stale nonce" again, then finally it will |
| // succeed. |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 4, fake_clock_); |
| EXPECT_NE(first_nonce, turn_port_->nonce()); |
| } |
| |
| // 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_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_); |
| 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()); |
| |
| // Four round trips; first we'll get "stale nonce", then |
| // "allocate mismatch", then "stale nonce" again, then finally it will |
| // succeed. |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 4, fake_clock_); |
| |
| // Verifies that the new port has a different address now. |
| EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress()); |
| |
| // Verify that all packets received from the shared socket are ignored. |
| std::string test_packet = "Test packet"; |
| EXPECT_FALSE(turn_port_->HandleIncomingPacket( |
| socket_.get(), test_packet.data(), test_packet.size(), |
| rtc::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0), rtc::TimeMicros())); |
| } |
| |
| // 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_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_); |
| 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(); |
| // Extra 2 round trips due to allocate mismatch. |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 4, fake_clock_); |
| |
| // 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, PROTO_TCP); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); |
| |
| // Do a normal allocation first. |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 3, fake_clock_); |
| 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()); |
| |
| // Extra 2 round trips due to allocate mismatch. |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 5, fake_clock_); |
| |
| // Verifies that the new port has a different address now. |
| EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress()); |
| } |
| |
| TEST_F(TurnPortTest, TestRefreshRequestGetsErrorResponse) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| PrepareTurnAndUdpPorts(PROTO_UDP); |
| turn_port_->CreateConnection(udp_port_->Candidates()[0], |
| Port::ORIGIN_MESSAGE); |
| // Set bad credentials. |
| RelayCredentials bad_credentials("bad_user", "bad_pwd"); |
| turn_port_->set_credentials(bad_credentials); |
| turn_refresh_success_ = false; |
| // This sends out the first RefreshRequest with correct credentials. |
| // When this succeeds, it will schedule a new RefreshRequest with the bad |
| // credential. |
| turn_port_->FlushRequests(TURN_REFRESH_REQUEST); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_refresh_success_, kSimulatedRtt, fake_clock_); |
| // Flush it again, it will receive a bad response. |
| turn_port_->FlushRequests(TURN_REFRESH_REQUEST); |
| EXPECT_TRUE_SIMULATED_WAIT(!turn_refresh_success_, kSimulatedRtt, |
| fake_clock_); |
| EXPECT_FALSE(turn_port_->connected()); |
| EXPECT_TRUE(CheckAllConnectionsFailedAndPruned()); |
| EXPECT_FALSE(turn_port_->HasRequests()); |
| } |
| |
| // Test that TurnPort will not handle any incoming packets once it has been |
| // closed. |
| TEST_F(TurnPortTest, TestStopProcessingPacketsAfterClosed) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| PrepareTurnAndUdpPorts(PROTO_UDP); |
| 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); |
| // Make sure conn2 is writable. |
| conn2->Ping(0); |
| EXPECT_EQ_SIMULATED_WAIT(Connection::STATE_WRITABLE, conn2->write_state(), |
| kSimulatedRtt * 2, fake_clock_); |
| |
| turn_port_->Close(); |
| SIMULATED_WAIT(false, kSimulatedRtt, fake_clock_); |
| turn_unknown_address_ = false; |
| conn2->Ping(0); |
| SIMULATED_WAIT(false, kSimulatedRtt, fake_clock_); |
| // Since the turn port does not handle packets any more, it should not |
| // SignalUnknownAddress. |
| EXPECT_FALSE(turn_unknown_address_); |
| } |
| |
| // Test that CreateConnection will return null if port becomes disconnected. |
| TEST_F(TurnPortTest, TestCreateConnectionWhenSocketClosed) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); |
| PrepareTurnAndUdpPorts(PROTO_TCP); |
| // 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); |
| } |
| |
| // Tests that when a TCP socket is closed, the respective TURN connection will |
| // be destroyed. |
| TEST_F(TurnPortTest, TestSocketCloseWillDestroyConnection) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); |
| PrepareTurnAndUdpPorts(PROTO_TCP); |
| Connection* conn = turn_port_->CreateConnection(udp_port_->Candidates()[0], |
| Port::ORIGIN_MESSAGE); |
| EXPECT_NE(nullptr, conn); |
| EXPECT_TRUE(!turn_port_->connections().empty()); |
| turn_port_->socket()->SignalClose(turn_port_->socket(), 1); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_port_->connections().empty(), |
| kConnectionDestructionDelay, fake_clock_); |
| } |
| |
| // Test try-alternate-server feature. |
| TEST_F(TurnPortTest, TestTurnAlternateServerUDP) { |
| TestTurnAlternateServer(PROTO_UDP); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerTCP) { |
| TestTurnAlternateServer(PROTO_TCP); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerTLS) { |
| TestTurnAlternateServer(PROTO_TLS); |
| } |
| |
| // Test that we fail when we redirect to an address different from |
| // current IP family. |
| TEST_F(TurnPortTest, TestTurnAlternateServerV4toV6UDP) { |
| TestTurnAlternateServerV4toV6(PROTO_UDP); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerV4toV6TCP) { |
| TestTurnAlternateServerV4toV6(PROTO_TCP); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerV4toV6TLS) { |
| TestTurnAlternateServerV4toV6(PROTO_TLS); |
| } |
| |
| // Test try-alternate-server catches the case of pingpong. |
| TEST_F(TurnPortTest, TestTurnAlternateServerPingPongUDP) { |
| TestTurnAlternateServerPingPong(PROTO_UDP); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerPingPongTCP) { |
| TestTurnAlternateServerPingPong(PROTO_TCP); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerPingPongTLS) { |
| TestTurnAlternateServerPingPong(PROTO_TLS); |
| } |
| |
| // Test try-alternate-server catch the case of repeated server. |
| TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetitionUDP) { |
| TestTurnAlternateServerDetectRepetition(PROTO_UDP); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetitionTCP) { |
| TestTurnAlternateServerDetectRepetition(PROTO_TCP); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetitionTLS) { |
| TestTurnAlternateServerDetectRepetition(PROTO_TCP); |
| } |
| |
| // Test catching the case of a redirect to loopback. |
| TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackUdpIpv4) { |
| TestTurnAlternateServerLoopback(PROTO_UDP, false); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackUdpIpv6) { |
| TestTurnAlternateServerLoopback(PROTO_UDP, true); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackTcpIpv4) { |
| TestTurnAlternateServerLoopback(PROTO_TCP, false); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackTcpIpv6) { |
| TestTurnAlternateServerLoopback(PROTO_TCP, true); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackTlsIpv4) { |
| TestTurnAlternateServerLoopback(PROTO_TLS, false); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackTlsIpv6) { |
| TestTurnAlternateServerLoopback(PROTO_TLS, true); |
| } |
| |
| // 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(PROTO_UDP); |
| } |
| |
| // Similar to above, except that this test will use the shared socket. |
| TEST_F(TurnPortTest, TestTurnConnectionUsingSharedSocket) { |
| CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| TestTurnConnection(PROTO_UDP); |
| } |
| |
| // Test that we can establish a TCP connection with TURN server. |
| TEST_F(TurnPortTest, TestTurnTcpConnection) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); |
| TestTurnConnection(PROTO_TCP); |
| } |
| |
| // Test that we can establish a TLS connection with TURN server. |
| TEST_F(TurnPortTest, TestTurnTlsConnection) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr); |
| TestTurnConnection(PROTO_TLS); |
| } |
| |
| // Test that if a connection on a TURN port is destroyed, the TURN port can |
| // still receive ping on that connection as if it is from an unknown address. |
| // If the connection is created again, it will be used to receive ping. |
| TEST_F(TurnPortTest, TestDestroyTurnConnection) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| TestDestroyTurnConnection(); |
| } |
| |
| // Similar to above, except that this test will use the shared socket. |
| TEST_F(TurnPortTest, TestDestroyTurnConnectionUsingSharedSocket) { |
| CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| TestDestroyTurnConnection(); |
| } |
| |
| // 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(PROTO_UDP); |
| } |
| |
| // Test that CreatePermissionRequest will be scheduled after the success |
| // of the first create permission request and the request will get an |
| // ErrorResponse if the ufrag and pwd are incorrect. |
| TEST_F(TurnPortTest, TestRefreshCreatePermissionRequest) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| PrepareTurnAndUdpPorts(PROTO_UDP); |
| |
| Connection* conn = turn_port_->CreateConnection(udp_port_->Candidates()[0], |
| Port::ORIGIN_MESSAGE); |
| ASSERT_TRUE(conn != NULL); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_create_permission_success_, kSimulatedRtt, |
| fake_clock_); |
| turn_create_permission_success_ = false; |
| // A create-permission-request should be pending. |
| // After the next create-permission-response is received, it will schedule |
| // another request with bad_ufrag and bad_pwd. |
| RelayCredentials bad_credentials("bad_user", "bad_pwd"); |
| turn_port_->set_credentials(bad_credentials); |
| turn_port_->FlushRequests(kAllRequests); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_create_permission_success_, kSimulatedRtt, |
| fake_clock_); |
| // Flush the requests again; the create-permission-request will fail. |
| turn_port_->FlushRequests(kAllRequests); |
| EXPECT_TRUE_SIMULATED_WAIT(!turn_create_permission_success_, kSimulatedRtt, |
| fake_clock_); |
| EXPECT_TRUE(CheckConnectionFailedAndPruned(conn)); |
| } |
| |
| TEST_F(TurnPortTest, TestChannelBindGetErrorResponse) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| PrepareTurnAndUdpPorts(PROTO_UDP); |
| Connection* conn1 = turn_port_->CreateConnection(udp_port_->Candidates()[0], |
| Port::ORIGIN_MESSAGE); |
| ASSERT_TRUE(conn1 != nullptr); |
| Connection* conn2 = udp_port_->CreateConnection(turn_port_->Candidates()[0], |
| Port::ORIGIN_MESSAGE); |
| |
| ASSERT_TRUE(conn2 != nullptr); |
| conn1->Ping(0); |
| EXPECT_TRUE_SIMULATED_WAIT(conn1->writable(), kSimulatedRtt * 2, fake_clock_); |
| // TODO(deadbeef): SetEntryChannelId should not be a public method. |
| // Instead we should set an option on the fake TURN server to force it to |
| // send a channel bind errors. |
| ASSERT_TRUE( |
| turn_port_->SetEntryChannelId(udp_port_->Candidates()[0].address(), -1)); |
| |
| std::string data = "ABC"; |
| conn1->Send(data.data(), data.length(), options); |
| |
| EXPECT_TRUE_SIMULATED_WAIT(CheckConnectionFailedAndPruned(conn1), |
| kSimulatedRtt, fake_clock_); |
| // Verify that packets are allowed to be sent after a bind request error. |
| // They'll just use a send indication instead. |
| conn2->SignalReadPacket.connect(static_cast<TurnPortTest*>(this), |
| &TurnPortTest::OnUdpReadPacket); |
| conn1->Send(data.data(), data.length(), options); |
| EXPECT_TRUE_SIMULATED_WAIT(!udp_packets_.empty(), kSimulatedRtt, fake_clock_); |
| } |
| |
| // 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(PROTO_UDP); |
| EXPECT_EQ(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, PROTO_TCP); |
| // Create ports and prepare addresses. |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); |
| TestTurnSendData(PROTO_TCP); |
| EXPECT_EQ(TCP_PROTOCOL_NAME, turn_port_->Candidates()[0].relay_protocol()); |
| } |
| |
| // Do a TURN allocation, establish a TLS connection, and send some data. |
| TEST_F(TurnPortTest, TestTurnSendDataTurnTlsToUdp) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr); |
| TestTurnSendData(PROTO_TLS); |
| EXPECT_EQ(TLS_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, PROTO_UDP); |
| CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, |
| kTurnUdpProtoAddr); |
| turn_port_->PrepareAddress(); |
| ASSERT_TRUE_SIMULATED_WAIT(turn_error_, kSimulatedRtt, fake_clock_); |
| 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, PROTO_UDP); |
| CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, |
| kTurnUdpIPv6ProtoAddr); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_); |
| 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()); |
| } |
| |
| // Tests that the local and remote candidate address families should match when |
| // a connection is created. Specifically, if a TURN port has an IPv6 address, |
| // its local candidate will still be an IPv4 address and it can only create |
| // connections with IPv4 remote candidates. |
| TEST_F(TurnPortTest, TestCandidateAddressFamilyMatch) { |
| turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, PROTO_UDP); |
| |
| CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, |
| kTurnUdpIPv6ProtoAddr); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_); |
| ASSERT_EQ(1U, turn_port_->Candidates().size()); |
| |
| // Create an IPv4 candidate. It will match the TURN candidate. |
| Candidate remote_candidate(ICE_CANDIDATE_COMPONENT_RTP, "udp", kLocalAddr2, 0, |
| "", "", "local", 0, kCandidateFoundation); |
| remote_candidate.set_address(kLocalAddr2); |
| Connection* conn = |
| turn_port_->CreateConnection(remote_candidate, Port::ORIGIN_MESSAGE); |
| EXPECT_NE(nullptr, conn); |
| |
| // Set the candidate address family to IPv6. It won't match the TURN |
| // candidate. |
| remote_candidate.set_address(kLocalIPv6Addr2); |
| conn = turn_port_->CreateConnection(remote_candidate, Port::ORIGIN_MESSAGE); |
| EXPECT_EQ(nullptr, conn); |
| } |
| |
| TEST_F(TurnPortTest, TestOriginHeader) { |
| CreateTurnPortWithOrigin(kLocalAddr1, kTurnUsername, kTurnPassword, |
| kTurnUdpProtoAddr, kTestOrigin); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_); |
| 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 CreatePermission failure will result in the connection being |
| // pruned and failed. |
| TEST_F(TurnPortTest, TestConnectionFailedAndPrunedOnCreatePermissionFailure) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); |
| turn_server_.server()->set_reject_private_addresses(true); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); |
| turn_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 3, fake_clock_); |
| |
| CreateUdpPort(SocketAddress("10.0.0.10", 0)); |
| udp_port_->PrepareAddress(); |
| EXPECT_TRUE_SIMULATED_WAIT(udp_ready_, kSimulatedRtt, fake_clock_); |
| // Create a connection. |
| TestConnectionWrapper conn(turn_port_->CreateConnection( |
| udp_port_->Candidates()[0], Port::ORIGIN_MESSAGE)); |
| EXPECT_TRUE(conn.connection() != nullptr); |
| |
| // Asynchronously, CreatePermission request should be sent and fail, which |
| // will make the connection pruned and failed. |
| EXPECT_TRUE_SIMULATED_WAIT(CheckConnectionFailedAndPruned(conn.connection()), |
| kSimulatedRtt, fake_clock_); |
| EXPECT_TRUE_SIMULATED_WAIT(!turn_create_permission_success_, kSimulatedRtt, |
| fake_clock_); |
| // Check that the connection is not deleted asynchronously. |
| SIMULATED_WAIT(conn.connection() == nullptr, kConnectionDestructionDelay, |
| fake_clock_); |
| EXPECT_NE(nullptr, conn.connection()); |
| } |
| |
| // Test that a TURN allocation is released when the port is closed. |
| TEST_F(TurnPortTest, TestTurnReleaseAllocation) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| TestTurnReleaseAllocation(PROTO_UDP); |
| } |
| |
| // Test that a TURN TCP allocation is released when the port is closed. |
| TEST_F(TurnPortTest, TestTurnTCPReleaseAllocation) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); |
| TestTurnReleaseAllocation(PROTO_TCP); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnTLSReleaseAllocation) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr); |
| TestTurnReleaseAllocation(PROTO_TLS); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnUDPGracefulReleaseAllocation) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_UDP); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| TestTurnGracefulReleaseAllocation(PROTO_UDP); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnTCPGracefulReleaseAllocation) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); |
| TestTurnGracefulReleaseAllocation(PROTO_TCP); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnTLSGracefulReleaseAllocation) { |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr); |
| TestTurnGracefulReleaseAllocation(PROTO_TLS); |
| } |
| |
| // Test that nothing bad happens if we try to create a connection to the same |
| // remote address twice. Previously there was a bug that caused this to hit a |
| // DCHECK. |
| TEST_F(TurnPortTest, CanCreateTwoConnectionsToSameAddress) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); |
| PrepareTurnAndUdpPorts(PROTO_UDP); |
| Connection* conn1 = turn_port_->CreateConnection(udp_port_->Candidates()[0], |
| Port::ORIGIN_MESSAGE); |
| Connection* conn2 = turn_port_->CreateConnection(udp_port_->Candidates()[0], |
| Port::ORIGIN_MESSAGE); |
| EXPECT_NE(conn1, conn2); |
| } |
| |
| // 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, PROTO_UDP); |
| int last_fd_count = GetFDCount(); |
| // Need to supply unresolved address to kick off resolver. |
| CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, |
| ProtocolAddress(rtc::SocketAddress("www.google.invalid", 3478), |
| PROTO_UDP)); |
| turn_port_->PrepareAddress(); |
| ASSERT_TRUE_WAIT(turn_error_, kResolverTimeout); |
| EXPECT_TRUE(turn_port_->Candidates().empty()); |
| turn_port_.reset(); |
| rtc::Thread::Current()->Post(RTC_FROM_HERE, this, MSG_TESTFINISH); |
| // Waiting for above message to be processed. |
| ASSERT_TRUE_SIMULATED_WAIT(test_finish_, 1, fake_clock_); |
| EXPECT_EQ(last_fd_count, GetFDCount()); |
| } |
| #endif |
| |
| class MessageObserver : public StunMessageObserver { |
| public: |
| MessageObserver(unsigned int* message_counter, |
| unsigned int* channel_data_counter, |
| unsigned int* attr_counter) |
| : message_counter_(message_counter), |
| channel_data_counter_(channel_data_counter), |
| attr_counter_(attr_counter) {} |
| virtual ~MessageObserver() {} |
| void ReceivedMessage(const TurnMessage* msg) override { |
| if (message_counter_ != nullptr) { |
| (*message_counter_)++; |
| } |
| // Implementation defined attributes are returned as ByteString |
| const StunByteStringAttribute* attr = |
| msg->GetByteString(TestTurnCustomizer::STUN_ATTR_COUNTER); |
| if (attr != nullptr && attr_counter_ != nullptr) { |
| rtc::ByteBufferReader buf(attr->bytes(), attr->length()); |
| unsigned int val = ~0u; |
| buf.ReadUInt32(&val); |
| (*attr_counter_)++; |
| } |
| } |
| |
| void ReceivedChannelData(const char* data, size_t size) override { |
| if (channel_data_counter_ != nullptr) { |
| (*channel_data_counter_)++; |
| } |
| } |
| |
| // Number of TurnMessages observed. |
| unsigned int* message_counter_ = nullptr; |
| |
| // Number of channel data observed. |
| unsigned int* channel_data_counter_ = nullptr; |
| |
| // Number of TurnMessages that had STUN_ATTR_COUNTER. |
| unsigned int* attr_counter_ = nullptr; |
| }; |
| |
| // Do a TURN allocation, establish a TLS connection, and send some data. |
| // Add customizer and check that it get called. |
| TEST_F(TurnPortTest, TestTurnCustomizerCount) { |
| unsigned int observer_message_counter = 0; |
| unsigned int observer_channel_data_counter = 0; |
| unsigned int observer_attr_counter = 0; |
| TestTurnCustomizer* customizer = new TestTurnCustomizer(); |
| std::unique_ptr<MessageObserver> validator(new MessageObserver( |
| &observer_message_counter, &observer_channel_data_counter, |
| &observer_attr_counter)); |
| |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS); |
| turn_customizer_.reset(customizer); |
| turn_server_.server()->SetStunMessageObserver(std::move(validator)); |
| |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr); |
| TestTurnSendData(PROTO_TLS); |
| EXPECT_EQ(TLS_PROTOCOL_NAME, turn_port_->Candidates()[0].relay_protocol()); |
| |
| // There should have been at least turn_packets_.size() calls to `customizer`. |
| EXPECT_GE(customizer->modify_cnt_ + customizer->allow_channel_data_cnt_, |
| turn_packets_.size()); |
| |
| // Some channel data should be received. |
| EXPECT_GE(observer_channel_data_counter, 0u); |
| |
| // Need to release TURN port before the customizer. |
| turn_port_.reset(nullptr); |
| } |
| |
| // Do a TURN allocation, establish a TLS connection, and send some data. |
| // Add customizer and check that it can can prevent usage of channel data. |
| TEST_F(TurnPortTest, TestTurnCustomizerDisallowChannelData) { |
| unsigned int observer_message_counter = 0; |
| unsigned int observer_channel_data_counter = 0; |
| unsigned int observer_attr_counter = 0; |
| TestTurnCustomizer* customizer = new TestTurnCustomizer(); |
| std::unique_ptr<MessageObserver> validator(new MessageObserver( |
| &observer_message_counter, &observer_channel_data_counter, |
| &observer_attr_counter)); |
| customizer->allow_channel_data_ = false; |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS); |
| turn_customizer_.reset(customizer); |
| turn_server_.server()->SetStunMessageObserver(std::move(validator)); |
| |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr); |
| TestTurnSendData(PROTO_TLS); |
| EXPECT_EQ(TLS_PROTOCOL_NAME, turn_port_->Candidates()[0].relay_protocol()); |
| |
| // There should have been at least turn_packets_.size() calls to `customizer`. |
| EXPECT_GE(customizer->modify_cnt_, turn_packets_.size()); |
| |
| // No channel data should be received. |
| EXPECT_EQ(observer_channel_data_counter, 0u); |
| |
| // Need to release TURN port before the customizer. |
| turn_port_.reset(nullptr); |
| } |
| |
| // Do a TURN allocation, establish a TLS connection, and send some data. |
| // Add customizer and check that it can add attribute to messages. |
| TEST_F(TurnPortTest, TestTurnCustomizerAddAttribute) { |
| unsigned int observer_message_counter = 0; |
| unsigned int observer_channel_data_counter = 0; |
| unsigned int observer_attr_counter = 0; |
| TestTurnCustomizer* customizer = new TestTurnCustomizer(); |
| std::unique_ptr<MessageObserver> validator(new MessageObserver( |
| &observer_message_counter, &observer_channel_data_counter, |
| &observer_attr_counter)); |
| customizer->allow_channel_data_ = false; |
| customizer->add_counter_ = true; |
| turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS); |
| turn_customizer_.reset(customizer); |
| turn_server_.server()->SetStunMessageObserver(std::move(validator)); |
| |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr); |
| TestTurnSendData(PROTO_TLS); |
| EXPECT_EQ(TLS_PROTOCOL_NAME, turn_port_->Candidates()[0].relay_protocol()); |
| |
| // There should have been at least turn_packets_.size() calls to `customizer`. |
| EXPECT_GE(customizer->modify_cnt_, turn_packets_.size()); |
| |
| // Everything will be sent as messages since channel data is disallowed. |
| EXPECT_GE(customizer->modify_cnt_, observer_message_counter); |
| |
| // All messages should have attribute. |
| EXPECT_EQ(observer_message_counter, observer_attr_counter); |
| |
| // At least allow_channel_data_cnt_ messages should have been sent. |
| EXPECT_GE(customizer->modify_cnt_, customizer->allow_channel_data_cnt_); |
| EXPECT_GE(customizer->allow_channel_data_cnt_, 0u); |
| |
| // No channel data should be received. |
| EXPECT_EQ(observer_channel_data_counter, 0u); |
| |
| // Need to release TURN port before the customizer. |
| turn_port_.reset(nullptr); |
| } |
| |
| TEST_F(TurnPortTest, TestOverlongUsername) { |
| std::string overlong_username(513, 'x'); |
| RelayCredentials credentials(overlong_username, kTurnPassword); |
| EXPECT_FALSE( |
| CreateTurnPort(overlong_username, kTurnPassword, kTurnTlsProtoAddr)); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnDangerousServer) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnDangerousProtoAddr); |
| ASSERT_FALSE(turn_port_); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnDangerousServerPermits53) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnPort53ProtoAddr); |
| ASSERT_TRUE(turn_port_); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnDangerousServerPermits80) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnPort80ProtoAddr); |
| ASSERT_TRUE(turn_port_); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnDangerousServerPermits443) { |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnPort443ProtoAddr); |
| ASSERT_TRUE(turn_port_); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnDangerousAlternateServer) { |
| const ProtocolType protocol_type = PROTO_TCP; |
| std::vector<rtc::SocketAddress> redirect_addresses; |
| redirect_addresses.push_back(kTurnDangerousAddr); |
| |
| TestTurnRedirector redirector(redirect_addresses); |
| |
| turn_server_.AddInternalSocket(kTurnIntAddr, protocol_type); |
| turn_server_.AddInternalSocket(kTurnDangerousAddr, protocol_type); |
| turn_server_.set_redirect_hook(&redirector); |
| CreateTurnPort(kTurnUsername, kTurnPassword, |
| 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(); |
| // This should result in an error event. |
| EXPECT_TRUE_SIMULATED_WAIT(error_event_.error_code != 0, |
| TimeToGetAlternateTurnCandidate(protocol_type), |
| fake_clock_); |
| // but should NOT result in the port turning ready, and no candidates |
| // should be gathered. |
| EXPECT_FALSE(turn_ready_); |
| ASSERT_EQ(0U, turn_port_->Candidates().size()); |
| } |
| |
| TEST_F(TurnPortTest, TestTurnDangerousServerAllowedWithFieldTrial) { |
| webrtc::test::ScopedFieldTrials override_field_trials( |
| "WebRTC-Turn-AllowSystemPorts/Enabled/"); |
| CreateTurnPort(kTurnUsername, kTurnPassword, kTurnDangerousProtoAddr); |
| ASSERT_TRUE(turn_port_); |
| } |
| |
| } // namespace cricket |