diff options
9 files changed, 410 insertions, 76 deletions
diff --git a/content/browser/renderer_host/p2p/socket_host.cc b/content/browser/renderer_host/p2p/socket_host.cc index 40d173a..e3203e0 100644 --- a/content/browser/renderer_host/p2p/socket_host.cc +++ b/content/browser/renderer_host/p2p/socket_host.cc @@ -10,7 +10,6 @@ #include "content/browser/renderer_host/p2p/socket_host_udp.h" namespace { -const int kStunHeaderSize = 20; const uint32 kStunMagicCookie = 0x2112A442; } // namespace @@ -79,10 +78,18 @@ P2PSocketHost* P2PSocketHost::Create( return new P2PSocketHostUdp(message_sender, id); case P2P_SOCKET_TCP_SERVER: - return new P2PSocketHostTcpServer(message_sender, id); + return new P2PSocketHostTcpServer( + message_sender, id, P2P_SOCKET_TCP_CLIENT); + + case P2P_SOCKET_STUN_TCP_SERVER: + return new P2PSocketHostTcpServer( + message_sender, id, P2P_SOCKET_STUN_TCP_CLIENT); case P2P_SOCKET_TCP_CLIENT: return new P2PSocketHostTcp(message_sender, id); + + case P2P_SOCKET_STUN_TCP_CLIENT: + return new P2PSocketHostStunTcp(message_sender, id); } NOTREACHED(); diff --git a/content/browser/renderer_host/p2p/socket_host.h b/content/browser/renderer_host/p2p/socket_host.h index 9fe985d..0283700 100644 --- a/content/browser/renderer_host/p2p/socket_host.h +++ b/content/browser/renderer_host/p2p/socket_host.h @@ -19,6 +19,7 @@ namespace content { // Base class for P2P sockets. class CONTENT_EXPORT P2PSocketHost { public: + static const int kStunHeaderSize = 20; // Creates P2PSocketHost of the specific type. static P2PSocketHost* Create(IPC::Sender* message_sender, int id, P2PSocketType type); diff --git a/content/browser/renderer_host/p2p/socket_host_tcp.cc b/content/browser/renderer_host/p2p/socket_host_tcp.cc index de62ffb..65d4c85 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp.cc +++ b/content/browser/renderer_host/p2p/socket_host_tcp.cc @@ -13,27 +13,33 @@ #include "net/socket/tcp_client_socket.h" namespace { + +typedef uint16 PacketLength; +const int kPacketHeaderSize = sizeof(PacketLength); const int kReadBufferSize = 4096; -const int kPacketHeaderSize = sizeof(uint16); +const int kPacketLengthOffset = 2; +const int kTurnChannelDataHeaderSize = 4; + } // namespace namespace content { -P2PSocketHostTcp::P2PSocketHostTcp(IPC::Sender* message_sender, int id) +P2PSocketHostTcpBase::P2PSocketHostTcpBase(IPC::Sender* message_sender, + int id) : P2PSocketHost(message_sender, id), write_pending_(false), connected_(false) { } -P2PSocketHostTcp::~P2PSocketHostTcp() { +P2PSocketHostTcpBase::~P2PSocketHostTcpBase() { if (state_ == STATE_OPEN) { DCHECK(socket_.get()); socket_.reset(); } } -bool P2PSocketHostTcp::InitAccepted(const net::IPEndPoint& remote_address, - net::StreamSocket* socket) { +bool P2PSocketHostTcpBase::InitAccepted(const net::IPEndPoint& remote_address, + net::StreamSocket* socket) { DCHECK(socket); DCHECK_EQ(state_, STATE_UNINITIALIZED); @@ -44,7 +50,7 @@ bool P2PSocketHostTcp::InitAccepted(const net::IPEndPoint& remote_address, return state_ != STATE_ERROR; } -bool P2PSocketHostTcp::Init(const net::IPEndPoint& local_address, +bool P2PSocketHostTcpBase::Init(const net::IPEndPoint& local_address, const net::IPEndPoint& remote_address) { DCHECK_EQ(state_, STATE_UNINITIALIZED); @@ -68,7 +74,7 @@ bool P2PSocketHostTcp::Init(const net::IPEndPoint& local_address, return state_ != STATE_ERROR; } -void P2PSocketHostTcp::OnError() { +void P2PSocketHostTcpBase::OnError() { socket_.reset(); if (state_ == STATE_UNINITIALIZED || state_ == STATE_CONNECTING || @@ -79,7 +85,7 @@ void P2PSocketHostTcp::OnError() { state_ = STATE_ERROR; } -void P2PSocketHostTcp::OnConnected(int result) { +void P2PSocketHostTcpBase::OnConnected(int result) { DCHECK_EQ(state_, STATE_CONNECTING); DCHECK_NE(result, net::ERR_IO_PENDING); @@ -103,7 +109,7 @@ void P2PSocketHostTcp::OnConnected(int result) { DoRead(); } -void P2PSocketHostTcp::DoRead() { +void P2PSocketHostTcpBase::DoRead() { int result; do { if (!read_buffer_) { @@ -124,14 +130,14 @@ void P2PSocketHostTcp::DoRead() { } while (result > 0); } -void P2PSocketHostTcp::OnRead(int result) { +void P2PSocketHostTcpBase::OnRead(int result) { DidCompleteRead(result); if (state_ == STATE_OPEN) { DoRead(); } } -void P2PSocketHostTcp::OnPacket(std::vector<char>& data) { +void P2PSocketHostTcpBase::OnPacket(const std::vector<char>& data) { if (!connected_) { P2PSocketHost::StunMessageType type; bool stun = GetStunPacketType(&*data.begin(), data.size(), &type); @@ -150,43 +156,8 @@ void P2PSocketHostTcp::OnPacket(std::vector<char>& data) { message_sender_->Send(new P2PMsg_OnDataReceived(id_, remote_address_, data)); } -void P2PSocketHostTcp::DidCompleteRead(int result) { - DCHECK_EQ(state_, STATE_OPEN); - - if (result == net::ERR_IO_PENDING) { - return; - } else if (result < 0){ - LOG(ERROR) << "Error when reading from TCP socket: " << result; - OnError(); - return; - } - - read_buffer_->set_offset(read_buffer_->offset() + result); - char* head = read_buffer_->StartOfBuffer(); // Purely a convenience. - int consumed = 0; - while (consumed + kPacketHeaderSize <= read_buffer_->offset() && - state_ == STATE_OPEN) { - int packet_size = base::NetToHost16( - *reinterpret_cast<uint16*>(head + consumed)); - if (consumed + packet_size + kPacketHeaderSize > read_buffer_->offset()) - break; - // We've got a full packet! - consumed += kPacketHeaderSize; - char* cur = head + consumed; - std::vector<char> data(cur, cur + packet_size); - OnPacket(data); - consumed += packet_size; - } - // We've consumed all complete packets from the buffer; now move any remaining - // bytes to the head of the buffer and set offset to reflect this. - if (consumed && consumed <= read_buffer_->offset()) { - memmove(head, head + consumed, read_buffer_->offset() - consumed); - read_buffer_->set_offset(read_buffer_->offset() - consumed); - } -} - -void P2PSocketHostTcp::Send(const net::IPEndPoint& to, - const std::vector<char>& data) { +void P2PSocketHostTcpBase::Send(const net::IPEndPoint& to, + const std::vector<char>& data) { if (!socket_) { // The Send message may be sent after the an OnError message was // sent by hasn't been processed the renderer. @@ -211,12 +182,11 @@ void P2PSocketHostTcp::Send(const net::IPEndPoint& to, } } - int size = kPacketHeaderSize + data.size(); - scoped_refptr<net::DrainableIOBuffer> buffer = - new net::DrainableIOBuffer(new net::IOBuffer(size), size); - *reinterpret_cast<uint16*>(buffer->data()) = base::HostToNet16(data.size()); - memcpy(buffer->data() + kPacketHeaderSize, &data[0], data.size()); + DoSend(to, data); +} +void P2PSocketHostTcpBase::WriteOrQueue( + scoped_refptr<net::DrainableIOBuffer>& buffer) { if (write_buffer_) { write_queue_.push(buffer); return; @@ -226,7 +196,7 @@ void P2PSocketHostTcp::Send(const net::IPEndPoint& to, DoWrite(); } -void P2PSocketHostTcp::DoWrite() { +void P2PSocketHostTcpBase::DoWrite() { while (write_buffer_ && state_ == STATE_OPEN && !write_pending_) { int result = socket_->Write(write_buffer_, write_buffer_->BytesRemaining(), base::Bind(&P2PSocketHostTcp::OnWritten, @@ -235,7 +205,7 @@ void P2PSocketHostTcp::DoWrite() { } } -void P2PSocketHostTcp::OnWritten(int result) { +void P2PSocketHostTcpBase::OnWritten(int result) { DCHECK(write_pending_); DCHECK_NE(result, net::ERR_IO_PENDING); @@ -244,7 +214,7 @@ void P2PSocketHostTcp::OnWritten(int result) { DoWrite(); } -void P2PSocketHostTcp::HandleWriteResult(int result) { +void P2PSocketHostTcpBase::HandleWriteResult(int result) { DCHECK(write_buffer_); if (result >= 0) { write_buffer_->DidConsume(result); @@ -265,11 +235,161 @@ void P2PSocketHostTcp::HandleWriteResult(int result) { } } -P2PSocketHost* P2PSocketHostTcp::AcceptIncomingTcpConnection( +P2PSocketHost* P2PSocketHostTcpBase::AcceptIncomingTcpConnection( const net::IPEndPoint& remote_address, int id) { NOTREACHED(); OnError(); return NULL; } -} // namespace content +void P2PSocketHostTcpBase::DidCompleteRead(int result) { + DCHECK_EQ(state_, STATE_OPEN); + + if (result == net::ERR_IO_PENDING) { + return; + } else if (result < 0) { + LOG(ERROR) << "Error when reading from TCP socket: " << result; + OnError(); + return; + } + + read_buffer_->set_offset(read_buffer_->offset() + result); + char* head = read_buffer_->StartOfBuffer(); // Purely a convenience. + int pos = 0; + while (pos <= read_buffer_->offset() && state_ == STATE_OPEN) { + int consumed = ProcessInput(head + pos, read_buffer_->offset() - pos); + if (!consumed) + break; + pos += consumed; + } + // We've consumed all complete packets from the buffer; now move any remaining + // bytes to the head of the buffer and set offset to reflect this. + if (pos && pos <= read_buffer_->offset()) { + memmove(head, head + pos, read_buffer_->offset() - pos); + read_buffer_->set_offset(read_buffer_->offset() - pos); + } +} + +P2PSocketHostTcp::P2PSocketHostTcp(IPC::Sender* message_sender, int id) + : P2PSocketHostTcpBase(message_sender, id) { +} + +P2PSocketHostTcp::~P2PSocketHostTcp() { +} + +int P2PSocketHostTcp::ProcessInput(char* input, int input_len) { + if (input_len < kPacketHeaderSize) + return 0; + int packet_size = base::NetToHost16(*reinterpret_cast<uint16*>(input)); + if (input_len < packet_size + kPacketHeaderSize) + return 0; + + int consumed = kPacketHeaderSize; + char* cur = input + consumed; + std::vector<char> data(cur, cur + packet_size); + OnPacket(data); + consumed += packet_size; + return consumed; +} + +void P2PSocketHostTcp::DoSend(const net::IPEndPoint& to, + const std::vector<char>& data) { + int size = kPacketHeaderSize + data.size(); + scoped_refptr<net::DrainableIOBuffer> buffer = + new net::DrainableIOBuffer(new net::IOBuffer(size), size); + *reinterpret_cast<uint16*>(buffer->data()) = base::HostToNet16(data.size()); + memcpy(buffer->data() + kPacketHeaderSize, &data[0], data.size()); + + WriteOrQueue(buffer); +} + +// P2PSocketHostStunTcp +P2PSocketHostStunTcp::P2PSocketHostStunTcp(IPC::Sender* message_sender, + int id) + : P2PSocketHostTcpBase(message_sender, id) { +} + +P2PSocketHostStunTcp::~P2PSocketHostStunTcp() { +} + +int P2PSocketHostStunTcp::ProcessInput(char* input, int input_len) { + if (input_len < kPacketHeaderSize + kPacketLengthOffset) + return 0; + + int pad_bytes; + int packet_size = GetExpectedPacketSize( + input, input_len, &pad_bytes); + + if (input_len < packet_size + pad_bytes) + return 0; + + // We have a complete packet. Read through it. + int consumed = 0; + char* cur = input; + std::vector<char> data(cur, cur + packet_size); + OnPacket(data); + consumed += packet_size; + consumed += pad_bytes; + return consumed; +} + +void P2PSocketHostStunTcp::DoSend(const net::IPEndPoint& to, + const std::vector<char>& data) { + // Each packet is expected to have header (STUN/TURN ChannelData), where + // header contains message type and and length of message. + if (data.size() < kPacketHeaderSize + kPacketLengthOffset) { + NOTREACHED(); + OnError(); + return; + } + + int pad_bytes; + size_t expected_len = GetExpectedPacketSize( + &data[0], data.size(), &pad_bytes); + + // Accepts only complete STUN/TURN packets. + if (data.size() != expected_len) { + NOTREACHED(); + OnError(); + return; + } + + // Add any pad bytes to the total size. + int size = data.size() + pad_bytes; + + scoped_refptr<net::DrainableIOBuffer> buffer = + new net::DrainableIOBuffer(new net::IOBuffer(size), size); + memcpy(buffer->data(), &data[0], data.size()); + + if (pad_bytes) { + char padding[4] = {0}; + DCHECK_LE(pad_bytes, 4); + memcpy(buffer->data() + data.size(), padding, pad_bytes); + } + WriteOrQueue(buffer); +} + +int P2PSocketHostStunTcp::GetExpectedPacketSize( + const char* data, int len, int* pad_bytes) { + DCHECK_LE(kTurnChannelDataHeaderSize, len); + // Both stun and turn had length at offset 2. + int packet_size = base::NetToHost16(*reinterpret_cast<const uint16*>( + data + kPacketLengthOffset)); + + // Get packet type (STUN or TURN). + uint16 msg_type = base::NetToHost16(*reinterpret_cast<const uint16*>(data)); + + *pad_bytes = 0; + // Add heder length to packet length. + if ((msg_type & 0xC000) == 0) { + packet_size += kStunHeaderSize; + } else { + packet_size += kTurnChannelDataHeaderSize; + // Calculate any padding if present. + if (packet_size % 4) + *pad_bytes = 4 - packet_size % 4; + } + return packet_size; +} + +} // namespace content diff --git a/content/browser/renderer_host/p2p/socket_host_tcp.h b/content/browser/renderer_host/p2p/socket_host_tcp.h index 4fa3fae..1b6ec9a 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp.h +++ b/content/browser/renderer_host/p2p/socket_host_tcp.h @@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_P2P_SOCKET_HOST_TCP_H_ #define CONTENT_BROWSER_RENDERER_HOST_P2P_SOCKET_HOST_TCP_H_ +#include <queue> #include <vector> #include "base/compiler_specific.h" @@ -24,10 +25,10 @@ class StreamSocket; namespace content { -class CONTENT_EXPORT P2PSocketHostTcp : public P2PSocketHost { +class CONTENT_EXPORT P2PSocketHostTcpBase : public P2PSocketHost { public: - P2PSocketHostTcp(IPC::Sender* message_sender, int id); - virtual ~P2PSocketHostTcp(); + P2PSocketHostTcpBase(IPC::Sender* message_sender, int id); + virtual ~P2PSocketHostTcpBase(); bool InitAccepted(const net::IPEndPoint& remote_address, net::StreamSocket* socket); @@ -40,15 +41,23 @@ class CONTENT_EXPORT P2PSocketHostTcp : public P2PSocketHost { virtual P2PSocketHost* AcceptIncomingTcpConnection( const net::IPEndPoint& remote_address, int id) OVERRIDE; + protected: + // Derived classes will provide the implementation. + virtual int ProcessInput(char* input, int input_len) = 0; + virtual void DoSend(const net::IPEndPoint& to, + const std::vector<char>& data) = 0; + + void WriteOrQueue(scoped_refptr<net::DrainableIOBuffer>& buffer); + void OnPacket(const std::vector<char>& data); + void OnError(); + private: friend class P2PSocketHostTcpTest; friend class P2PSocketHostTcpServerTest; + friend class P2PSocketHostStunTcpTest; - void OnError(); - - void DoRead(); void DidCompleteRead(int result); - void OnPacket(std::vector<char>& data); + void DoRead(); void DoWrite(); void HandleWriteResult(int result); @@ -62,16 +71,49 @@ class CONTENT_EXPORT P2PSocketHostTcp : public P2PSocketHost { scoped_ptr<net::StreamSocket> socket_; scoped_refptr<net::GrowableIOBuffer> read_buffer_; - std::queue<scoped_refptr<net::DrainableIOBuffer> > write_queue_; scoped_refptr<net::DrainableIOBuffer> write_buffer_; + bool write_pending_; bool connected_; + DISALLOW_COPY_AND_ASSIGN(P2PSocketHostTcpBase); +}; + +class CONTENT_EXPORT P2PSocketHostTcp : public P2PSocketHostTcpBase { + public: + P2PSocketHostTcp(IPC::Sender* message_sender, int id); + virtual ~P2PSocketHostTcp(); + + protected: + virtual int ProcessInput(char* input, int input_len) OVERRIDE; + virtual void DoSend(const net::IPEndPoint& to, + const std::vector<char>& data) OVERRIDE; + private: DISALLOW_COPY_AND_ASSIGN(P2PSocketHostTcp); }; +// P2PSocketHostStunTcp class provides the framing of STUN messages when used +// with TURN. These messages will not have length at front of the packet and +// are padded to multiple of 4 bytes. +// Formatting of messages is defined in RFC5766. +class CONTENT_EXPORT P2PSocketHostStunTcp : public P2PSocketHostTcpBase { + public: + P2PSocketHostStunTcp(IPC::Sender* message_sender, int id); + virtual ~P2PSocketHostStunTcp(); + + protected: + virtual int ProcessInput(char* input, int input_len) OVERRIDE; + virtual void DoSend(const net::IPEndPoint& to, + const std::vector<char>& data) OVERRIDE; + private: + int GetExpectedPacketSize(const char* data, int len, int* pad_bytes); + + DISALLOW_COPY_AND_ASSIGN(P2PSocketHostStunTcp); +}; + + } // namespace content #endif // CONTENT_BROWSER_RENDERER_HOST_P2P_SOCKET_HOST_TCP_H_ diff --git a/content/browser/renderer_host/p2p/socket_host_tcp_server.cc b/content/browser/renderer_host/p2p/socket_host_tcp_server.cc index 113eb4d..a55c86b 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp_server.cc +++ b/content/browser/renderer_host/p2p/socket_host_tcp_server.cc @@ -21,8 +21,9 @@ const int kListenBacklog = 5; namespace content { P2PSocketHostTcpServer::P2PSocketHostTcpServer( - IPC::Sender* message_sender, int id) + IPC::Sender* message_sender, int id, P2PSocketType client_type) : P2PSocketHost(message_sender, id), + client_type_(client_type), socket_(new net::TCPServerSocket(NULL, net::NetLog::Source())), accept_callback_( base::Bind(&P2PSocketHostTcpServer::OnAccepted, @@ -127,11 +128,15 @@ P2PSocketHost* P2PSocketHostTcpServer::AcceptIncomingTcpConnection( net::StreamSocket* socket = it->second; accepted_sockets_.erase(it); - scoped_ptr<P2PSocketHostTcp> result( - new P2PSocketHostTcp(message_sender_, id)); + + scoped_ptr<P2PSocketHostTcpBase> result; + if (client_type_ == P2P_SOCKET_TCP_CLIENT) { + result.reset(new P2PSocketHostTcp(message_sender_, id)); + } else { + result.reset(new P2PSocketHostStunTcp(message_sender_, id)); + } if (!result->InitAccepted(remote_address, socket)) return NULL; - return result.release(); } diff --git a/content/browser/renderer_host/p2p/socket_host_tcp_server.h b/content/browser/renderer_host/p2p/socket_host_tcp_server.h index cd1ad17..2c50249 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp_server.h +++ b/content/browser/renderer_host/p2p/socket_host_tcp_server.h @@ -26,7 +26,10 @@ namespace content { class CONTENT_EXPORT P2PSocketHostTcpServer : public P2PSocketHost { public: - P2PSocketHostTcpServer(IPC::Sender* message_sender, int id); + typedef std::map<net::IPEndPoint, net::StreamSocket*> AcceptedSocketsMap; + + P2PSocketHostTcpServer(IPC::Sender* message_sender, int id, + P2PSocketType client_type); virtual ~P2PSocketHostTcpServer(); // P2PSocketHost overrides. @@ -40,8 +43,6 @@ class CONTENT_EXPORT P2PSocketHostTcpServer : public P2PSocketHost { private: friend class P2PSocketHostTcpServerTest; - typedef std::map<net::IPEndPoint, net::StreamSocket*> AcceptedSocketsMap; - void OnError(); void DoAccept(); @@ -50,6 +51,7 @@ class CONTENT_EXPORT P2PSocketHostTcpServer : public P2PSocketHost { // Callback for Accept(). void OnAccepted(int result); + const P2PSocketType client_type_; scoped_ptr<net::ServerSocket> socket_; net::IPEndPoint local_address_; diff --git a/content/browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc b/content/browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc index 71634df..6bca738 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc +++ b/content/browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc @@ -93,7 +93,8 @@ class P2PSocketHostTcpServerTest : public testing::Test { protected: virtual void SetUp() OVERRIDE { socket_ = new FakeServerSocket(); - socket_host_.reset(new P2PSocketHostTcpServer(&sender_, 0)); + socket_host_.reset(new P2PSocketHostTcpServer( + &sender_, 0, P2P_SOCKET_TCP_CLIENT)); socket_host_->socket_.reset(socket_); EXPECT_CALL(sender_, Send( diff --git a/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc b/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc index cc46b2b..b352c9f 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc +++ b/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc @@ -59,6 +59,46 @@ class P2PSocketHostTcpTest : public testing::Test { net::IPEndPoint dest2_; }; +class P2PSocketHostStunTcpTest : public testing::Test { + protected: + virtual void SetUp() OVERRIDE { + EXPECT_CALL(sender_, Send( + MatchMessage(static_cast<uint32>(P2PMsg_OnSocketCreated::ID)))) + .WillOnce(DoAll(DeleteArg<0>(), Return(true))); + + socket_host_.reset(new P2PSocketHostStunTcp(&sender_, 0)); + socket_ = new FakeSocket(&sent_data_); + socket_->SetLocalAddress(ParseAddress(kTestLocalIpAddress, kTestPort1)); + socket_host_->socket_.reset(socket_); + + dest_ = 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 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_|. + scoped_ptr<P2PSocketHostStunTcp> socket_host_; + MockIPCSender sender_; + + net::IPEndPoint local_address_; + + net::IPEndPoint dest_; + net::IPEndPoint dest2_; +}; + // Verify that we can send STUN message and that they are formatted // properly. TEST_F(P2PSocketHostTcpTest, SendStunNoAuth) { @@ -211,4 +251,118 @@ TEST_F(P2PSocketHostTcpTest, AsyncWrites) { 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>(P2PMsg_OnSendComplete::ID)))) + .Times(3) + .WillRepeatedly(DoAll(DeleteArg<0>(), Return(true))); + + std::vector<char> packet1; + CreateStunRequest(&packet1); + socket_host_->Send(dest_, packet1); + + std::vector<char> packet2; + CreateStunResponse(&packet2); + socket_host_->Send(dest_, packet2); + + std::vector<char> packet3; + CreateStunError(&packet3); + socket_host_->Send(dest_, packet3); + + 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>(P2PMsg_OnSendComplete::ID)))) + .Times(3) + .WillRepeatedly(DoAll(DeleteArg<0>(), Return(true))); + + std::vector<char> packet1; + CreateStunRequest(&packet1); + socket_host_->Send(dest_, packet1); + + std::vector<char> packet2; + CreateStunResponse(&packet2); + socket_host_->Send(dest_, packet2); + + std::vector<char> packet3; + CreateStunError(&packet3); + socket_host_->Send(dest_, packet3); + + 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>(P2PMsg_OnError::ID)))) + .WillOnce(DoAll(DeleteArg<0>(), Return(true))); + + std::vector<char> packet; + CreateRandomPacket(&packet); + socket_host_->Send(dest_, packet); + + EXPECT_EQ(0U, sent_data_.size()); +} + +// Verify that asynchronous writes are handled correctly. +TEST_F(P2PSocketHostStunTcpTest, AsyncWrites) { + MessageLoop message_loop; + + socket_->set_async_write(true); + + EXPECT_CALL(sender_, Send( + MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID)))) + .Times(2) + .WillRepeatedly(DoAll(DeleteArg<0>(), Return(true))); + + std::vector<char> packet1; + CreateStunRequest(&packet1); + socket_host_->Send(dest_, packet1); + + std::vector<char> packet2; + CreateStunResponse(&packet2); + socket_host_->Send(dest_, packet2); + + message_loop.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_); +} + } // namespace content diff --git a/content/common/p2p_sockets.h b/content/common/p2p_sockets.h index f24d2ac..b8de0d7 100644 --- a/content/common/p2p_sockets.h +++ b/content/common/p2p_sockets.h @@ -14,7 +14,9 @@ namespace content { enum P2PSocketType { P2P_SOCKET_UDP, P2P_SOCKET_TCP_SERVER, + P2P_SOCKET_STUN_TCP_SERVER, P2P_SOCKET_TCP_CLIENT, + P2P_SOCKET_STUN_TCP_CLIENT, }; } // namespace content |