mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +01:00

RegisterReceivedPacketCallback is used instead of sigslot::SignalReadPacket. The callback use a new data class ReceivedPacket that combine meta data and packet payload from a received packet. This is the first step in an attempt to cleanup the data types used in the packet receive pipeline. Eventually, the ReceivedPacket class can contain more meta data such as ECN information. Bug: webrtc:11943,webrtc:15368 Change-Id: I984c561b9262fe4aa00176529bd8d901adf66640 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/325060 Reviewed-by: Jonas Oreland <jonaso@webrtc.org> Commit-Queue: Per Kjellander <perkj@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#41021}
1959 lines
80 KiB
C++
1959 lines
80 KiB
C++
/*
|
|
* 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>
|
|
|
|
#include "absl/strings/string_view.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/mock_dns_resolving_packet_socket_factory.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/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/net_helper.h"
|
|
#include "rtc_base/socket.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/gtest.h"
|
|
#include "test/scoped_key_value_config.h"
|
|
|
|
namespace {
|
|
using rtc::SocketAddress;
|
|
|
|
using ::testing::_;
|
|
using ::testing::DoAll;
|
|
using ::testing::Return;
|
|
using ::testing::ReturnPointee;
|
|
using ::testing::SetArgPointee;
|
|
|
|
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 SocketAddress kTurnValidAddr("www.google.valid.", 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";
|
|
// 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;
|
|
|
|
constexpr uint64_t kTiebreakerDefault = 44444;
|
|
|
|
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 cricket::ProtocolAddress kTurnPortInvalidHostnameProtoAddr(
|
|
kTurnInvalidAddr,
|
|
cricket::PROTO_UDP);
|
|
static const cricket::ProtocolAddress kTurnPortValidHostnameProtoAddr(
|
|
kTurnValidAddr,
|
|
cricket::PROTO_UDP);
|
|
|
|
#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
|
|
|
|
} // unnamed namespace
|
|
|
|
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);
|
|
}
|
|
|
|
~TestConnectionWrapper() {
|
|
if (connection_) {
|
|
connection_->SignalDestroyed.disconnect(this);
|
|
}
|
|
}
|
|
|
|
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 TurnPort::CallbacksForTest,
|
|
public sigslot::has_slots<> {
|
|
public:
|
|
TurnPortTest()
|
|
: ss_(new TurnPortTestVirtualSocketServer()),
|
|
main_(ss_.get()),
|
|
turn_server_(&main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr),
|
|
socket_factory_(ss_.get()) {
|
|
// 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));
|
|
}
|
|
|
|
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 OnUdpPortComplete(Port* port) { udp_ready_ = true; }
|
|
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 OnTurnPortDestroyed(PortInterface* port) { turn_port_destroyed_ = true; }
|
|
|
|
// TurnPort::TestCallbacks
|
|
void OnTurnCreatePermissionResult(int code) override {
|
|
turn_create_permission_success_ = (code == 0);
|
|
}
|
|
void OnTurnRefreshResult(int code) override {
|
|
turn_refresh_success_ = (code == 0);
|
|
}
|
|
void OnTurnPortClosed() override { turn_port_closed_ = true; }
|
|
|
|
void OnConnectionSignalDestroyed(Connection* connection) {
|
|
connection->DeregisterReceivedPacketCallback();
|
|
}
|
|
|
|
rtc::Socket* CreateServerSocket(const SocketAddress addr) {
|
|
rtc::Socket* socket = ss_->CreateSocket(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(absl::string_view username,
|
|
absl::string_view password,
|
|
const ProtocolAddress& server_address) {
|
|
return CreateTurnPortWithAllParams(MakeNetwork(kLocalAddr1), username,
|
|
password, server_address);
|
|
}
|
|
bool CreateTurnPort(const rtc::SocketAddress& local_address,
|
|
absl::string_view username,
|
|
absl::string_view password,
|
|
const ProtocolAddress& server_address) {
|
|
return CreateTurnPortWithAllParams(MakeNetwork(local_address), username,
|
|
password, server_address);
|
|
}
|
|
|
|
bool CreateTurnPortWithNetwork(const rtc::Network* network,
|
|
absl::string_view username,
|
|
absl::string_view password,
|
|
const ProtocolAddress& server_address) {
|
|
return CreateTurnPortWithAllParams(network, username, password,
|
|
server_address);
|
|
}
|
|
|
|
// 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(const rtc::Network* network,
|
|
absl::string_view username,
|
|
absl::string_view password,
|
|
const ProtocolAddress& server_address) {
|
|
RelayServerConfig config;
|
|
config.credentials = RelayCredentials(username, password);
|
|
CreateRelayPortArgs args;
|
|
args.network_thread = &main_;
|
|
args.socket_factory = socket_factory();
|
|
args.network = network;
|
|
args.username = kIceUfrag1;
|
|
args.password = kIcePwd1;
|
|
args.server_address = &server_address;
|
|
args.config = &config;
|
|
args.turn_customizer = turn_customizer_.get();
|
|
args.field_trials = &field_trials_;
|
|
|
|
turn_port_ = TurnPort::Create(args, 0, 0);
|
|
if (!turn_port_) {
|
|
return false;
|
|
}
|
|
// This TURN port will be the controlling.
|
|
turn_port_->SetIceRole(ICEROLE_CONTROLLING);
|
|
turn_port_->SetIceTiebreaker(kTiebreakerDefault);
|
|
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(absl::string_view username,
|
|
absl::string_view 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);
|
|
}
|
|
|
|
RelayServerConfig config;
|
|
config.credentials = RelayCredentials(username, password);
|
|
CreateRelayPortArgs args;
|
|
args.network_thread = &main_;
|
|
args.socket_factory = socket_factory();
|
|
args.network = MakeNetwork(kLocalAddr1);
|
|
args.username = kIceUfrag1;
|
|
args.password = kIcePwd1;
|
|
args.server_address = &server_address;
|
|
args.config = &config;
|
|
args.turn_customizer = turn_customizer_.get();
|
|
args.field_trials = &field_trials_;
|
|
turn_port_ = TurnPort::Create(args, socket_.get());
|
|
// This TURN port will be the controlling.
|
|
turn_port_->SetIceRole(ICEROLE_CONTROLLING);
|
|
turn_port_->SetIceTiebreaker(kTiebreakerDefault);
|
|
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_->SubscribePortDestroyed(
|
|
[this](PortInterface* port) { OnTurnPortDestroyed(port); });
|
|
turn_port_->SetCallbacksForTest(this);
|
|
}
|
|
|
|
void CreateUdpPort() { CreateUdpPort(kLocalAddr2); }
|
|
|
|
void CreateUdpPort(const SocketAddress& address) {
|
|
udp_port_ = UDPPort::Create(&main_, socket_factory(), MakeNetwork(address),
|
|
0, 0, kIceUfrag2, kIcePwd2, false,
|
|
absl::nullopt, &field_trials_);
|
|
// UDP port will be controlled.
|
|
udp_port_->SetIceRole(ICEROLE_CONTROLLED);
|
|
udp_port_->SetIceTiebreaker(kTiebreakerDefault);
|
|
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 TestTurnAllocateSucceeds(unsigned int timeout) {
|
|
ASSERT_TRUE(turn_port_);
|
|
turn_port_->PrepareAddress();
|
|
EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, timeout, 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());
|
|
}
|
|
|
|
void TestReconstructedServerUrl(ProtocolType protocol_type,
|
|
absl::string_view expected_url) {
|
|
ASSERT_TRUE(turn_port_);
|
|
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.
|
|
turn_port_->DestroyConnection(conn2);
|
|
|
|
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));
|
|
|
|
// TODO(chromium:1395625): When `TurnPort` doesn't find connection objects
|
|
// for incoming packets, it forwards calls to the parent class, `Port`. This
|
|
// happens inside `TurnPort::DispatchPacket`. The `Port` implementation may
|
|
// need to send a binding error back over a connection which, unless the
|
|
// `TurnPort` implementation handles it, could result in a null deref.
|
|
// This special check tests if dispatching messages via `TurnPort` for which
|
|
// there's no connection, results in a no-op rather than crashing.
|
|
// See `TurnPort::SendBindingErrorResponse` for the check.
|
|
// This should probably be done in a neater way both from a testing pov and
|
|
// how incoming messages are handled in the `Port` class, when an assumption
|
|
// is made about connection objects existing and when those assumptions
|
|
// may not hold.
|
|
std::string pwd = conn1->remote_password_for_test();
|
|
conn1->set_remote_password_for_test("bad");
|
|
auto msg = conn1->BuildPingRequestForTest();
|
|
|
|
rtc::ByteBufferWriter buf;
|
|
msg->Write(&buf);
|
|
conn1->Send(buf.Data(), buf.Length(), options);
|
|
|
|
// Now restore the password before continuing.
|
|
conn1->set_remote_password_for_test(pwd);
|
|
|
|
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->RegisterReceivedPacketCallback(
|
|
[&](Connection* connection, const rtc::ReceivedPacket& packet) {
|
|
turn_packets_.push_back(
|
|
rtc::Buffer(packet.payload().data(), packet.payload().size()));
|
|
});
|
|
conn1->SignalDestroyed.connect(this,
|
|
&TurnPortTest::OnConnectionSignalDestroyed);
|
|
conn2->RegisterReceivedPacketCallback(
|
|
[&](Connection* connection, const rtc::ReceivedPacket& packet) {
|
|
udp_packets_.push_back(
|
|
rtc::Buffer(packet.payload().data(), packet.payload().size()));
|
|
});
|
|
conn2->SignalDestroyed.connect(this,
|
|
&TurnPortTest::OnConnectionSignalDestroyed);
|
|
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->RegisterReceivedPacketCallback(
|
|
[&](Connection* connection, const rtc::ReceivedPacket& packet) {
|
|
turn_packets_.push_back(
|
|
rtc::Buffer(packet.payload().data(), packet.payload().size()));
|
|
});
|
|
conn1->SignalDestroyed.connect(this,
|
|
&TurnPortTest::OnConnectionSignalDestroyed);
|
|
conn2->RegisterReceivedPacketCallback(
|
|
[&](Connection* connection, const rtc::ReceivedPacket& packet) {
|
|
udp_packets_.push_back(
|
|
rtc::Buffer(packet.payload().data(), packet.payload().size()));
|
|
});
|
|
conn2->SignalDestroyed.connect(this,
|
|
&TurnPortTest::OnConnectionSignalDestroyed);
|
|
|
|
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:
|
|
virtual rtc::PacketSocketFactory* socket_factory() {
|
|
return &socket_factory_;
|
|
}
|
|
|
|
webrtc::test::ScopedKeyValueConfig field_trials_;
|
|
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_;
|
|
std::unique_ptr<rtc::AsyncPacketSocket> socket_;
|
|
TestTurnServer turn_server_;
|
|
std::unique_ptr<TurnPort> turn_port_;
|
|
std::unique_ptr<UDPPort> udp_port_;
|
|
bool turn_ready_ = false;
|
|
bool turn_error_ = false;
|
|
bool turn_unknown_address_ = false;
|
|
bool turn_create_permission_success_ = false;
|
|
bool turn_port_closed_ = false;
|
|
bool turn_port_destroyed_ = false;
|
|
bool udp_ready_ = false;
|
|
bool test_finish_ = false;
|
|
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_;
|
|
|
|
private:
|
|
rtc::BasicPacketSocketFactory socket_factory_;
|
|
};
|
|
|
|
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");
|
|
}
|
|
|
|
TEST_F(TurnPortTest, TestReconstructedServerUrlForHostname) {
|
|
CreateTurnPort(kTurnUsername, kTurnPassword,
|
|
kTurnPortInvalidHostnameProtoAddr);
|
|
// This test follows the pattern from TestTurnTcpOnAddressResolveFailure.
|
|
// As VSS doesn't provide DNS resolution, name resolve will fail,
|
|
// the error will be set and contain the url.
|
|
turn_port_->PrepareAddress();
|
|
EXPECT_TRUE_WAIT(turn_error_, kResolverTimeout);
|
|
std::string server_url =
|
|
"turn:" + kTurnInvalidAddr.ToString() + "?transport=udp";
|
|
ASSERT_EQ(error_event_.url, server_url);
|
|
}
|
|
|
|
// 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));
|
|
TestTurnAllocateSucceeds(kSimulatedRtt * 2);
|
|
}
|
|
|
|
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->string_view());
|
|
} 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"));
|
|
TestTurnAllocateSucceeds(kSimulatedRtt * 2);
|
|
}
|
|
|
|
TEST_F(TurnPortTest, TestTurnAllocateWithoutLoggingId) {
|
|
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
|
|
turn_server_.server()->SetStunMessageObserver(
|
|
std::make_unique<TurnLoggingIdValidator>(nullptr));
|
|
TestTurnAllocateSucceeds(kSimulatedRtt * 2);
|
|
}
|
|
|
|
// 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));
|
|
TestTurnAllocateSucceeds(kSimulatedRtt * 3);
|
|
}
|
|
|
|
// 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));
|
|
TestTurnAllocateSucceeds(kSimulatedRtt * 3);
|
|
|
|
// 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 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_->request_manager().FlushForTest(TURN_REFRESH_REQUEST);
|
|
EXPECT_TRUE_SIMULATED_WAIT(turn_refresh_success_, kSimulatedRtt, fake_clock_);
|
|
// Flush it again, it will receive a bad response.
|
|
turn_port_->request_manager().FlushForTest(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_->CloseForTest();
|
|
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()->NotifyClosedForTest(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_->request_manager().FlushForTest(kAllRequestsForTest);
|
|
EXPECT_TRUE_SIMULATED_WAIT(turn_create_permission_success_, kSimulatedRtt,
|
|
fake_clock_);
|
|
// Flush the requests again; the create-permission-request will fail.
|
|
turn_port_->request_manager().FlushForTest(kAllRequestsForTest);
|
|
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->RegisterReceivedPacketCallback(
|
|
[&](Connection* connection, const rtc::ReceivedPacket& packet) {
|
|
udp_packets_.push_back(
|
|
rtc::Buffer(packet.payload().data(), packet.payload().size()));
|
|
});
|
|
conn1->Send(data.data(), data.length(), options);
|
|
EXPECT_TRUE_SIMULATED_WAIT(!udp_packets_.empty(), kSimulatedRtt, fake_clock_);
|
|
conn2->DeregisterReceivedPacketCallback();
|
|
}
|
|
|
|
// 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);
|
|
TestTurnAllocateSucceeds(kSimulatedRtt * 2);
|
|
}
|
|
|
|
// 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 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(kTurnInvalidAddr, PROTO_UDP));
|
|
turn_port_->PrepareAddress();
|
|
ASSERT_TRUE_WAIT(turn_error_, kResolverTimeout);
|
|
EXPECT_TRUE(turn_port_->Candidates().empty());
|
|
turn_port_.reset();
|
|
rtc::Thread::Current()->PostTask([this] { test_finish_ = true; });
|
|
// 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());
|
|
}
|
|
|
|
class TurnPortWithMockDnsResolverTest : public TurnPortTest {
|
|
public:
|
|
TurnPortWithMockDnsResolverTest()
|
|
: TurnPortTest(), socket_factory_(ss_.get()) {}
|
|
|
|
rtc::PacketSocketFactory* socket_factory() override {
|
|
return &socket_factory_;
|
|
}
|
|
|
|
void SetDnsResolverExpectations(
|
|
rtc::MockDnsResolvingPacketSocketFactory::Expectations expectations) {
|
|
socket_factory_.SetExpectations(expectations);
|
|
}
|
|
|
|
private:
|
|
rtc::MockDnsResolvingPacketSocketFactory socket_factory_;
|
|
};
|
|
|
|
// Test an allocation from a TURN server specified by a hostname.
|
|
TEST_F(TurnPortWithMockDnsResolverTest, TestHostnameResolved) {
|
|
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnPortValidHostnameProtoAddr);
|
|
SetDnsResolverExpectations(
|
|
[](webrtc::MockAsyncDnsResolver* resolver,
|
|
webrtc::MockAsyncDnsResolverResult* resolver_result) {
|
|
EXPECT_CALL(*resolver, Start(kTurnValidAddr, /*family=*/AF_INET, _))
|
|
.WillOnce([](const rtc::SocketAddress& addr, int family,
|
|
absl::AnyInvocable<void()> callback) { callback(); });
|
|
EXPECT_CALL(*resolver, result)
|
|
.WillRepeatedly(ReturnPointee(resolver_result));
|
|
EXPECT_CALL(*resolver_result, GetError).WillRepeatedly(Return(0));
|
|
EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET, _))
|
|
.WillOnce(DoAll(SetArgPointee<1>(kTurnUdpIntAddr), Return(true)));
|
|
});
|
|
TestTurnAllocateSucceeds(kSimulatedRtt * 2);
|
|
}
|
|
|
|
// Test an allocation from a TURN server specified by a hostname on an IPv6
|
|
// network.
|
|
TEST_F(TurnPortWithMockDnsResolverTest, TestHostnameResolvedIPv6Network) {
|
|
turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, PROTO_UDP);
|
|
CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword,
|
|
kTurnPortValidHostnameProtoAddr);
|
|
SetDnsResolverExpectations(
|
|
[](webrtc::MockAsyncDnsResolver* resolver,
|
|
webrtc::MockAsyncDnsResolverResult* resolver_result) {
|
|
EXPECT_CALL(*resolver, Start(kTurnValidAddr, /*family=*/AF_INET6, _))
|
|
.WillOnce([](const rtc::SocketAddress& addr, int family,
|
|
absl::AnyInvocable<void()> callback) { callback(); });
|
|
EXPECT_CALL(*resolver, result)
|
|
.WillRepeatedly(ReturnPointee(resolver_result));
|
|
EXPECT_CALL(*resolver_result, GetError).WillRepeatedly(Return(0));
|
|
EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _))
|
|
.WillOnce(
|
|
DoAll(SetArgPointee<1>(kTurnUdpIPv6IntAddr), Return(true)));
|
|
});
|
|
TestTurnAllocateSucceeds(kSimulatedRtt * 2);
|
|
}
|
|
|
|
} // namespace cricket
|