summaryrefslogtreecommitdiff
path: root/chromium/net/udp/udp_socket_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/udp/udp_socket_unittest.cc')
-rw-r--r--chromium/net/udp/udp_socket_unittest.cc585
1 files changed, 585 insertions, 0 deletions
diff --git a/chromium/net/udp/udp_socket_unittest.cc b/chromium/net/udp/udp_socket_unittest.cc
new file mode 100644
index 00000000000..ffb56d98141
--- /dev/null
+++ b/chromium/net/udp/udp_socket_unittest.cc
@@ -0,0 +1,585 @@
+// 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 "net/udp/udp_client_socket.h"
+#include "net/udp/udp_server_socket.h"
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/metrics/histogram.h"
+#include "base/stl_util.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log_unittest.h"
+#include "net/base/net_util.h"
+#include "net/base/test_completion_callback.h"
+#include "net/test/net_test_suite.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace net {
+
+namespace {
+
+class UDPSocketTest : public PlatformTest {
+ public:
+ UDPSocketTest()
+ : buffer_(new IOBufferWithSize(kMaxRead)) {
+ }
+
+ // Blocks until data is read from the socket.
+ std::string RecvFromSocket(UDPServerSocket* socket) {
+ TestCompletionCallback callback;
+
+ int rv = socket->RecvFrom(
+ buffer_.get(), kMaxRead, &recv_from_address_, callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ if (rv < 0)
+ return std::string(); // error!
+ return std::string(buffer_->data(), rv);
+ }
+
+ // Loop until |msg| has been written to the socket or until an
+ // error occurs.
+ // If |address| is specified, then it is used for the destination
+ // to send to. Otherwise, will send to the last socket this server
+ // received from.
+ int SendToSocket(UDPServerSocket* socket, std::string msg) {
+ return SendToSocket(socket, msg, recv_from_address_);
+ }
+
+ int SendToSocket(UDPServerSocket* socket,
+ std::string msg,
+ const IPEndPoint& address) {
+ TestCompletionCallback callback;
+
+ int length = msg.length();
+ scoped_refptr<StringIOBuffer> io_buffer(new StringIOBuffer(msg));
+ scoped_refptr<DrainableIOBuffer> buffer(
+ new DrainableIOBuffer(io_buffer.get(), length));
+
+ int bytes_sent = 0;
+ while (buffer->BytesRemaining()) {
+ int rv = socket->SendTo(
+ buffer.get(), buffer->BytesRemaining(), address, callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ if (rv <= 0)
+ return bytes_sent > 0 ? bytes_sent : rv;
+ bytes_sent += rv;
+ buffer->DidConsume(rv);
+ }
+ return bytes_sent;
+ }
+
+ std::string ReadSocket(UDPClientSocket* socket) {
+ TestCompletionCallback callback;
+
+ int rv = socket->Read(buffer_.get(), kMaxRead, callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ if (rv < 0)
+ return std::string(); // error!
+ return std::string(buffer_->data(), rv);
+ }
+
+ // Loop until |msg| has been written to the socket or until an
+ // error occurs.
+ int WriteSocket(UDPClientSocket* socket, std::string msg) {
+ TestCompletionCallback callback;
+
+ int length = msg.length();
+ scoped_refptr<StringIOBuffer> io_buffer(new StringIOBuffer(msg));
+ scoped_refptr<DrainableIOBuffer> buffer(
+ new DrainableIOBuffer(io_buffer.get(), length));
+
+ int bytes_sent = 0;
+ while (buffer->BytesRemaining()) {
+ int rv = socket->Write(
+ buffer.get(), buffer->BytesRemaining(), callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ if (rv <= 0)
+ return bytes_sent > 0 ? bytes_sent : rv;
+ bytes_sent += rv;
+ buffer->DidConsume(rv);
+ }
+ return bytes_sent;
+ }
+
+ protected:
+ static const int kMaxRead = 1024;
+ scoped_refptr<IOBufferWithSize> buffer_;
+ IPEndPoint recv_from_address_;
+};
+
+// Creates and address from an ip/port and returns it in |address|.
+void CreateUDPAddress(std::string ip_str, int port, IPEndPoint* address) {
+ IPAddressNumber ip_number;
+ bool rv = ParseIPLiteralToNumber(ip_str, &ip_number);
+ if (!rv)
+ return;
+ *address = IPEndPoint(ip_number, port);
+}
+
+TEST_F(UDPSocketTest, Connect) {
+ const int kPort = 9999;
+ std::string simple_message("hello world!");
+
+ // Setup the server to listen.
+ IPEndPoint bind_address;
+ CreateUDPAddress("127.0.0.1", kPort, &bind_address);
+ CapturingNetLog server_log;
+ scoped_ptr<UDPServerSocket> server(
+ new UDPServerSocket(&server_log, NetLog::Source()));
+ server->AllowAddressReuse();
+ int rv = server->Listen(bind_address);
+ ASSERT_EQ(OK, rv);
+
+ // Setup the client.
+ IPEndPoint server_address;
+ CreateUDPAddress("127.0.0.1", kPort, &server_address);
+ CapturingNetLog client_log;
+ scoped_ptr<UDPClientSocket> client(
+ new UDPClientSocket(DatagramSocket::DEFAULT_BIND,
+ RandIntCallback(),
+ &client_log,
+ NetLog::Source()));
+ rv = client->Connect(server_address);
+ EXPECT_EQ(OK, rv);
+
+ // Client sends to the server.
+ rv = WriteSocket(client.get(), simple_message);
+ EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
+
+ // Server waits for message.
+ std::string str = RecvFromSocket(server.get());
+ DCHECK(simple_message == str);
+
+ // Server echoes reply.
+ rv = SendToSocket(server.get(), simple_message);
+ EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
+
+ // Client waits for response.
+ str = ReadSocket(client.get());
+ DCHECK(simple_message == str);
+
+ // Delete sockets so they log their final events.
+ server.reset();
+ client.reset();
+
+ // Check the server's log.
+ CapturingNetLog::CapturedEntryList server_entries;
+ server_log.GetEntries(&server_entries);
+ EXPECT_EQ(4u, server_entries.size());
+ EXPECT_TRUE(LogContainsBeginEvent(
+ server_entries, 0, NetLog::TYPE_SOCKET_ALIVE));
+ EXPECT_TRUE(LogContainsEvent(
+ server_entries, 1, NetLog::TYPE_UDP_BYTES_RECEIVED, NetLog::PHASE_NONE));
+ EXPECT_TRUE(LogContainsEvent(
+ server_entries, 2, NetLog::TYPE_UDP_BYTES_SENT, NetLog::PHASE_NONE));
+ EXPECT_TRUE(LogContainsEndEvent(
+ server_entries, 3, NetLog::TYPE_SOCKET_ALIVE));
+
+ // Check the client's log.
+ CapturingNetLog::CapturedEntryList client_entries;
+ client_log.GetEntries(&client_entries);
+ EXPECT_EQ(6u, client_entries.size());
+ EXPECT_TRUE(LogContainsBeginEvent(
+ client_entries, 0, NetLog::TYPE_SOCKET_ALIVE));
+ EXPECT_TRUE(LogContainsBeginEvent(
+ client_entries, 1, NetLog::TYPE_UDP_CONNECT));
+ EXPECT_TRUE(LogContainsEndEvent(
+ client_entries, 2, NetLog::TYPE_UDP_CONNECT));
+ EXPECT_TRUE(LogContainsEvent(
+ client_entries, 3, NetLog::TYPE_UDP_BYTES_SENT, NetLog::PHASE_NONE));
+ EXPECT_TRUE(LogContainsEvent(
+ client_entries, 4, NetLog::TYPE_UDP_BYTES_RECEIVED, NetLog::PHASE_NONE));
+ EXPECT_TRUE(LogContainsEndEvent(
+ client_entries, 5, NetLog::TYPE_SOCKET_ALIVE));
+}
+
+#if defined(OS_MACOSX)
+// UDPSocketPrivate_Broadcast is disabled for OSX because it requires
+// root permissions on OSX 10.7+.
+TEST_F(UDPSocketTest, DISABLED_Broadcast) {
+#elif defined(OS_ANDROID)
+// It is also disabled for Android because it is extremely flaky.
+// The first call to SendToSocket returns -109 (Address not reachable)
+// in some unpredictable cases. crbug.com/139144.
+TEST_F(UDPSocketTest, DISABLED_Broadcast) {
+#else
+TEST_F(UDPSocketTest, Broadcast) {
+#endif
+ const int kPort = 9999;
+ std::string first_message("first message"), second_message("second message");
+
+ IPEndPoint broadcast_address;
+ CreateUDPAddress("255.255.255.255", kPort, &broadcast_address);
+ IPEndPoint listen_address;
+ CreateUDPAddress("0.0.0.0", kPort, &listen_address);
+
+ CapturingNetLog server1_log, server2_log;
+ scoped_ptr<UDPServerSocket> server1(
+ new UDPServerSocket(&server1_log, NetLog::Source()));
+ scoped_ptr<UDPServerSocket> server2(
+ new UDPServerSocket(&server2_log, NetLog::Source()));
+ server1->AllowAddressReuse();
+ server1->AllowBroadcast();
+ server2->AllowAddressReuse();
+ server2->AllowBroadcast();
+
+ int rv = server1->Listen(listen_address);
+ EXPECT_EQ(OK, rv);
+ rv = server2->Listen(listen_address);
+ EXPECT_EQ(OK, rv);
+
+ rv = SendToSocket(server1.get(), first_message, broadcast_address);
+ ASSERT_EQ(static_cast<int>(first_message.size()), rv);
+ std::string str = RecvFromSocket(server1.get());
+ ASSERT_EQ(first_message, str);
+ str = RecvFromSocket(server2.get());
+ ASSERT_EQ(first_message, str);
+
+ rv = SendToSocket(server2.get(), second_message, broadcast_address);
+ ASSERT_EQ(static_cast<int>(second_message.size()), rv);
+ str = RecvFromSocket(server1.get());
+ ASSERT_EQ(second_message, str);
+ str = RecvFromSocket(server2.get());
+ ASSERT_EQ(second_message, str);
+}
+
+// In this test, we verify that random binding logic works, which attempts
+// to bind to a random port and returns if succeeds, otherwise retries for
+// |kBindRetries| number of times.
+
+// To generate the scenario, we first create |kBindRetries| number of
+// UDPClientSockets with default binding policy and connect to the same
+// peer and save the used port numbers. Then we get rid of the last
+// socket, making sure that the local port it was bound to is available.
+// Finally, we create a socket with random binding policy, passing it a
+// test PRNG that would serve used port numbers in the array, one after
+// another. At the end, we make sure that the test socket was bound to the
+// port that became available after deleting the last socket with default
+// binding policy.
+
+// We do not test the randomness of bound ports, but that we are using
+// passed in PRNG correctly, thus, it's the duty of PRNG to produce strong
+// random numbers.
+static const int kBindRetries = 10;
+
+class TestPrng {
+ public:
+ explicit TestPrng(const std::deque<int>& numbers) : numbers_(numbers) {}
+ int GetNext(int /* min */, int /* max */) {
+ DCHECK(!numbers_.empty());
+ int rv = numbers_.front();
+ numbers_.pop_front();
+ return rv;
+ }
+ private:
+ std::deque<int> numbers_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestPrng);
+};
+
+#if defined(OS_ANDROID)
+// Disabled on Android for lack of 192.168.1.13. crbug.com/161245
+TEST_F(UDPSocketTest, DISABLED_ConnectRandomBind) {
+#else
+TEST_F(UDPSocketTest, ConnectRandomBind) {
+#endif
+ std::vector<UDPClientSocket*> sockets;
+ IPEndPoint peer_address;
+ CreateUDPAddress("192.168.1.13", 53, &peer_address);
+
+ // Create and connect sockets and save port numbers.
+ std::deque<int> used_ports;
+ for (int i = 0; i < kBindRetries; ++i) {
+ UDPClientSocket* socket =
+ new UDPClientSocket(DatagramSocket::DEFAULT_BIND,
+ RandIntCallback(),
+ NULL,
+ NetLog::Source());
+ sockets.push_back(socket);
+ EXPECT_EQ(OK, socket->Connect(peer_address));
+
+ IPEndPoint client_address;
+ EXPECT_EQ(OK, socket->GetLocalAddress(&client_address));
+ used_ports.push_back(client_address.port());
+ }
+
+ // Free the last socket, its local port is still in |used_ports|.
+ delete sockets.back();
+ sockets.pop_back();
+
+ TestPrng test_prng(used_ports);
+ RandIntCallback rand_int_cb =
+ base::Bind(&TestPrng::GetNext, base::Unretained(&test_prng));
+
+ // Create a socket with random binding policy and connect.
+ scoped_ptr<UDPClientSocket> test_socket(
+ new UDPClientSocket(DatagramSocket::RANDOM_BIND,
+ rand_int_cb,
+ NULL,
+ NetLog::Source()));
+ EXPECT_EQ(OK, test_socket->Connect(peer_address));
+
+ // Make sure that the last port number in the |used_ports| was used.
+ IPEndPoint client_address;
+ EXPECT_EQ(OK, test_socket->GetLocalAddress(&client_address));
+ EXPECT_EQ(used_ports.back(), client_address.port());
+
+ STLDeleteElements(&sockets);
+}
+
+// Return a privileged port (under 1024) so binding will fail.
+int PrivilegedRand(int min, int max) {
+ // Chosen by fair dice roll. Guaranteed to be random.
+ return 4;
+}
+
+TEST_F(UDPSocketTest, ConnectFail) {
+ IPEndPoint peer_address;
+ CreateUDPAddress("0.0.0.0", 53, &peer_address);
+
+ scoped_ptr<UDPSocket> socket(
+ new UDPSocket(DatagramSocket::RANDOM_BIND,
+ base::Bind(&PrivilegedRand),
+ NULL,
+ NetLog::Source()));
+ int rv = socket->Connect(peer_address);
+ // Connect should have failed since we couldn't bind to that port,
+ EXPECT_NE(OK, rv);
+ // Make sure that UDPSocket actually closed the socket.
+ EXPECT_FALSE(socket->is_connected());
+}
+
+// In this test, we verify that connect() on a socket will have the effect
+// of filtering reads on this socket only to data read from the destination
+// we connected to.
+//
+// The purpose of this test is that some documentation indicates that connect
+// binds the client's sends to send to a particular server endpoint, but does
+// not bind the client's reads to only be from that endpoint, and that we need
+// to always use recvfrom() to disambiguate.
+TEST_F(UDPSocketTest, VerifyConnectBindsAddr) {
+ const int kPort1 = 9999;
+ const int kPort2 = 10000;
+ std::string simple_message("hello world!");
+ std::string foreign_message("BAD MESSAGE TO GET!!");
+
+ // Setup the first server to listen.
+ IPEndPoint bind_address;
+ CreateUDPAddress("127.0.0.1", kPort1, &bind_address);
+ UDPServerSocket server1(NULL, NetLog::Source());
+ server1.AllowAddressReuse();
+ int rv = server1.Listen(bind_address);
+ ASSERT_EQ(OK, rv);
+
+ // Setup the second server to listen.
+ CreateUDPAddress("127.0.0.1", kPort2, &bind_address);
+ UDPServerSocket server2(NULL, NetLog::Source());
+ server2.AllowAddressReuse();
+ rv = server2.Listen(bind_address);
+ ASSERT_EQ(OK, rv);
+
+ // Setup the client, connected to server 1.
+ IPEndPoint server_address;
+ CreateUDPAddress("127.0.0.1", kPort1, &server_address);
+ UDPClientSocket client(DatagramSocket::DEFAULT_BIND,
+ RandIntCallback(),
+ NULL,
+ NetLog::Source());
+ rv = client.Connect(server_address);
+ EXPECT_EQ(OK, rv);
+
+ // Client sends to server1.
+ rv = WriteSocket(&client, simple_message);
+ EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
+
+ // Server1 waits for message.
+ std::string str = RecvFromSocket(&server1);
+ DCHECK(simple_message == str);
+
+ // Get the client's address.
+ IPEndPoint client_address;
+ rv = client.GetLocalAddress(&client_address);
+ EXPECT_EQ(OK, rv);
+
+ // Server2 sends reply.
+ rv = SendToSocket(&server2, foreign_message,
+ client_address);
+ EXPECT_EQ(foreign_message.length(), static_cast<size_t>(rv));
+
+ // Server1 sends reply.
+ rv = SendToSocket(&server1, simple_message,
+ client_address);
+ EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
+
+ // Client waits for response.
+ str = ReadSocket(&client);
+ DCHECK(simple_message == str);
+}
+
+TEST_F(UDPSocketTest, ClientGetLocalPeerAddresses) {
+ struct TestData {
+ std::string remote_address;
+ std::string local_address;
+ bool may_fail;
+ } tests[] = {
+ { "127.0.00.1", "127.0.0.1", false },
+ { "::1", "::1", true },
+#if !defined(OS_ANDROID)
+ // Addresses below are disabled on Android. See crbug.com/161248
+ { "192.168.1.1", "127.0.0.1", false },
+ { "2001:db8:0::42", "::1", true },
+#endif
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); i++) {
+ SCOPED_TRACE(std::string("Connecting from ") + tests[i].local_address +
+ std::string(" to ") + tests[i].remote_address);
+
+ IPAddressNumber ip_number;
+ ParseIPLiteralToNumber(tests[i].remote_address, &ip_number);
+ IPEndPoint remote_address(ip_number, 80);
+ ParseIPLiteralToNumber(tests[i].local_address, &ip_number);
+ IPEndPoint local_address(ip_number, 80);
+
+ UDPClientSocket client(DatagramSocket::DEFAULT_BIND,
+ RandIntCallback(),
+ NULL,
+ NetLog::Source());
+ int rv = client.Connect(remote_address);
+ if (tests[i].may_fail && rv == ERR_ADDRESS_UNREACHABLE) {
+ // Connect() may return ERR_ADDRESS_UNREACHABLE for IPv6
+ // addresses if IPv6 is not configured.
+ continue;
+ }
+
+ EXPECT_LE(ERR_IO_PENDING, rv);
+
+ IPEndPoint fetched_local_address;
+ rv = client.GetLocalAddress(&fetched_local_address);
+ EXPECT_EQ(OK, rv);
+
+ // TODO(mbelshe): figure out how to verify the IP and port.
+ // The port is dynamically generated by the udp stack.
+ // The IP is the real IP of the client, not necessarily
+ // loopback.
+ //EXPECT_EQ(local_address.address(), fetched_local_address.address());
+
+ IPEndPoint fetched_remote_address;
+ rv = client.GetPeerAddress(&fetched_remote_address);
+ EXPECT_EQ(OK, rv);
+
+ EXPECT_EQ(remote_address, fetched_remote_address);
+ }
+}
+
+TEST_F(UDPSocketTest, ServerGetLocalAddress) {
+ IPEndPoint bind_address;
+ CreateUDPAddress("127.0.0.1", 0, &bind_address);
+ UDPServerSocket server(NULL, NetLog::Source());
+ int rv = server.Listen(bind_address);
+ EXPECT_EQ(OK, rv);
+
+ IPEndPoint local_address;
+ rv = server.GetLocalAddress(&local_address);
+ EXPECT_EQ(rv, 0);
+
+ // Verify that port was allocated.
+ EXPECT_GT(local_address.port(), 0);
+ EXPECT_EQ(local_address.address(), bind_address.address());
+}
+
+TEST_F(UDPSocketTest, ServerGetPeerAddress) {
+ IPEndPoint bind_address;
+ CreateUDPAddress("127.0.0.1", 0, &bind_address);
+ UDPServerSocket server(NULL, NetLog::Source());
+ int rv = server.Listen(bind_address);
+ EXPECT_EQ(OK, rv);
+
+ IPEndPoint peer_address;
+ rv = server.GetPeerAddress(&peer_address);
+ EXPECT_EQ(rv, ERR_SOCKET_NOT_CONNECTED);
+}
+
+// Close the socket while read is pending.
+TEST_F(UDPSocketTest, CloseWithPendingRead) {
+ IPEndPoint bind_address;
+ CreateUDPAddress("127.0.0.1", 0, &bind_address);
+ UDPServerSocket server(NULL, NetLog::Source());
+ int rv = server.Listen(bind_address);
+ EXPECT_EQ(OK, rv);
+
+ TestCompletionCallback callback;
+ IPEndPoint from;
+ rv = server.RecvFrom(buffer_.get(), kMaxRead, &from, callback.callback());
+ EXPECT_EQ(rv, ERR_IO_PENDING);
+
+ server.Close();
+
+ EXPECT_FALSE(callback.have_result());
+}
+
+#if defined(OS_ANDROID)
+// Some Android devices do not support multicast socket.
+// The ones supporting multicast need WifiManager.MulitcastLock to enable it.
+// http://goo.gl/jjAk9
+#define MAYBE_JoinMulticastGroup DISABLED_JoinMulticastGroup
+#else
+#define MAYBE_JoinMulticastGroup JoinMulticastGroup
+#endif // defined(OS_ANDROID)
+
+TEST_F(UDPSocketTest, MAYBE_JoinMulticastGroup) {
+ const int kPort = 9999;
+ const char* const kGroup = "237.132.100.17";
+
+ IPEndPoint bind_address;
+ CreateUDPAddress("0.0.0.0", kPort, &bind_address);
+ IPAddressNumber group_ip;
+ EXPECT_TRUE(ParseIPLiteralToNumber(kGroup, &group_ip));
+
+ UDPSocket socket(DatagramSocket::DEFAULT_BIND,
+ RandIntCallback(),
+ NULL,
+ NetLog::Source());
+ EXPECT_EQ(OK, socket.Bind(bind_address));
+ EXPECT_EQ(OK, socket.JoinGroup(group_ip));
+ // Joining group multiple times.
+ EXPECT_NE(OK, socket.JoinGroup(group_ip));
+ EXPECT_EQ(OK, socket.LeaveGroup(group_ip));
+ // Leaving group multiple times.
+ EXPECT_NE(OK, socket.LeaveGroup(group_ip));
+
+ socket.Close();
+}
+
+TEST_F(UDPSocketTest, MulticastOptions) {
+ const int kPort = 9999;
+ IPEndPoint bind_address;
+ CreateUDPAddress("0.0.0.0", kPort, &bind_address);
+
+ UDPSocket socket(DatagramSocket::DEFAULT_BIND,
+ RandIntCallback(),
+ NULL,
+ NetLog::Source());
+ // Before binding.
+ EXPECT_EQ(OK, socket.SetMulticastLoopbackMode(false));
+ EXPECT_EQ(OK, socket.SetMulticastLoopbackMode(true));
+ EXPECT_EQ(OK, socket.SetMulticastTimeToLive(0));
+ EXPECT_EQ(OK, socket.SetMulticastTimeToLive(3));
+ EXPECT_NE(OK, socket.SetMulticastTimeToLive(-1));
+
+ EXPECT_EQ(OK, socket.Bind(bind_address));
+
+ socket.Close();
+}
+
+} // namespace
+
+} // namespace net