summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-09 16:54:32 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-09 16:54:32 +0000
commit949db516ff98c3177ffe34f18941e1bef0cbfa2b (patch)
tree9ead4822499297e00358261624531c94e1095c32
parent00070c73ac9346bd60307711bb38bbd2270b1a40 (diff)
downloadchromium_src-949db516ff98c3177ffe34f18941e1bef0cbfa2b.zip
chromium_src-949db516ff98c3177ffe34f18941e1bef0cbfa2b.tar.gz
chromium_src-949db516ff98c3177ffe34f18941e1bef0cbfa2b.tar.bz2
Security restrictions for P2P UDP Sockets.
BUG=None TEST=Unittests Review URL: http://codereview.chromium.org/6800023 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@81039 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--content/browser/renderer_host/p2p_socket_host.cc68
-rw-r--r--content/browser/renderer_host/p2p_socket_host.h38
-rw-r--r--content/browser/renderer_host/p2p_socket_host_udp.cc51
-rw-r--r--content/browser/renderer_host/p2p_socket_host_udp.h17
-rw-r--r--content/browser/renderer_host/p2p_socket_host_udp_unittest.cc300
6 files changed, 446 insertions, 29 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 232a384..ecfeb38 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1874,6 +1874,7 @@
'../content/browser/in_process_webkit/webkit_thread_unittest.cc',
'../content/browser/plugin_service_unittest.cc',
'../content/browser/renderer_host/audio_renderer_host_unittest.cc',
+ '../content/browser/renderer_host/p2p_socket_host_udp_unittest.cc',
'../content/browser/renderer_host/render_view_host_unittest.cc',
'../content/browser/renderer_host/render_widget_host_unittest.cc',
'../content/browser/renderer_host/resource_dispatcher_host_unittest.cc',
diff --git a/content/browser/renderer_host/p2p_socket_host.cc b/content/browser/renderer_host/p2p_socket_host.cc
index c1e8849..39e0119 100644
--- a/content/browser/renderer_host/p2p_socket_host.cc
+++ b/content/browser/renderer_host/p2p_socket_host.cc
@@ -4,26 +4,86 @@
#include "content/browser/renderer_host/p2p_socket_host.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <winsock2.h> // for htonl
+#else
+#include <arpa/inet.h>
+#endif
+
#include "content/browser/renderer_host/p2p_socket_host_udp.h"
-P2PSocketHost::P2PSocketHost(P2PSocketsHost* host, int routing_id, int id)
- : host_(host), routing_id_(routing_id), id_(id) {
+namespace {
+const int kStunHeaderSize = 20;
+const uint32 kStunMagicCookie = 0x2112A442;
+} // namespace
+
+P2PSocketHost::P2PSocketHost(IPC::Message::Sender* message_sender,
+ int routing_id, int id)
+ : message_sender_(message_sender), routing_id_(routing_id), id_(id) {
}
P2PSocketHost::~P2PSocketHost() { }
+// Verifies that the packet |data| has a valid STUN header.
+bool P2PSocketHost::GetStunPacketType(
+ const char* data, int data_size, StunMessageType* type) {
+
+ if (data_size < kStunHeaderSize)
+ return false;
+
+ // TODO(sergeyu): Fix libjingle to format STUN message according to
+ // RFC5389 and validate STUN magic cookie here.
+ //
+ // uint32 cookie = ntohl(*reinterpret_cast<const uint32*>(data + 4));
+ // if (cookie != kStunMagicCookie)
+ // return false;
+
+ uint16 length = ntohs(*reinterpret_cast<const uint16*>(data + 2));
+ if (length != data_size - kStunHeaderSize)
+ return false;
+
+ int message_type = ntohs(*reinterpret_cast<const uint16*>(data));
+
+ // Verify that the type is known:
+ switch (message_type) {
+ case STUN_BINDING_REQUEST:
+ case STUN_BINDING_RESPONSE:
+ case STUN_BINDING_ERROR_RESPONSE:
+ case STUN_SHARED_SECRET_REQUEST:
+ case STUN_SHARED_SECRET_RESPONSE:
+ case STUN_SHARED_SECRET_ERROR_RESPONSE:
+ case STUN_ALLOCATE_REQUEST:
+ case STUN_ALLOCATE_RESPONSE:
+ case STUN_ALLOCATE_ERROR_RESPONSE:
+ case STUN_SEND_REQUEST:
+ case STUN_SEND_RESPONSE:
+ case STUN_SEND_ERROR_RESPONSE:
+ case STUN_DATA_INDICATION:
+ *type = static_cast<StunMessageType>(message_type);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
// static
P2PSocketHost* P2PSocketHost::Create(
- P2PSocketsHost* host, int routing_id, int id, P2PSocketType type) {
+ IPC::Message::Sender* message_sender, int routing_id, int id,
+ P2PSocketType type) {
switch (type) {
case P2P_SOCKET_UDP:
- return new P2PSocketHostUdp(host, routing_id, id);
+ return new P2PSocketHostUdp(message_sender, routing_id, id);
case P2P_SOCKET_TCP_SERVER:
// TODO(sergeyu): Implement TCP sockets support.
+ NOTIMPLEMENTED();
return NULL;
case P2P_SOCKET_TCP_CLIENT:
+ NOTIMPLEMENTED();
return NULL;
}
diff --git a/content/browser/renderer_host/p2p_socket_host.h b/content/browser/renderer_host/p2p_socket_host.h
index 55af872e..0248b577 100644
--- a/content/browser/renderer_host/p2p_socket_host.h
+++ b/content/browser/renderer_host/p2p_socket_host.h
@@ -7,30 +7,50 @@
#include "content/common/p2p_sockets.h"
+#include "ipc/ipc_message.h"
#include "net/base/ip_endpoint.h"
-class P2PSocketsHost;
-
// Base class for P2P sockets used by P2PSocketsHost.
class P2PSocketHost {
public:
// Creates P2PSocketHost of the specific type.
- static P2PSocketHost* Create(P2PSocketsHost* host, int routing_id, int id,
- P2PSocketType type);
+ static P2PSocketHost* Create(IPC::Message::Sender* message_sender,
+ int routing_id, int id, P2PSocketType type);
virtual ~P2PSocketHost();
// Initalizes the socket. Returns false when initiazations fails.
virtual bool Init(const net::IPEndPoint& local_address) = 0;
- // Sends |data| on the socket to |socket_address|.
- virtual void Send(const net::IPEndPoint& socket_address,
+ // Sends |data| on the socket to |to|.
+ virtual void Send(const net::IPEndPoint& to,
const std::vector<char>& data) = 0;
protected:
- P2PSocketHost(P2PSocketsHost* host, int routing_id, int id);
-
- P2PSocketsHost* host_;
+ enum StunMessageType {
+ STUN_BINDING_REQUEST = 0x0001,
+ STUN_BINDING_RESPONSE = 0x0101,
+ STUN_BINDING_ERROR_RESPONSE = 0x0111,
+ STUN_SHARED_SECRET_REQUEST = 0x0002,
+ STUN_SHARED_SECRET_RESPONSE = 0x0102,
+ STUN_SHARED_SECRET_ERROR_RESPONSE = 0x0112,
+ STUN_ALLOCATE_REQUEST = 0x0003,
+ STUN_ALLOCATE_RESPONSE = 0x0103,
+ STUN_ALLOCATE_ERROR_RESPONSE = 0x0113,
+ STUN_SEND_REQUEST = 0x0004,
+ STUN_SEND_RESPONSE = 0x0104,
+ STUN_SEND_ERROR_RESPONSE = 0x0114,
+ STUN_DATA_INDICATION = 0x0115
+ };
+
+ P2PSocketHost(IPC::Message::Sender* message_sender, int routing_id, int id);
+
+ // Verifies that the packet |data| has a valid STUN header. In case
+ // of success stores type of the message in |type|.
+ bool GetStunPacketType(const char* data, int data_size,
+ StunMessageType* type);
+
+ IPC::Message::Sender* message_sender_;
int routing_id_;
int id_;
diff --git a/content/browser/renderer_host/p2p_socket_host_udp.cc b/content/browser/renderer_host/p2p_socket_host_udp.cc
index 9d9a063..05ef7ce 100644
--- a/content/browser/renderer_host/p2p_socket_host_udp.cc
+++ b/content/browser/renderer_host/p2p_socket_host_udp.cc
@@ -4,7 +4,6 @@
#include "content/browser/renderer_host/p2p_socket_host_udp.h"
-#include "content/browser/renderer_host/p2p_sockets_host.h"
#include "content/common/p2p_messages.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -12,13 +11,16 @@
namespace {
+// UDP packets cannot be bigger than 64k.
const int kReadBufferSize = 65536;
} // namespace
-P2PSocketHostUdp::P2PSocketHostUdp(P2PSocketsHost* host, int routing_id, int id)
- : P2PSocketHost(host, routing_id, id),
+P2PSocketHostUdp::P2PSocketHostUdp(IPC::Message::Sender* message_sender,
+ int routing_id, int id)
+ : P2PSocketHost(message_sender, routing_id, id),
state_(STATE_UNINITIALIZED),
+ socket_(new net::UDPServerSocket(NULL, net::NetLog::Source())),
send_pending_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(
recv_callback_(this, &P2PSocketHostUdp::OnRecv)),
@@ -34,9 +36,7 @@ P2PSocketHostUdp::~P2PSocketHostUdp() {
}
bool P2PSocketHostUdp::Init(const net::IPEndPoint& local_address) {
- net::UDPServerSocket* socket = new net::UDPServerSocket(
- NULL, net::NetLog::Source());
- socket_.reset(socket);
+ DCHECK_EQ(state_, STATE_UNINITIALIZED);
int result = socket_->Listen(local_address);
if (result < 0) {
@@ -61,7 +61,7 @@ bool P2PSocketHostUdp::Init(const net::IPEndPoint& local_address) {
recv_buffer_ = new net::IOBuffer(kReadBufferSize);
DoRead();
- host_->Send(new P2PMsg_OnSocketCreated(routing_id_, id_, address));
+ message_sender_->Send(new P2PMsg_OnSocketCreated(routing_id_, id_, address));
return true;
}
@@ -70,7 +70,7 @@ void P2PSocketHostUdp::OnError() {
socket_.reset();
if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN)
- host_->Send(new P2PMsg_OnError(routing_id_, id_));
+ message_sender_->Send(new P2PMsg_OnError(routing_id_, id_));
state_ = STATE_ERROR;
}
@@ -96,15 +96,30 @@ void P2PSocketHostUdp::DidCompleteRead(int result) {
if (result > 0) {
std::vector<char> data(recv_buffer_->data(), recv_buffer_->data() + result);
- host_->Send(new P2PMsg_OnDataReceived(routing_id_, id_,
- recv_address_, data));
+
+ if (authorized_peers_.find(recv_address_) == authorized_peers_.end()) {
+ P2PSocketHost::StunMessageType type;
+ bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
+ if (stun && (type == STUN_BINDING_REQUEST ||
+ type == STUN_BINDING_RESPONSE)) {
+ authorized_peers_.insert(recv_address_);
+ } else if (!stun || type == STUN_DATA_INDICATION) {
+ LOG(ERROR) << "Received unexpected data packet from "
+ << recv_address_.ToString()
+ << " before STUN binding is finished.";
+ return;
+ }
+ }
+
+ message_sender_->Send(new P2PMsg_OnDataReceived(routing_id_, id_,
+ recv_address_, data));
} else if (result < 0 && result != net::ERR_IO_PENDING) {
LOG(ERROR) << "Error when reading from UDP socket: " << result;
OnError();
}
}
-void P2PSocketHostUdp::Send(const net::IPEndPoint& socket_address,
+void P2PSocketHostUdp::Send(const net::IPEndPoint& to,
const std::vector<char>& data) {
if (send_pending_) {
// Silently drop packet if previous send hasn't finished.
@@ -112,10 +127,20 @@ void P2PSocketHostUdp::Send(const net::IPEndPoint& socket_address,
return;
}
+ if (authorized_peers_.find(to) == authorized_peers_.end()) {
+ P2PSocketHost::StunMessageType type;
+ bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
+ if (!stun || type == STUN_DATA_INDICATION) {
+ LOG(ERROR) << "Page tried to send a data packet to " << to.ToString()
+ << " before STUN binding is finished.";
+ OnError();
+ return;
+ }
+ }
+
scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data.size());
memcpy(buffer->data(), &data.begin()[0], data.size());
- int result = socket_->SendTo(buffer, data.size(), socket_address,
- &send_callback_);
+ int result = socket_->SendTo(buffer, data.size(), to, &send_callback_);
if (result == net::ERR_IO_PENDING) {
send_pending_ = true;
} else if (result < 0) {
diff --git a/content/browser/renderer_host/p2p_socket_host_udp.h b/content/browser/renderer_host/p2p_socket_host_udp.h
index ba57427..2fd300e 100644
--- a/content/browser/renderer_host/p2p_socket_host_udp.h
+++ b/content/browser/renderer_host/p2p_socket_host_udp.h
@@ -5,29 +5,36 @@
#ifndef CONTENT_BROWSER_RENDERER_HOST_P2P_SOCKET_HOST_UDP_H_
#define CONTENT_BROWSER_RENDERER_HOST_P2P_SOCKET_HOST_UDP_H_
-#include "content/common/p2p_sockets.h"
+#include <set>
#include "base/message_loop.h"
#include "content/browser/renderer_host/p2p_socket_host.h"
+#include "content/common/p2p_sockets.h"
+#include "net/base/ip_endpoint.h"
#include "net/udp/udp_server_socket.h"
class P2PSocketHostUdp : public P2PSocketHost {
public:
- P2PSocketHostUdp(P2PSocketsHost* host, int routing_id, int id);
+ P2PSocketHostUdp(IPC::Message::Sender* message_sender,
+ int routing_id, int id);
virtual ~P2PSocketHostUdp();
// P2PSocketHost overrides.
virtual bool Init(const net::IPEndPoint& local_address) OVERRIDE;
- virtual void Send(const net::IPEndPoint& socket_address,
+ virtual void Send(const net::IPEndPoint& to,
const std::vector<char>& data) OVERRIDE;
private:
+ friend class P2PSocketHostUdpTest;
+
enum State {
STATE_UNINITIALIZED,
STATE_OPEN,
STATE_ERROR,
};
+ typedef std::set<net::IPEndPoint> AuthorizedPeerSet;
+
void OnError();
void DoRead();
void DidCompleteRead(int result);
@@ -42,6 +49,10 @@ class P2PSocketHostUdp : public P2PSocketHost {
net::IPEndPoint recv_address_;
bool send_pending_;
+ // Set of peer for which we have received STUN binding request or
+ // response.
+ AuthorizedPeerSet authorized_peers_;
+
net::CompletionCallbackImpl<P2PSocketHostUdp> recv_callback_;
net::CompletionCallbackImpl<P2PSocketHostUdp> send_callback_;
diff --git a/content/browser/renderer_host/p2p_socket_host_udp_unittest.cc b/content/browser/renderer_host/p2p_socket_host_udp_unittest.cc
new file mode 100644
index 0000000..4fa3fec
--- /dev/null
+++ b/content/browser/renderer_host/p2p_socket_host_udp_unittest.cc
@@ -0,0 +1,300 @@
+// Copyright (c) 2011 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 "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <winsock2.h> // for htonl
+#else
+#include <arpa/inet.h>
+#endif
+
+#include <deque>
+#include <vector>
+
+#include "content/browser/renderer_host/p2p_socket_host_udp.h"
+#include "content/common/p2p_messages.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/udp/datagram_server_socket.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 {
+
+const char kTestLocalIpAddress[] = "123.44.22.4";
+const char kTestIpAddress1[] = "123.44.22.31";
+const int kTestPort1 = 234;
+const char kTestIpAddress2[] = "133.11.22.33";
+const int kTestPort2 = 543;
+
+const int kStunHeaderSize = 20;
+const uint16 kStunBindingRequest = 0x0001;
+const uint16 kStunBindingResponse = 0x0102;
+const uint16 kStunBindingError = 0x0111;
+const uint32 kStunMagicCookie = 0x2112A442;
+
+class FakeDatagramServerSocket : public net::DatagramServerSocket {
+ public:
+ typedef std::pair<net::IPEndPoint, std::vector<char> > UDPPacket;
+
+ // P2PSocketHostUdp destroyes a socket on errors so sent packets
+ // need to be stored outside of this object.
+ explicit FakeDatagramServerSocket(std::deque<UDPPacket>* sent_packets)
+ : sent_packets_(sent_packets), recv_callback_(NULL) {
+ }
+
+ virtual void Close() OVERRIDE {
+ }
+
+ virtual int GetPeerAddress(net::IPEndPoint* address) const OVERRIDE {
+ NOTREACHED();
+ return net::ERR_SOCKET_NOT_CONNECTED;
+ }
+
+ virtual int GetLocalAddress(net::IPEndPoint* address) const OVERRIDE {
+ *address = address_;
+ return 0;
+ }
+
+ virtual int Listen(const net::IPEndPoint& address) OVERRIDE {
+ address_ = address;
+ return 0;
+ }
+
+ virtual int RecvFrom(net::IOBuffer* buf, int buf_len,
+ net::IPEndPoint* address,
+ net::CompletionCallback* callback) OVERRIDE {
+ CHECK(!recv_callback_);
+ if (incoming_packets_.size() > 0) {
+ scoped_refptr<net::IOBuffer> buffer(buf);
+ int size = std::min(
+ static_cast<int>(incoming_packets_.front().second.size()), buf_len);
+ memcpy(buffer->data(), &*incoming_packets_.front().second.begin(), size);
+ *address = incoming_packets_.front().first;
+ incoming_packets_.pop_front();
+ return size;
+ } else {
+ recv_callback_ = callback;
+ recv_buffer_ = buf;
+ recv_size_ = buf_len;
+ recv_address_ = address;
+ return net::ERR_IO_PENDING;
+ }
+ }
+
+ virtual int SendTo(net::IOBuffer* buf, int buf_len,
+ const net::IPEndPoint& address,
+ net::CompletionCallback* callback) OVERRIDE {
+ scoped_refptr<net::IOBuffer> buffer(buf);
+ std::vector<char> data_vector(buffer->data(), buffer->data() + buf_len);
+ sent_packets_->push_back(UDPPacket(address, data_vector));
+ return buf_len;
+ }
+
+ void ReceivePacket(const net::IPEndPoint& address, std::vector<char> data) {
+ if (recv_callback_) {
+ int size = std::min(recv_size_, static_cast<int>(data.size()));
+ memcpy(recv_buffer_->data(), &*data.begin(), size);
+ *recv_address_ = address;
+ net::CompletionCallback* cb = recv_callback_;
+ recv_callback_ = NULL;
+ recv_buffer_ = NULL;
+ cb->Run(size);
+ } else {
+ incoming_packets_.push_back(UDPPacket(address, data));
+ }
+ }
+
+ private:
+ net::IPEndPoint address_;
+ std::deque<UDPPacket>* sent_packets_;
+ std::deque<UDPPacket> incoming_packets_;
+
+ scoped_refptr<net::IOBuffer> recv_buffer_;
+ net::IPEndPoint* recv_address_;
+ int recv_size_;
+ net::CompletionCallback* recv_callback_;
+};
+
+class MockIPCSender : public IPC::Message::Sender {
+ public:
+ MOCK_METHOD1(Send, bool(IPC::Message* msg));
+};
+
+MATCHER_P(MatchMessage, type, "") {
+ return arg->type() == type;
+}
+
+} // namespace
+
+class P2PSocketHostUdpTest : public testing::Test {
+ protected:
+ void SetUp() OVERRIDE {
+ EXPECT_CALL(sender_, Send(
+ MatchMessage(static_cast<uint32>(P2PMsg_OnSocketCreated::ID))))
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+
+ socket_host_.reset(new P2PSocketHostUdp(&sender_, 0, 0));
+ socket_ = new FakeDatagramServerSocket(&sent_packets_);
+ socket_host_->socket_.reset(socket_);
+
+ net::IPAddressNumber local_ip;
+ ASSERT_TRUE(net::ParseIPLiteralToNumber(kTestLocalIpAddress, &local_ip));
+ local_address_ = net::IPEndPoint(local_ip, kTestPort1);
+ socket_host_->Init(local_address_);
+
+ net::IPAddressNumber ip1;
+ ASSERT_TRUE(net::ParseIPLiteralToNumber(kTestIpAddress1, &ip1));
+ dest1_ = net::IPEndPoint(ip1, kTestPort1);
+ net::IPAddressNumber ip2;
+ ASSERT_TRUE(net::ParseIPLiteralToNumber(kTestIpAddress2, &ip2));
+ dest2_ = net::IPEndPoint(ip2, kTestPort2);
+ }
+
+ void CreateRandomPacket(std::vector<char>* packet) {
+ size_t size = kStunHeaderSize + rand() % 1000;
+ packet->resize(size);
+ for (size_t i = 0; i < size; i++) {
+ (*packet)[i] = rand() % 256;
+ }
+ // Always set the first bit to ensure that generated packet is not
+ // valid STUN packet.
+ (*packet)[0] = (*packet)[0] | 0x80;
+ }
+
+ void CreateStunPacket(std::vector<char>* packet, uint16 type) {
+ CreateRandomPacket(packet);
+ *reinterpret_cast<uint16*>(&*packet->begin()) = htons(type);
+ *reinterpret_cast<uint16*>(&*packet->begin() + 2) =
+ htons(packet->size() - kStunHeaderSize);
+ *reinterpret_cast<uint32*>(&*packet->begin() + 4) = htonl(kStunMagicCookie);
+ }
+
+ void CreateStunRequest(std::vector<char>* packet) {
+ CreateStunPacket(packet, kStunBindingRequest);
+ }
+
+ void CreateStunResponse(std::vector<char>* packet) {
+ CreateStunPacket(packet, kStunBindingResponse);
+ }
+
+ void CreateStunError(std::vector<char>* packet) {
+ CreateStunPacket(packet, kStunBindingError);
+ }
+
+ std::deque<FakeDatagramServerSocket::UDPPacket> sent_packets_;
+ FakeDatagramServerSocket* socket_; // Owned by |socket_host_|.
+ scoped_ptr<P2PSocketHostUdp> socket_host_;
+ MockIPCSender sender_;
+
+ net::IPEndPoint local_address_;
+
+ net::IPEndPoint dest1_;
+ net::IPEndPoint dest2_;
+};
+
+// Verify that we can send STUN messages before we receive anything
+// from the other side.
+TEST_F(P2PSocketHostUdpTest, SendStunNoAuth) {
+ std::vector<char> packet1;
+ CreateStunRequest(&packet1);
+ socket_host_->Send(dest1_, packet1);
+
+ std::vector<char> packet2;
+ CreateStunResponse(&packet2);
+ socket_host_->Send(dest1_, packet2);
+
+ std::vector<char> packet3;
+ CreateStunError(&packet3);
+ socket_host_->Send(dest1_, packet3);
+
+ ASSERT_EQ(sent_packets_.size(), 3U);
+ ASSERT_EQ(sent_packets_[0].second, packet1);
+ ASSERT_EQ(sent_packets_[1].second, packet2);
+ ASSERT_EQ(sent_packets_[2].second, packet3);
+}
+
+// Verify that no data packets can be sent before STUN binding has
+// finished.
+TEST_F(P2PSocketHostUdpTest, 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(dest1_, packet);
+
+ ASSERT_EQ(sent_packets_.size(), 0U);
+}
+
+// Verify that we can send data after we've received STUN request
+// from the other side.
+TEST_F(P2PSocketHostUdpTest, SendAfterStunRequest) {
+ EXPECT_CALL(sender_, Send(
+ MatchMessage(static_cast<uint32>(P2PMsg_OnDataReceived::ID))))
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+
+ // Receive packet from |dest1_|.
+ std::vector<char> request_packet;
+ CreateStunRequest(&request_packet);
+ socket_->ReceivePacket(dest1_, request_packet);
+
+ // Now we should be able to send any data to |dest1_|.
+ std::vector<char> packet;
+ CreateRandomPacket(&packet);
+ socket_host_->Send(dest1_, packet);
+
+ ASSERT_EQ(1U, sent_packets_.size());
+ ASSERT_EQ(dest1_, sent_packets_[0].first);
+}
+
+// Verify that we can send data after we've received STUN response
+// from the other side.
+TEST_F(P2PSocketHostUdpTest, SendAfterStunResponse) {
+ EXPECT_CALL(sender_, Send(
+ MatchMessage(static_cast<uint32>(P2PMsg_OnDataReceived::ID))))
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+
+ // Receive packet from |dest1_|.
+ std::vector<char> request_packet;
+ CreateStunRequest(&request_packet);
+ socket_->ReceivePacket(dest1_, request_packet);
+
+ // Now we should be able to send any data to |dest1_|.
+ std::vector<char> packet;
+ CreateRandomPacket(&packet);
+ socket_host_->Send(dest1_, packet);
+
+ ASSERT_EQ(1U, sent_packets_.size());
+ ASSERT_EQ(dest1_, sent_packets_[0].first);
+}
+
+// Verify messages still cannot be sent to an unathorized host after
+// successful binding with different host.
+TEST_F(P2PSocketHostUdpTest, SendAfterStunResponseDifferentHost) {
+ EXPECT_CALL(sender_, Send(
+ MatchMessage(static_cast<uint32>(P2PMsg_OnDataReceived::ID))))
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+
+ // Receive packet from |dest1_|.
+ std::vector<char> request_packet;
+ CreateStunRequest(&request_packet);
+ socket_->ReceivePacket(dest1_, request_packet);
+
+ // Should fail when trying to send the same packet to |dest2_|.
+ std::vector<char> packet;
+ CreateRandomPacket(&packet);
+ EXPECT_CALL(sender_, Send(
+ MatchMessage(static_cast<uint32>(P2PMsg_OnError::ID))))
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+ socket_host_->Send(dest2_, packet);
+}