blob: 40cdbca305cdef3ea470cffca0a14292966dfa44 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/p2p/socket_host_tcp.h"
#include <stddef.h>
#include <stdint.h>
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/sys_byteorder.h"
#include "base/test/scoped_task_environment.h"
#include "content/browser/renderer_host/p2p/socket_host_test_utils.h"
#include "jingle/glue/fake_ssl_client_socket.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/stream_socket.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_test_util.h"
#include "services/network/proxy_resolving_client_socket_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::DeleteArg;
using ::testing::DoAll;
using ::testing::Return;
namespace content {
class P2PSocketHostTcpTestBase : public testing::Test {
protected:
explicit P2PSocketHostTcpTestBase(P2PSocketType type)
: socket_type_(type) {
}
void SetUp() override {
EXPECT_CALL(
sender_,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnSocketCreated::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
if (socket_type_ == P2P_SOCKET_TCP_CLIENT) {
socket_host_.reset(new P2PSocketHostTcp(
&sender_, 0, P2P_SOCKET_TCP_CLIENT, nullptr, nullptr));
} else {
socket_host_.reset(new P2PSocketHostStunTcp(
&sender_, 0, P2P_SOCKET_STUN_TCP_CLIENT, nullptr, nullptr));
}
socket_ = new FakeSocket(&sent_data_);
socket_->SetLocalAddress(ParseAddress(kTestLocalIpAddress, kTestPort1));
socket_host_->socket_.reset(socket_);
dest_.ip_address = ParseAddress(kTestIpAddress1, kTestPort1);
local_address_ = ParseAddress(kTestLocalIpAddress, kTestPort1);
socket_host_->remote_address_ = dest_;
socket_host_->state_ = P2PSocketHost::STATE_CONNECTING;
socket_host_->OnConnected(net::OK);
}
std::string IntToSize(int size) {
std::string result;
uint16_t size16 = base::HostToNet16(size);
result.resize(sizeof(size16));
memcpy(&result[0], &size16, sizeof(size16));
return result;
}
std::string sent_data_;
FakeSocket* socket_; // Owned by |socket_host_|.
std::unique_ptr<P2PSocketHostTcpBase> socket_host_;
MockIPCSender sender_;
net::IPEndPoint local_address_;
P2PHostAndIPEndPoint dest_;
P2PSocketType socket_type_;
};
class P2PSocketHostTcpTest : public P2PSocketHostTcpTestBase {
protected:
P2PSocketHostTcpTest() : P2PSocketHostTcpTestBase(P2P_SOCKET_TCP_CLIENT) { }
};
class P2PSocketHostStunTcpTest : public P2PSocketHostTcpTestBase {
protected:
P2PSocketHostStunTcpTest()
: P2PSocketHostTcpTestBase(P2P_SOCKET_STUN_TCP_CLIENT) {
}
};
// Verify that we can send STUN message and that they are formatted
// properly.
TEST_F(P2PSocketHostTcpTest, SendStunNoAuth) {
EXPECT_CALL(
sender_,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnSendComplete::ID))))
.Times(3)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
rtc::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
socket_host_->Send(dest_.ip_address, packet1, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::vector<char> packet2;
CreateStunResponse(&packet2);
socket_host_->Send(dest_.ip_address, packet2, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::vector<char> packet3;
CreateStunError(&packet3);
socket_host_->Send(dest_.ip_address, packet3, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::string expected_data;
expected_data.append(IntToSize(packet1.size()));
expected_data.append(packet1.begin(), packet1.end());
expected_data.append(IntToSize(packet2.size()));
expected_data.append(packet2.begin(), packet2.end());
expected_data.append(IntToSize(packet3.size()));
expected_data.append(packet3.begin(), packet3.end());
EXPECT_EQ(expected_data, sent_data_);
}
// Verify that we can receive STUN messages from the socket, and that
// the messages are parsed properly.
TEST_F(P2PSocketHostTcpTest, ReceiveStun) {
EXPECT_CALL(
sender_,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnSendComplete::ID))))
.Times(3)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
rtc::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
socket_host_->Send(dest_.ip_address, packet1, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::vector<char> packet2;
CreateStunResponse(&packet2);
socket_host_->Send(dest_.ip_address, packet2, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::vector<char> packet3;
CreateStunError(&packet3);
socket_host_->Send(dest_.ip_address, packet3, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::string received_data;
received_data.append(IntToSize(packet1.size()));
received_data.append(packet1.begin(), packet1.end());
received_data.append(IntToSize(packet2.size()));
received_data.append(packet2.begin(), packet2.end());
received_data.append(IntToSize(packet3.size()));
received_data.append(packet3.begin(), packet3.end());
EXPECT_CALL(sender_, Send(MatchPacketMessage(packet1)))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
EXPECT_CALL(sender_, Send(MatchPacketMessage(packet2)))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
EXPECT_CALL(sender_, Send(MatchPacketMessage(packet3)))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
size_t pos = 0;
size_t step_sizes[] = {3, 2, 1};
size_t step = 0;
while (pos < received_data.size()) {
size_t step_size = std::min(step_sizes[step], received_data.size() - pos);
socket_->AppendInputData(&received_data[pos], step_size);
pos += step_size;
if (++step >= arraysize(step_sizes))
step = 0;
}
}
// Verify that we can't send data before we've received STUN response
// from the other side.
TEST_F(P2PSocketHostTcpTest, SendDataNoAuth) {
EXPECT_CALL(sender_,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnError::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
rtc::PacketOptions options;
std::vector<char> packet;
CreateRandomPacket(&packet);
socket_host_->Send(dest_.ip_address, packet, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_EQ(0U, sent_data_.size());
}
// Verify that SetOption() doesn't crash after an error.
TEST_F(P2PSocketHostTcpTest, SetOptionAfterError) {
// Get the sender into the error state.
EXPECT_CALL(sender_,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnError::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
socket_host_->Send(dest_.ip_address, {1, 2, 3, 4}, rtc::PacketOptions(), 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
testing::Mock::VerifyAndClearExpectations(&sender_);
// Verify that SetOptions() fails, but doesn't crash.
EXPECT_FALSE(socket_host_->SetOption(P2P_SOCKET_OPT_RCVBUF, 2048));
}
// Verify that we can send data after we've received STUN response
// from the other side.
TEST_F(P2PSocketHostTcpTest, SendAfterStunRequest) {
// Receive packet from |dest_|.
std::vector<char> request_packet;
CreateStunRequest(&request_packet);
std::string received_data;
received_data.append(IntToSize(request_packet.size()));
received_data.append(request_packet.begin(), request_packet.end());
EXPECT_CALL(
sender_,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnSendComplete::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
EXPECT_CALL(sender_, Send(MatchPacketMessage(request_packet)))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
socket_->AppendInputData(&received_data[0], received_data.size());
rtc::PacketOptions options;
// Now we should be able to send any data to |dest_|.
std::vector<char> packet;
CreateRandomPacket(&packet);
socket_host_->Send(dest_.ip_address, packet, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::string expected_data;
expected_data.append(IntToSize(packet.size()));
expected_data.append(packet.begin(), packet.end());
EXPECT_EQ(expected_data, sent_data_);
}
// Verify that asynchronous writes are handled correctly.
TEST_F(P2PSocketHostTcpTest, AsyncWrites) {
base::MessageLoop message_loop;
socket_->set_async_write(true);
EXPECT_CALL(
sender_,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnSendComplete::ID))))
.Times(2)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
rtc::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
socket_host_->Send(dest_.ip_address, packet1, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::vector<char> packet2;
CreateStunResponse(&packet2);
socket_host_->Send(dest_.ip_address, packet2, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
base::RunLoop().RunUntilIdle();
std::string expected_data;
expected_data.append(IntToSize(packet1.size()));
expected_data.append(packet1.begin(), packet1.end());
expected_data.append(IntToSize(packet2.size()));
expected_data.append(packet2.begin(), packet2.end());
EXPECT_EQ(expected_data, sent_data_);
}
TEST_F(P2PSocketHostTcpTest, PacketIdIsPropagated) {
base::MessageLoop message_loop;
socket_->set_async_write(true);
const int32_t kRtcPacketId = 1234;
base::TimeTicks now = base::TimeTicks::Now();
EXPECT_CALL(sender_, Send(MatchSendPacketMetrics(kRtcPacketId, now)))
.Times(1)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
rtc::PacketOptions options;
options.packet_id = kRtcPacketId;
std::vector<char> packet1;
CreateStunRequest(&packet1);
socket_host_->Send(dest_.ip_address, packet1, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
base::RunLoop().RunUntilIdle();
std::string expected_data;
expected_data.append(IntToSize(packet1.size()));
expected_data.append(packet1.begin(), packet1.end());
EXPECT_EQ(expected_data, sent_data_);
}
TEST_F(P2PSocketHostTcpTest, SendDataWithPacketOptions) {
std::vector<char> request_packet;
CreateStunRequest(&request_packet);
std::string received_data;
received_data.append(IntToSize(request_packet.size()));
received_data.append(request_packet.begin(), request_packet.end());
EXPECT_CALL(
sender_,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnSendComplete::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
EXPECT_CALL(sender_, Send(MatchPacketMessage(request_packet)))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
socket_->AppendInputData(&received_data[0], received_data.size());
rtc::PacketOptions options;
options.packet_time_params.rtp_sendtime_extension_id = 3;
// Now we should be able to send any data to |dest_|.
std::vector<char> packet;
CreateRandomPacket(&packet);
// Make it a RTP packet.
*reinterpret_cast<uint16_t*>(&*packet.begin()) = base::HostToNet16(0x8000);
socket_host_->Send(dest_.ip_address, packet, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::string expected_data;
expected_data.append(IntToSize(packet.size()));
expected_data.append(packet.begin(), packet.end());
EXPECT_EQ(expected_data, sent_data_);
}
// Verify that we can send STUN message and that they are formatted
// properly.
TEST_F(P2PSocketHostStunTcpTest, SendStunNoAuth) {
EXPECT_CALL(
sender_,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnSendComplete::ID))))
.Times(3)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
rtc::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
socket_host_->Send(dest_.ip_address, packet1, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::vector<char> packet2;
CreateStunResponse(&packet2);
socket_host_->Send(dest_.ip_address, packet2, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::vector<char> packet3;
CreateStunError(&packet3);
socket_host_->Send(dest_.ip_address, packet3, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::string expected_data;
expected_data.append(packet1.begin(), packet1.end());
expected_data.append(packet2.begin(), packet2.end());
expected_data.append(packet3.begin(), packet3.end());
EXPECT_EQ(expected_data, sent_data_);
}
// Verify that we can receive STUN messages from the socket, and that
// the messages are parsed properly.
TEST_F(P2PSocketHostStunTcpTest, ReceiveStun) {
EXPECT_CALL(
sender_,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnSendComplete::ID))))
.Times(3)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
rtc::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
socket_host_->Send(dest_.ip_address, packet1, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::vector<char> packet2;
CreateStunResponse(&packet2);
socket_host_->Send(dest_.ip_address, packet2, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::vector<char> packet3;
CreateStunError(&packet3);
socket_host_->Send(dest_.ip_address, packet3, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::string received_data;
received_data.append(packet1.begin(), packet1.end());
received_data.append(packet2.begin(), packet2.end());
received_data.append(packet3.begin(), packet3.end());
EXPECT_CALL(sender_, Send(MatchPacketMessage(packet1)))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
EXPECT_CALL(sender_, Send(MatchPacketMessage(packet2)))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
EXPECT_CALL(sender_, Send(MatchPacketMessage(packet3)))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
size_t pos = 0;
size_t step_sizes[] = {3, 2, 1};
size_t step = 0;
while (pos < received_data.size()) {
size_t step_size = std::min(step_sizes[step], received_data.size() - pos);
socket_->AppendInputData(&received_data[pos], step_size);
pos += step_size;
if (++step >= arraysize(step_sizes))
step = 0;
}
}
// Verify that we can't send data before we've received STUN response
// from the other side.
TEST_F(P2PSocketHostStunTcpTest, SendDataNoAuth) {
EXPECT_CALL(sender_,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnError::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
rtc::PacketOptions options;
std::vector<char> packet;
CreateRandomPacket(&packet);
socket_host_->Send(dest_.ip_address, packet, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_EQ(0U, sent_data_.size());
}
// Verify that asynchronous writes are handled correctly.
TEST_F(P2PSocketHostStunTcpTest, AsyncWrites) {
base::MessageLoop message_loop;
socket_->set_async_write(true);
EXPECT_CALL(
sender_,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnSendComplete::ID))))
.Times(2)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
rtc::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
socket_host_->Send(dest_.ip_address, packet1, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
std::vector<char> packet2;
CreateStunResponse(&packet2);
socket_host_->Send(dest_.ip_address, packet2, options, 0,
TRAFFIC_ANNOTATION_FOR_TESTS);
base::RunLoop().RunUntilIdle();
std::string expected_data;
expected_data.append(packet1.begin(), packet1.end());
expected_data.append(packet2.begin(), packet2.end());
EXPECT_EQ(expected_data, sent_data_);
}
// When pseudo-tls is used (e.g. for P2P_SOCKET_SSLTCP_CLIENT),
// network::ProxyResolvingClientSocket::Connect() won't be called twice.
// Regression test for crbug.com/840797.
TEST(P2PSocketHostTcpWithPseudoTlsTest, Basic) {
base::test::ScopedTaskEnvironment scoped_task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
MockIPCSender sender;
EXPECT_CALL(
sender,
Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnSocketCreated::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
net::TestURLRequestContext context(true);
net::MockClientSocketFactory mock_socket_factory;
context.set_client_socket_factory(&mock_socket_factory);
context.Init();
network::ProxyResolvingClientSocketFactory factory(&mock_socket_factory,
&context);
base::StringPiece ssl_client_hello =
jingle_glue::FakeSSLClientSocket::GetSslClientHello();
base::StringPiece ssl_server_hello =
jingle_glue::FakeSSLClientSocket::GetSslServerHello();
net::MockRead reads[] = {
net::MockRead(net::ASYNC, ssl_server_hello.data(),
ssl_server_hello.size()),
net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING)};
net::MockWrite writes[] = {net::MockWrite(
net::SYNCHRONOUS, ssl_client_hello.data(), ssl_client_hello.size())};
net::StaticSocketDataProvider data_provider(reads, writes);
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
data_provider.set_connect_data(
net::MockConnect(net::SYNCHRONOUS, net::OK, server_addr));
mock_socket_factory.AddSocketDataProvider(&data_provider);
P2PSocketHostTcp host(&sender, 0 /*socket_id*/, P2P_SOCKET_SSLTCP_CLIENT,
nullptr, &factory);
P2PHostAndIPEndPoint dest;
dest.ip_address = server_addr;
bool success = host.Init(net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0),
0, 0, dest);
EXPECT_TRUE(success);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(data_provider.AllReadDataConsumed());
EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}
} // namespace content