webrtc_m130/p2p/base/turn_port_unittest.cc

1882 lines
78 KiB
C++
Raw Normal View History

/*
* 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
Make Port (and subclasses) fully "Network"-based, instead of IP-based. For ICE, we want sockets that are bound to specific network interfaces, rather than to specific IP addresses. So, a while ago, we added a "Network" class that gets passed into the Port constructor, in addition to the IP address as before. But we never finished the job of removing the IP address field, such that a Port only guarantees something about the network interface it's associated with, and not the specific IP address it ends up with. This CL does that, and as a consequence, if a port ends up bound to an IP address other than the "best" one (returned by Network::GetBestIP), this *won't* be treated as an error. This is relevant to Android, where even though we pass an IP address into "Bind" as a way of identifying the network, the socket actually gets bound using "android_setsocknetwork", which doesn't provide any guarantees about the IP address. So, if a network interface has multiple IPv6 addresses (for instance), we may not correctly predict the one the OS will choose, and that's ok. This CL also moves "SetAlternateLocalAddress" from VirtualSocket to VirtualSocketServer, which makes for much more readable test code. The next step, if there is one, is to pass along the Network class all the way to SocketServer::Bind. Then the socket server could do smart things with the network information. We could even stick a platform- specific network handle in the Network object, such that the socket server could use it for the binding, or for "sendmsg", for example. See bug 7026 for more context about the sendmsg idea. BUG=webrtc:7715 Review-Url: https://codereview.webrtc.org/2989303002 Cr-Commit-Position: refs/heads/master@{#19251}
2017-08-04 15:01:57 -07:00
#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/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.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"
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";
// 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 cricket::ProtocolAddress kTurnPortHostnameProtoAddr(
kTurnInvalidAddr,
cricket::PROTO_UDP);
static const unsigned int MSG_TESTFINISH = 0;
#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
static int GetFDCount() {
struct dirent* dp;
int fd_count = 0;
DIR* dir = opendir("/proc/self/fd/");
while ((dp = readdir(dir)) != NULL) {
if (dp->d_name[0] == '.')
continue;
++fd_count;
}
closedir(dir);
return fd_count;
}
#endif
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 sigslot::has_slots<>,
public rtc::MessageHandlerAutoCleanup {
public:
TurnPortTest()
: ss_(new TurnPortTestVirtualSocketServer()),
main_(ss_.get()),
socket_factory_(ss_.get()),
turn_server_(&main_, ss_.get(), 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.
Use newer version of TimeDelta and TimeStamp factories in webrtc find . -type f \( -name "*.h" -o -name "*.cc" \) | xargs sed -i -e "s/TimeDelta::Micros<\(.*\)>()/TimeDelta::Micros(\1)/g" find . -type f \( -name "*.h" -o -name "*.cc" \) | xargs sed -i -e "s/TimeDelta::Millis<\(.*\)>()/TimeDelta::Millis(\1)/g" find . -type f \( -name "*.h" -o -name "*.cc" \) | xargs sed -i -e "s/TimeDelta::Seconds<\(.*\)>()/TimeDelta::Seconds(\1)/g" find . -type f \( -name "*.h" -o -name "*.cc" \) | xargs sed -i -e "s/TimeDelta::us/TimeDelta::Micros/g" find . -type f \( -name "*.h" -o -name "*.cc" \) | xargs sed -i -e "s/TimeDelta::ms/TimeDelta::Millis/g" find . -type f \( -name "*.h" -o -name "*.cc" \) | xargs sed -i -e "s/TimeDelta::seconds/TimeDelta::Seconds/g" find . -type f \( -name "*.h" -o -name "*.cc" \) | xargs sed -i -e "s/Timestamp::Micros<\(.*\)>()/Timestamp::Micros(\1)/g" find . -type f \( -name "*.h" -o -name "*.cc" \) | xargs sed -i -e "s/Timestamp::Millis<\(.*\)>()/Timestamp::Millis(\1)/g" find . -type f \( -name "*.h" -o -name "*.cc" \) | xargs sed -i -e "s/Timestamp::Seconds<\(.*\)>()/Timestamp::Seconds(\1)/g" find . -type f \( -name "*.h" -o -name "*.cc" \) | xargs sed -i -e "s/Timestamp::us/Timestamp::Micros/g" find . -type f \( -name "*.h" -o -name "*.cc" \) | xargs sed -i -e "s/Timestamp::ms/Timestamp::Millis/g" find . -type f \( -name "*.h" -o -name "*.cc" \) | xargs sed -i -e "s/Timestamp::seconds/Timestamp::Seconds/g" git cl format Bug: None Change-Id: I87469d2e4a38369654da839ab7c838215a7911e7 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168402 Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Commit-Queue: Danil Chapovalov <danilchap@webrtc.org> Cr-Commit-Position: refs/heads/master@{#30491}
2020-02-10 11:16:00 +01:00
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::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;
}
Make Port (and subclasses) fully "Network"-based, instead of IP-based. For ICE, we want sockets that are bound to specific network interfaces, rather than to specific IP addresses. So, a while ago, we added a "Network" class that gets passed into the Port constructor, in addition to the IP address as before. But we never finished the job of removing the IP address field, such that a Port only guarantees something about the network interface it's associated with, and not the specific IP address it ends up with. This CL does that, and as a consequence, if a port ends up bound to an IP address other than the "best" one (returned by Network::GetBestIP), this *won't* be treated as an error. This is relevant to Android, where even though we pass an IP address into "Bind" as a way of identifying the network, the socket actually gets bound using "android_setsocknetwork", which doesn't provide any guarantees about the IP address. So, if a network interface has multiple IPv6 addresses (for instance), we may not correctly predict the one the OS will choose, and that's ok. This CL also moves "SetAlternateLocalAddress" from VirtualSocket to VirtualSocketServer, which makes for much more readable test code. The next step, if there is one, is to pass along the Network class all the way to SocketServer::Bind. Then the socket server could do smart things with the network information. We could even stick a platform- specific network handle in the Network object, such that the socket server could use it for the binding, or for "sendmsg", for example. See bug 7026 for more context about the sendmsg idea. BUG=webrtc:7715 Review-Url: https://codereview.webrtc.org/2989303002 Cr-Commit-Position: refs/heads/master@{#19251}
2017-08-04 15:01:57 -07:00
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);
}
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);
Make Port (and subclasses) fully "Network"-based, instead of IP-based. For ICE, we want sockets that are bound to specific network interfaces, rather than to specific IP addresses. So, a while ago, we added a "Network" class that gets passed into the Port constructor, in addition to the IP address as before. But we never finished the job of removing the IP address field, such that a Port only guarantees something about the network interface it's associated with, and not the specific IP address it ends up with. This CL does that, and as a consequence, if a port ends up bound to an IP address other than the "best" one (returned by Network::GetBestIP), this *won't* be treated as an error. This is relevant to Android, where even though we pass an IP address into "Bind" as a way of identifying the network, the socket actually gets bound using "android_setsocknetwork", which doesn't provide any guarantees about the IP address. So, if a network interface has multiple IPv6 addresses (for instance), we may not correctly predict the one the OS will choose, and that's ok. This CL also moves "SetAlternateLocalAddress" from VirtualSocket to VirtualSocketServer, which makes for much more readable test code. The next step, if there is one, is to pass along the Network class all the way to SocketServer::Bind. Then the socket server could do smart things with the network information. We could even stick a platform- specific network handle in the Network object, such that the socket server could use it for the binding, or for "sendmsg", for example. See bug 7026 for more context about the sendmsg idea. BUG=webrtc:7715 Review-Url: https://codereview.webrtc.org/2989303002 Cr-Commit-Position: refs/heads/master@{#19251}
2017-08-04 15:01:57 -07:00
}
bool CreateTurnPortWithNetwork(const rtc::Network* network,
Make Port (and subclasses) fully "Network"-based, instead of IP-based. For ICE, we want sockets that are bound to specific network interfaces, rather than to specific IP addresses. So, a while ago, we added a "Network" class that gets passed into the Port constructor, in addition to the IP address as before. But we never finished the job of removing the IP address field, such that a Port only guarantees something about the network interface it's associated with, and not the specific IP address it ends up with. This CL does that, and as a consequence, if a port ends up bound to an IP address other than the "best" one (returned by Network::GetBestIP), this *won't* be treated as an error. This is relevant to Android, where even though we pass an IP address into "Bind" as a way of identifying the network, the socket actually gets bound using "android_setsocknetwork", which doesn't provide any guarantees about the IP address. So, if a network interface has multiple IPv6 addresses (for instance), we may not correctly predict the one the OS will choose, and that's ok. This CL also moves "SetAlternateLocalAddress" from VirtualSocket to VirtualSocketServer, which makes for much more readable test code. The next step, if there is one, is to pass along the Network class all the way to SocketServer::Bind. Then the socket server could do smart things with the network information. We could even stick a platform- specific network handle in the Network object, such that the socket server could use it for the binding, or for "sendmsg", for example. See bug 7026 for more context about the sendmsg idea. BUG=webrtc:7715 Review-Url: https://codereview.webrtc.org/2989303002 Cr-Commit-Position: refs/heads/master@{#19251}
2017-08-04 15:01:57 -07:00
const std::string& username,
const std::string& password,
const ProtocolAddress& server_address) {
return CreateTurnPortWithAllParams(network, username, password,
server_address);
Make Port (and subclasses) fully "Network"-based, instead of IP-based. For ICE, we want sockets that are bound to specific network interfaces, rather than to specific IP addresses. So, a while ago, we added a "Network" class that gets passed into the Port constructor, in addition to the IP address as before. But we never finished the job of removing the IP address field, such that a Port only guarantees something about the network interface it's associated with, and not the specific IP address it ends up with. This CL does that, and as a consequence, if a port ends up bound to an IP address other than the "best" one (returned by Network::GetBestIP), this *won't* be treated as an error. This is relevant to Android, where even though we pass an IP address into "Bind" as a way of identifying the network, the socket actually gets bound using "android_setsocknetwork", which doesn't provide any guarantees about the IP address. So, if a network interface has multiple IPv6 addresses (for instance), we may not correctly predict the one the OS will choose, and that's ok. This CL also moves "SetAlternateLocalAddress" from VirtualSocket to VirtualSocketServer, which makes for much more readable test code. The next step, if there is one, is to pass along the Network class all the way to SocketServer::Bind. Then the socket server could do smart things with the network information. We could even stick a platform- specific network handle in the Network object, such that the socket server could use it for the binding, or for "sendmsg", for example. See bug 7026 for more context about the sendmsg idea. BUG=webrtc:7715 Review-Url: https://codereview.webrtc.org/2989303002 Cr-Commit-Position: refs/heads/master@{#19251}
2017-08-04 15:01:57 -07:00
}
// 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,
Make Port (and subclasses) fully "Network"-based, instead of IP-based. For ICE, we want sockets that are bound to specific network interfaces, rather than to specific IP addresses. So, a while ago, we added a "Network" class that gets passed into the Port constructor, in addition to the IP address as before. But we never finished the job of removing the IP address field, such that a Port only guarantees something about the network interface it's associated with, and not the specific IP address it ends up with. This CL does that, and as a consequence, if a port ends up bound to an IP address other than the "best" one (returned by Network::GetBestIP), this *won't* be treated as an error. This is relevant to Android, where even though we pass an IP address into "Bind" as a way of identifying the network, the socket actually gets bound using "android_setsocknetwork", which doesn't provide any guarantees about the IP address. So, if a network interface has multiple IPv6 addresses (for instance), we may not correctly predict the one the OS will choose, and that's ok. This CL also moves "SetAlternateLocalAddress" from VirtualSocket to VirtualSocketServer, which makes for much more readable test code. The next step, if there is one, is to pass along the Network class all the way to SocketServer::Bind. Then the socket server could do smart things with the network information. We could even stick a platform- specific network handle in the Network object, such that the socket server could use it for the binding, or for "sendmsg", for example. See bug 7026 for more context about the sendmsg idea. BUG=webrtc:7715 Review-Url: https://codereview.webrtc.org/2989303002 Cr-Commit-Position: refs/heads/master@{#19251}
2017-08-04 15:01:57 -07:00
const std::string& username,
const std::string& 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);
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);
}
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);
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, false,
absl::nullopt, &field_trials_);
// 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);
}
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
bool CheckConnectionFailedAndPruned(Connection* conn) {
return conn && !conn->active() &&
conn->state() == IceCandidatePairState::FAILED;
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
}
// Checks that `turn_port_` has a nonempty set of connections and they are all
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
// failed and pruned.
bool CheckAllConnectionsFailedAndPruned() {
auto& connections = turn_port_->connections();
if (connections.empty()) {
return false;
}
for (const auto& kv : connections) {
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
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.
2022-05-02 19:08:43 +00:00
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:
webrtc::test::ScopedKeyValueConfig field_trials_;
rtc::ScopedFakeClock fake_clock_;
Make Port (and subclasses) fully "Network"-based, instead of IP-based. For ICE, we want sockets that are bound to specific network interfaces, rather than to specific IP addresses. So, a while ago, we added a "Network" class that gets passed into the Port constructor, in addition to the IP address as before. But we never finished the job of removing the IP address field, such that a Port only guarantees something about the network interface it's associated with, and not the specific IP address it ends up with. This CL does that, and as a consequence, if a port ends up bound to an IP address other than the "best" one (returned by Network::GetBestIP), this *won't* be treated as an error. This is relevant to Android, where even though we pass an IP address into "Bind" as a way of identifying the network, the socket actually gets bound using "android_setsocknetwork", which doesn't provide any guarantees about the IP address. So, if a network interface has multiple IPv6 addresses (for instance), we may not correctly predict the one the OS will choose, and that's ok. This CL also moves "SetAlternateLocalAddress" from VirtualSocket to VirtualSocketServer, which makes for much more readable test code. The next step, if there is one, is to pass along the Network class all the way to SocketServer::Bind. Then the socket server could do smart things with the network information. We could even stick a platform- specific network handle in the Network object, such that the socket server could use it for the binding, or for "sendmsg", for example. See bug 7026 for more context about the sendmsg idea. BUG=webrtc:7715 Review-Url: https://codereview.webrtc.org/2989303002 Cr-Commit-Position: refs/heads/master@{#19251}
2017-08-04 15:01:57 -07:00
// 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");
}
TEST_F(TurnPortTest, TestReconstructedServerUrlForHostname) {
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnPortHostnameProtoAddr);
// 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));
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->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(
Use std::make_unique instead of absl::make_unique. WebRTC is now using C++14 so there is no need to use the Abseil version of std::make_unique. This CL has been created with the following steps: git grep -l absl::make_unique | sort | uniq > /tmp/make_unique.txt git grep -l absl::WrapUnique | sort | uniq > /tmp/wrap_unique.txt git grep -l "#include <memory>" | sort | uniq > /tmp/memory.txt diff --new-line-format="" --unchanged-line-format="" \ /tmp/make_unique.txt /tmp/wrap_unique.txt | sort | \ uniq > /tmp/only_make_unique.txt diff --new-line-format="" --unchanged-line-format="" \ /tmp/only_make_unique.txt /tmp/memory.txt | \ xargs grep -l "absl/memory" > /tmp/add-memory.txt git grep -l "\babsl::make_unique\b" | \ xargs sed -i "s/\babsl::make_unique\b/std::make_unique/g" git checkout PRESUBMIT.py abseil-in-webrtc.md cat /tmp/add-memory.txt | \ xargs sed -i \ 's/#include "absl\/memory\/memory.h"/#include <memory>/g' git cl format # Manual fix order of the new inserted #include <memory> cat /tmp/only_make_unique | xargs grep -l "#include <memory>" | \ xargs sed -i '/#include "absl\/memory\/memory.h"/d' git ls-files | grep BUILD.gn | \ xargs sed -i '/\/\/third_party\/abseil-cpp\/absl\/memory/d' python tools_webrtc/gn_check_autofix.py \ -m tryserver.webrtc -b linux_rel # Repead the gn_check_autofix step for other platforms git ls-files | grep BUILD.gn | \ xargs sed -i 's/absl\/memory:memory/absl\/memory/g' git cl format Bug: webrtc:10945 Change-Id: I3fe28ea80f4dd3ba3cf28effd151d5e1f19aff89 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/153221 Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Alessio Bazzica <alessiob@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29209}
2019-09-17 17:06:18 +02:00
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(
Use std::make_unique instead of absl::make_unique. WebRTC is now using C++14 so there is no need to use the Abseil version of std::make_unique. This CL has been created with the following steps: git grep -l absl::make_unique | sort | uniq > /tmp/make_unique.txt git grep -l absl::WrapUnique | sort | uniq > /tmp/wrap_unique.txt git grep -l "#include <memory>" | sort | uniq > /tmp/memory.txt diff --new-line-format="" --unchanged-line-format="" \ /tmp/make_unique.txt /tmp/wrap_unique.txt | sort | \ uniq > /tmp/only_make_unique.txt diff --new-line-format="" --unchanged-line-format="" \ /tmp/only_make_unique.txt /tmp/memory.txt | \ xargs grep -l "absl/memory" > /tmp/add-memory.txt git grep -l "\babsl::make_unique\b" | \ xargs sed -i "s/\babsl::make_unique\b/std::make_unique/g" git checkout PRESUBMIT.py abseil-in-webrtc.md cat /tmp/add-memory.txt | \ xargs sed -i \ 's/#include "absl\/memory\/memory.h"/#include <memory>/g' git cl format # Manual fix order of the new inserted #include <memory> cat /tmp/only_make_unique | xargs grep -l "#include <memory>" | \ xargs sed -i '/#include "absl\/memory\/memory.h"/d' git ls-files | grep BUILD.gn | \ xargs sed -i '/\/\/third_party\/abseil-cpp\/absl\/memory/d' python tools_webrtc/gn_check_autofix.py \ -m tryserver.webrtc -b linux_rel # Repead the gn_check_autofix step for other platforms git ls-files | grep BUILD.gn | \ xargs sed -i 's/absl\/memory:memory/absl\/memory/g' git cl format Bug: webrtc:10945 Change-Id: I3fe28ea80f4dd3ba3cf28effd151d5e1f19aff89 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/153221 Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Alessio Bazzica <alessiob@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29209}
2019-09-17 17:06:18 +02:00
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) {
Make Port (and subclasses) fully "Network"-based, instead of IP-based. For ICE, we want sockets that are bound to specific network interfaces, rather than to specific IP addresses. So, a while ago, we added a "Network" class that gets passed into the Port constructor, in addition to the IP address as before. But we never finished the job of removing the IP address field, such that a Port only guarantees something about the network interface it's associated with, and not the specific IP address it ends up with. This CL does that, and as a consequence, if a port ends up bound to an IP address other than the "best" one (returned by Network::GetBestIP), this *won't* be treated as an error. This is relevant to Android, where even though we pass an IP address into "Bind" as a way of identifying the network, the socket actually gets bound using "android_setsocknetwork", which doesn't provide any guarantees about the IP address. So, if a network interface has multiple IPv6 addresses (for instance), we may not correctly predict the one the OS will choose, and that's ok. This CL also moves "SetAlternateLocalAddress" from VirtualSocket to VirtualSocketServer, which makes for much more readable test code. The next step, if there is one, is to pass along the Network class all the way to SocketServer::Bind. Then the socket server could do smart things with the network information. We could even stick a platform- specific network handle in the Network object, such that the socket server could use it for the binding, or for "sendmsg", for example. See bug 7026 for more context about the sendmsg idea. BUG=webrtc:7715 Review-Url: https://codereview.webrtc.org/2989303002 Cr-Commit-Position: refs/heads/master@{#19251}
2017-08-04 15:01:57 -07:00
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);
Make Port (and subclasses) fully "Network"-based, instead of IP-based. For ICE, we want sockets that are bound to specific network interfaces, rather than to specific IP addresses. So, a while ago, we added a "Network" class that gets passed into the Port constructor, in addition to the IP address as before. But we never finished the job of removing the IP address field, such that a Port only guarantees something about the network interface it's associated with, and not the specific IP address it ends up with. This CL does that, and as a consequence, if a port ends up bound to an IP address other than the "best" one (returned by Network::GetBestIP), this *won't* be treated as an error. This is relevant to Android, where even though we pass an IP address into "Bind" as a way of identifying the network, the socket actually gets bound using "android_setsocknetwork", which doesn't provide any guarantees about the IP address. So, if a network interface has multiple IPv6 addresses (for instance), we may not correctly predict the one the OS will choose, and that's ok. This CL also moves "SetAlternateLocalAddress" from VirtualSocket to VirtualSocketServer, which makes for much more readable test code. The next step, if there is one, is to pass along the Network class all the way to SocketServer::Bind. Then the socket server could do smart things with the network information. We could even stick a platform- specific network handle in the Network object, such that the socket server could use it for the binding, or for "sendmsg", for example. See bug 7026 for more context about the sendmsg idea. BUG=webrtc:7715 Review-Url: https://codereview.webrtc.org/2989303002 Cr-Commit-Position: refs/heads/master@{#19251}
2017-08-04 15:01:57 -07:00
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());
Make Port (and subclasses) fully "Network"-based, instead of IP-based. For ICE, we want sockets that are bound to specific network interfaces, rather than to specific IP addresses. So, a while ago, we added a "Network" class that gets passed into the Port constructor, in addition to the IP address as before. But we never finished the job of removing the IP address field, such that a Port only guarantees something about the network interface it's associated with, and not the specific IP address it ends up with. This CL does that, and as a consequence, if a port ends up bound to an IP address other than the "best" one (returned by Network::GetBestIP), this *won't* be treated as an error. This is relevant to Android, where even though we pass an IP address into "Bind" as a way of identifying the network, the socket actually gets bound using "android_setsocknetwork", which doesn't provide any guarantees about the IP address. So, if a network interface has multiple IPv6 addresses (for instance), we may not correctly predict the one the OS will choose, and that's ok. This CL also moves "SetAlternateLocalAddress" from VirtualSocket to VirtualSocketServer, which makes for much more readable test code. The next step, if there is one, is to pass along the Network class all the way to SocketServer::Bind. Then the socket server could do smart things with the network information. We could even stick a platform- specific network handle in the Network object, such that the socket server could use it for the binding, or for "sendmsg", for example. See bug 7026 for more context about the sendmsg idea. BUG=webrtc:7715 Review-Url: https://codereview.webrtc.org/2989303002 Cr-Commit-Position: refs/heads/master@{#19251}
2017-08-04 15:01:57 -07:00
// 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);
Make Port (and subclasses) fully "Network"-based, instead of IP-based. For ICE, we want sockets that are bound to specific network interfaces, rather than to specific IP addresses. So, a while ago, we added a "Network" class that gets passed into the Port constructor, in addition to the IP address as before. But we never finished the job of removing the IP address field, such that a Port only guarantees something about the network interface it's associated with, and not the specific IP address it ends up with. This CL does that, and as a consequence, if a port ends up bound to an IP address other than the "best" one (returned by Network::GetBestIP), this *won't* be treated as an error. This is relevant to Android, where even though we pass an IP address into "Bind" as a way of identifying the network, the socket actually gets bound using "android_setsocknetwork", which doesn't provide any guarantees about the IP address. So, if a network interface has multiple IPv6 addresses (for instance), we may not correctly predict the one the OS will choose, and that's ok. This CL also moves "SetAlternateLocalAddress" from VirtualSocket to VirtualSocketServer, which makes for much more readable test code. The next step, if there is one, is to pass along the Network class all the way to SocketServer::Bind. Then the socket server could do smart things with the network information. We could even stick a platform- specific network handle in the Network object, such that the socket server could use it for the binding, or for "sendmsg", for example. See bug 7026 for more context about the sendmsg idea. BUG=webrtc:7715 Review-Url: https://codereview.webrtc.org/2989303002 Cr-Commit-Position: refs/heads/master@{#19251}
2017-08-04 15:01:57 -07:00
}
// 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_->FlushRequestsForTest(TURN_REFRESH_REQUEST);
EXPECT_TRUE_SIMULATED_WAIT(turn_refresh_success_, kSimulatedRtt, fake_clock_);
// Flush it again, it will receive a bad response.
turn_port_->FlushRequestsForTest(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_);
2022-05-02 19:08:43 +00:00
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);
}
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
// 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);
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
PrepareTurnAndUdpPorts(PROTO_TCP);
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
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_);
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
}
// 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_->FlushRequestsForTest(kAllRequests);
EXPECT_TRUE_SIMULATED_WAIT(turn_create_permission_success_, kSimulatedRtt,
fake_clock_);
// Flush the requests again; the create-permission-request will fail.
turn_port_->FlushRequestsForTest(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);
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
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));
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
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.
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
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 that a CreatePermission failure will result in the connection being
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
// 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));
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
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_);
Reland of Do not delete a connection in the turn port with permission error or refresh error. (patchset #1 id:1 of https://codereview.webrtc.org/2090833002/ ) Reason for revert: The Webrtc waterfall indicates that this revert is not necessary. Original issue's description: > Revert of Do not delete a connection in the turn port with permission error or refresh error. (patchset #6 id:260001 of https://codereview.webrtc.org/2068263003/ ) > > Reason for revert: > It broke webrtc builds. > > Original issue's description: > > Do not delete a connection in the turn port with permission error, refresh error, or binding error. > > > > Even if those error happened, the connection may still be able to receive packets for a while. > > If we delete the connections, all packets arriving will be dropped. > > > > BUG=webrtc:6007 > > R=deadbeef@webrtc.org, pthatcher@webrtc.org > > > > Committed: https://crrev.com/3d77deb29c15bfb8f794ef3413837a0ec0f0c131 > > Cr-Commit-Position: refs/heads/master@{#13262} > > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > BUG=webrtc:6007 > > Committed: https://crrev.com/3159ffae6b1d5cba2ad972bd3d8074ac85f2c7f9 > Cr-Commit-Position: refs/heads/master@{#13265} TBR=pthatcher@webrtc.org,deadbeef@webrtc.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=webrtc:6007 Review-Url: https://codereview.webrtc.org/2090073003 Cr-Commit-Position: refs/heads/master@{#13266}
2016-06-22 16:26:29 -07:00
// 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()->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::ScopedKeyValueConfig override_field_trials(
field_trials_, "WebRTC-Turn-AllowSystemPorts/Enabled/");
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnDangerousProtoAddr);
ASSERT_TRUE(turn_port_);
}
} // namespace cricket