blob: c13ae0fc216092cbcc4a071be4b91aebd7edd715 [file] [log] [blame]
// Copyright 2013 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 <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <map>
#include <string>
#include "echo_server.h"
#include "gtest/gtest.h"
#include "nacl_io/kernel_intercept.h"
#include "nacl_io/kernel_proxy.h"
#include "nacl_io/ossocket.h"
#include "nacl_io/ostypes.h"
#include "ppapi/cpp/message_loop.h"
#include "ppapi_simple/ps.h"
#ifdef PROVIDES_SOCKET_API
using namespace nacl_io;
using namespace sdk_util;
#define LOCAL_HOST 0x7F000001
#define PORT1 4006
#define PORT2 4007
#define ANY_PORT 0
namespace {
void IP4ToSockAddr(uint32_t ip, uint16_t port, struct sockaddr_in* addr) {
memset(addr, 0, sizeof(*addr));
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
addr->sin_addr.s_addr = htonl(ip);
}
static int ki_fcntl_wrapper(int fd, int request, ...) {
va_list ap;
va_start(ap, request);
int rtn = ki_fcntl(fd, request, ap);
va_end(ap);
return rtn;
}
static void SetNonBlocking(int sock) {
int flags = ki_fcntl_wrapper(sock, F_GETFL);
ASSERT_NE(-1, flags);
flags |= O_NONBLOCK;
ASSERT_EQ(0, ki_fcntl_wrapper(sock, F_SETFL, flags));
ASSERT_EQ(flags, ki_fcntl_wrapper(sock, F_GETFL));
}
class SocketTest : public ::testing::Test {
public:
SocketTest() : sock1_(-1), sock2_(-1) {}
void TearDown() {
if (sock1_ != -1)
EXPECT_EQ(0, ki_close(sock1_));
if (sock2_ != -1)
EXPECT_EQ(0, ki_close(sock2_));
}
int Bind(int fd, uint32_t ip, uint16_t port) {
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
IP4ToSockAddr(ip, port, &addr);
int err = ki_bind(fd, (sockaddr*)&addr, addrlen);
if (err == -1)
return errno;
return 0;
}
protected:
int sock1_;
int sock2_;
};
class SocketTestUDP : public SocketTest {
public:
SocketTestUDP() {}
void SetUp() {
sock1_ = ki_socket(AF_INET, SOCK_DGRAM, 0);
sock2_ = ki_socket(AF_INET, SOCK_DGRAM, 0);
EXPECT_GT(sock1_, -1);
EXPECT_GT(sock2_, -1);
}
};
class SocketTestTCP : public SocketTest {
public:
SocketTestTCP() {}
void SetUp() {
sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);
sock2_ = ki_socket(AF_INET, SOCK_STREAM, 0);
EXPECT_GT(sock1_, -1);
EXPECT_GT(sock2_, -1);
}
};
class SocketTestWithServer : public ::testing::Test {
public:
SocketTestWithServer() : instance_(PSGetInstanceId()) {
pthread_mutex_init(&ready_lock_, NULL);
pthread_cond_init(&ready_cond_, NULL);
}
void ServerThreadMain() {
loop_.AttachToCurrentThread();
pp::Instance instance(PSGetInstanceId());
EchoServer server(&instance, PORT1, ServerLog, &ready_cond_, &ready_lock_);
loop_.Run();
}
static void* ServerThreadMainStatic(void* arg) {
SocketTestWithServer* test = (SocketTestWithServer*)arg;
test->ServerThreadMain();
return NULL;
}
void SetUp() {
loop_ = pp::MessageLoop(&instance_);
pthread_mutex_lock(&ready_lock_);
// Start an echo server on a background thread.
pthread_create(&server_thread_, NULL, ServerThreadMainStatic, this);
// Wait for thread to signal that it is ready to accept connections.
pthread_cond_wait(&ready_cond_, &ready_lock_);
pthread_mutex_unlock(&ready_lock_);
sock_ = ki_socket(AF_INET, SOCK_STREAM, 0);
EXPECT_GT(sock_, -1);
}
void TearDown() {
// Stop the echo server and the background thread it runs on
loop_.PostQuit(true);
pthread_join(server_thread_, NULL);
ASSERT_EQ(0, ki_close(sock_));
}
static void ServerLog(const char* msg) {
// Uncomment to see logs of echo server on stdout
//printf("server: %s\n", msg);
}
protected:
int sock_;
pp::MessageLoop loop_;
pp::Instance instance_;
pthread_cond_t ready_cond_;
pthread_mutex_t ready_lock_;
pthread_t server_thread_;
};
} // namespace
TEST(SocketTestSimple, Socket) {
EXPECT_EQ(-1, ki_socket(AF_UNIX, SOCK_STREAM, 0));
EXPECT_EQ(errno, EAFNOSUPPORT);
// We don't support RAW sockets
EXPECT_EQ(-1, ki_socket(AF_INET, SOCK_RAW, IPPROTO_TCP));
EXPECT_EQ(EPROTONOSUPPORT, errno);
// Invalid type
EXPECT_EQ(-1, ki_socket(AF_INET, -1, 0));
EXPECT_EQ(EINVAL, errno);
int sock1_ = ki_socket(AF_INET, SOCK_DGRAM, 0);
EXPECT_NE(-1, sock1_);
int sock2_ = ki_socket(AF_INET6, SOCK_DGRAM, 0);
EXPECT_NE(-1, sock2_);
int sock3 = ki_socket(AF_INET, SOCK_STREAM, 0);
EXPECT_NE(-1, sock3);
int sock4 = ki_socket(AF_INET6, SOCK_STREAM, 0);
EXPECT_NE(-1, sock4);
ki_close(sock1_);
ki_close(sock2_);
ki_close(sock3);
ki_close(sock4);
}
TEST_F(SocketTestUDP, Bind) {
// Bind away.
EXPECT_EQ(0, Bind(sock1_, LOCAL_HOST, PORT1));
// Invalid to rebind a socket.
EXPECT_EQ(EINVAL, Bind(sock1_, LOCAL_HOST, PORT1));
// Addr in use.
EXPECT_EQ(EADDRINUSE, Bind(sock2_, LOCAL_HOST, PORT1));
// Bind with a wildcard.
EXPECT_EQ(0, Bind(sock2_, LOCAL_HOST, ANY_PORT));
// Invalid to rebind after wildcard
EXPECT_EQ(EINVAL, Bind(sock2_, LOCAL_HOST, PORT1));
}
TEST_F(SocketTestUDP, SendRecv) {
char outbuf[256];
char inbuf[512];
memset(outbuf, 1, sizeof(outbuf));
memset(inbuf, 0, sizeof(inbuf));
EXPECT_EQ(0, Bind(sock1_, LOCAL_HOST, PORT1));
EXPECT_EQ(0, Bind(sock2_, LOCAL_HOST, PORT2));
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
IP4ToSockAddr(LOCAL_HOST, PORT2, &addr);
int len1 =
ki_sendto(sock1_, outbuf, sizeof(outbuf), 0, (sockaddr*) &addr, addrlen);
EXPECT_EQ(sizeof(outbuf), len1);
// Ensure the buffers are different
EXPECT_NE(0, memcmp(outbuf, inbuf, sizeof(outbuf)));
memset(&addr, 0, sizeof(addr));
// Try to receive the previously sent packet
int len2 =
ki_recvfrom(sock2_, inbuf, sizeof(inbuf), 0, (sockaddr*) &addr, &addrlen);
EXPECT_EQ(sizeof(outbuf), len2);
EXPECT_EQ(sizeof(sockaddr_in), addrlen);
EXPECT_EQ(PORT1, htons(addr.sin_port));
// Now they should be the same
EXPECT_EQ(0, memcmp(outbuf, inbuf, sizeof(outbuf)));
}
TEST_F(SocketTestUDP, SendRecvUnbound) {
char outbuf[256];
char inbuf[512];
memset(outbuf, 1, sizeof(outbuf));
memset(inbuf, 0, sizeof(inbuf));
// Don't bind sock1_, this will automatically bind sock1_ to a random port
// at the time of the first send.
EXPECT_EQ(0, Bind(sock2_, LOCAL_HOST, PORT2));
sockaddr_in addr;
sockaddr_in addr2;
socklen_t addrlen = sizeof(addr2);
IP4ToSockAddr(LOCAL_HOST, PORT2, &addr2);
// The first send hasn't occurred, so the socket is not yet bound.
socklen_t out_addrlen = sizeof(addr);
ASSERT_EQ(0, ki_getsockname(sock1_, (sockaddr*)&addr, &out_addrlen));
EXPECT_EQ(addrlen, out_addrlen);
EXPECT_EQ(0, htonl(addr.sin_addr.s_addr));
EXPECT_EQ(0, htons(addr.sin_port));
int len1 =
ki_sendto(sock1_, outbuf, sizeof(outbuf), 0, (sockaddr*) &addr2, addrlen);
EXPECT_EQ(sizeof(outbuf), len1);
// After the first send, the socket should be bound; the port is set, but
// the address is still 0.
ASSERT_EQ(0, ki_getsockname(sock1_, (sockaddr*)&addr, &out_addrlen));
EXPECT_EQ(addrlen, out_addrlen);
EXPECT_EQ(0, htonl(addr.sin_addr.s_addr));
EXPECT_NE(0, htons(addr.sin_port));
// Ensure the buffers are different
EXPECT_NE(0, memcmp(outbuf, inbuf, sizeof(outbuf)));
// Try to receive the previously sent packet
int len2 =
ki_recvfrom(sock2_, inbuf, sizeof(inbuf), 0, (sockaddr*) &addr, &addrlen);
EXPECT_EQ(sizeof(outbuf), len2);
EXPECT_EQ(sizeof(sockaddr_in), addrlen);
EXPECT_EQ(LOCAL_HOST, htonl(addr.sin_addr.s_addr));
EXPECT_NE(0, htons(addr.sin_port));
// Now they should be the same
EXPECT_EQ(0, memcmp(outbuf, inbuf, sizeof(outbuf)));
}
const size_t kQueueSize = 65536 * 8;
TEST_F(SocketTestUDP, FullFifo) {
char outbuf[16 * 1024];
ASSERT_EQ(0, Bind(sock1_, LOCAL_HOST, PORT1));
ASSERT_EQ(0, Bind(sock2_, LOCAL_HOST, PORT2));
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
IP4ToSockAddr(LOCAL_HOST, PORT2, &addr);
size_t total = 0;
while (total < kQueueSize * 8) {
int len = ki_sendto(sock1_, outbuf, sizeof(outbuf), MSG_DONTWAIT,
(sockaddr*) &addr, addrlen);
if (len <= 0) {
EXPECT_EQ(-1, len);
EXPECT_EQ(EWOULDBLOCK, errno);
break;
}
if (len >= 0) {
EXPECT_EQ(sizeof(outbuf), len);
total += len;
}
}
EXPECT_GT(total, kQueueSize - 1);
EXPECT_LT(total, kQueueSize * 8);
}
TEST_F(SocketTestWithServer, TCPConnect) {
char outbuf[256];
char inbuf[512];
memset(outbuf, 1, sizeof(outbuf));
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
ASSERT_EQ(0, ki_connect(sock_, (sockaddr*) &addr, addrlen))
<< "Failed with " << errno << ": " << strerror(errno);
// Send two different messages to the echo server and verify the
// response matches.
strcpy(outbuf, "hello");
memset(inbuf, 0, sizeof(inbuf));
ASSERT_EQ(sizeof(outbuf), ki_write(sock_, outbuf, sizeof(outbuf)))
<< "socket write failed with: " << strerror(errno);
ASSERT_EQ(sizeof(outbuf), ki_read(sock_, inbuf, sizeof(inbuf)));
EXPECT_EQ(0, memcmp(outbuf, inbuf, sizeof(outbuf)));
strcpy(outbuf, "world");
memset(inbuf, 0, sizeof(inbuf));
ASSERT_EQ(sizeof(outbuf), ki_write(sock_, outbuf, sizeof(outbuf)));
ASSERT_EQ(sizeof(outbuf), ki_read(sock_, inbuf, sizeof(inbuf)));
EXPECT_EQ(0, memcmp(outbuf, inbuf, sizeof(outbuf)));
}
TEST_F(SocketTestWithServer, TCPConnectNonBlock) {
char outbuf[256];
//char inbuf[512];
memset(outbuf, 1, sizeof(outbuf));
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
SetNonBlocking(sock_);
ASSERT_EQ(-1, ki_connect(sock_, (sockaddr*) &addr, addrlen));
ASSERT_EQ(EINPROGRESS, errno)
<< "expected EINPROGRESS but got: " << strerror(errno);
ASSERT_EQ(-1, ki_connect(sock_, (sockaddr*) &addr, addrlen));
ASSERT_EQ(EALREADY, errno);
// Wait for the socket connection to complete using poll()
struct pollfd pollfd = { sock_, POLLIN|POLLOUT, 0 };
ASSERT_EQ(1, ki_poll(&pollfd, 1, -1));
ASSERT_EQ(POLLOUT, pollfd.revents);
// Attempts to connect again should yield EISCONN
ASSERT_EQ(-1, ki_connect(sock_, (sockaddr*) &addr, addrlen));
ASSERT_EQ(EISCONN, errno);
// And SO_ERROR should be 0.
}
TEST_F(SocketTestTCP, TCPConnectFails) {
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
// 10 is an unassigned well-known port, nothing should be bound to it.
IP4ToSockAddr(LOCAL_HOST, 10, &addr);
ASSERT_EQ(-1, ki_connect(sock1_, (sockaddr*) &addr, addrlen));
ASSERT_EQ(ECONNREFUSED, errno);
}
TEST_F(SocketTest, Getsockopt) {
sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);
EXPECT_GT(sock1_, -1);
sock2_ = ki_socket(AF_INET, SOCK_DGRAM, 0);
EXPECT_GT(sock1_, -1);
int socket_error = 99;
socklen_t len = sizeof(socket_error);
// Test for valid option (SO_ERROR) which should be 0 when a socket
// is first created.
ASSERT_EQ(0, ki_getsockopt(sock1_, SOL_SOCKET, SO_ERROR,
&socket_error, &len));
ASSERT_EQ(0, socket_error);
ASSERT_EQ(sizeof(socket_error), len);
// Check SO_TYPE for TCP sockets
int socket_type = 0;
len = sizeof(socket_type);
ASSERT_EQ(0, ki_getsockopt(sock1_, SOL_SOCKET, SO_TYPE, &socket_type, &len));
ASSERT_EQ(SOCK_STREAM, socket_type);
ASSERT_EQ(sizeof(socket_type), len);
// Check SO_TYPE for UDP sockets
socket_type = 0;
len = sizeof(socket_type);
ASSERT_EQ(0, ki_getsockopt(sock2_, SOL_SOCKET, SO_TYPE, &socket_type, &len));
ASSERT_EQ(SOCK_DGRAM, socket_type);
ASSERT_EQ(sizeof(socket_type), len);
// Test for an invalid option (-1)
ASSERT_EQ(-1, ki_getsockopt(sock1_, SOL_SOCKET, -1, &socket_error, &len));
ASSERT_EQ(ENOPROTOOPT, errno);
}
TEST_F(SocketTest, Setsockopt) {
sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);
EXPECT_GT(sock1_, -1);
// It should not be possible to set SO_ERROR using setsockopt.
int socket_error = 10;
socklen_t len = sizeof(socket_error);
ASSERT_EQ(-1, ki_setsockopt(sock1_, SOL_SOCKET, SO_ERROR,
&socket_error, len));
ASSERT_EQ(ENOPROTOOPT, errno);
}
TEST_F(SocketTest, Sockopt_TCP_NODELAY) {
int option = 0;
socklen_t len = sizeof(option);
// Getting and setting TCP_NODELAY on UDP socket should fail
sock1_ = ki_socket(AF_INET, SOCK_DGRAM, 0);
ASSERT_EQ(-1, ki_setsockopt(sock1_, IPPROTO_TCP, TCP_NODELAY, &option, len));
ASSERT_EQ(ENOPROTOOPT, errno);
ASSERT_EQ(-1, ki_getsockopt(sock1_, IPPROTO_TCP, TCP_NODELAY, &option, &len));
ASSERT_EQ(ENOPROTOOPT, errno);
// Getting and setting TCP_NODELAY on TCP socket should preserve value
sock2_ = ki_socket(AF_INET, SOCK_STREAM, 0);
ASSERT_EQ(0, ki_getsockopt(sock2_, IPPROTO_TCP, TCP_NODELAY, &option, &len));
ASSERT_EQ(0, option);
ASSERT_EQ(sizeof(option), len);
option = 1;
len = sizeof(option);
ASSERT_EQ(0, ki_setsockopt(sock2_, IPPROTO_TCP, TCP_NODELAY, &option, len))
<< "Failed with " << errno << ": " << strerror(errno);
ASSERT_EQ(1, option);
}
TEST_F(SocketTest, Sockopt_KEEPALIVE) {
sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);
ASSERT_GT(sock1_, -1);
sock2_ = ki_socket(AF_INET, SOCK_DGRAM, 0);
ASSERT_GT(sock2_, -1);
int value = 0;
socklen_t len = sizeof(value);
ASSERT_EQ(0, ki_getsockopt(sock1_, SOL_SOCKET, SO_KEEPALIVE, &value, &len));
ASSERT_EQ(0, value);
ASSERT_EQ(sizeof(int), len);
}
// Disabled until we support SO_LINGER (i.e. syncronouse close()/shutdown())
// TODO(sbc): re-enable once we fix http://crbug.com/312401
TEST_F(SocketTest, DISABLED_Sockopt_LINGER) {
sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);
ASSERT_GT(sock1_, -1);
sock2_ = ki_socket(AF_INET, SOCK_DGRAM, 0);
ASSERT_GT(sock2_, -1);
struct linger linger = { 7, 8 };
socklen_t len = sizeof(linger);
ASSERT_EQ(0, ki_getsockopt(sock1_, SOL_SOCKET, SO_LINGER, &linger, &len));
ASSERT_EQ(0, linger.l_onoff);
ASSERT_EQ(0, linger.l_linger);
ASSERT_EQ(sizeof(struct linger), len);
ASSERT_EQ(0, ki_getsockopt(sock2_, SOL_SOCKET, SO_LINGER, &linger, &len));
ASSERT_EQ(0, linger.l_onoff);
ASSERT_EQ(0, linger.l_linger);
ASSERT_EQ(sizeof(struct linger), len);
linger.l_onoff = 1;
linger.l_linger = 77;
len = sizeof(linger);
ASSERT_EQ(0, ki_setsockopt(sock1_, SOL_SOCKET, SO_LINGER, &linger, len));
linger.l_onoff = 1;
linger.l_linger = 88;
ASSERT_EQ(0, ki_setsockopt(sock2_, SOL_SOCKET, SO_LINGER, &linger, len));
len = sizeof(linger);
ASSERT_EQ(0, ki_getsockopt(sock1_, SOL_SOCKET, SO_LINGER, &linger, &len));
ASSERT_EQ(1, linger.l_onoff);
ASSERT_EQ(77, linger.l_linger);
ASSERT_EQ(sizeof(struct linger), len);
ASSERT_EQ(0, ki_getsockopt(sock2_, SOL_SOCKET, SO_LINGER, &linger, &len));
ASSERT_EQ(1, linger.l_onoff);
ASSERT_EQ(88, linger.l_linger);
ASSERT_EQ(sizeof(struct linger), len);
}
TEST_F(SocketTest, Sockopt_REUSEADDR) {
int value = 1;
socklen_t len = sizeof(value);
sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);
ASSERT_GT(sock1_, -1);
ASSERT_EQ(0, ki_setsockopt(sock1_, SOL_SOCKET, SO_REUSEADDR, &value, len));
value = 0;
len = sizeof(value);
ASSERT_EQ(0, ki_getsockopt(sock1_, SOL_SOCKET, SO_REUSEADDR, &value, &len));
ASSERT_EQ(1, value);
ASSERT_EQ(sizeof(int), len);
}
// The size of the data to send is deliberately chosen to be
// larger than the TCP buffer in nacl_io.
// TODO(sbc): use ioctl to discover the actual buffer size at
// runtime.
#define LARGE_SEND_BYTES (800 * 1024)
TEST_F(SocketTestWithServer, LargeSend) {
char* outbuf = (char*)malloc(LARGE_SEND_BYTES);
char* inbuf = (char*)malloc(LARGE_SEND_BYTES);
int bytes_sent = 0;
int bytes_received = 0;
// Fill output buffer with ascending integers
int* outbuf_int = (int*)outbuf;
int* inbuf_int = (int*)inbuf;
for (int i = 0; i < LARGE_SEND_BYTES/sizeof(int); i++) {
outbuf_int[i] = i;
}
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
ASSERT_EQ(0, ki_connect(sock_, (sockaddr*) &addr, addrlen))
<< "Failed with " << errno << ": " << strerror(errno);
// Call send an recv until all bytes have been transfered.
while (bytes_received < LARGE_SEND_BYTES) {
if (bytes_sent < LARGE_SEND_BYTES) {
int sent = ki_send(sock_, outbuf + bytes_sent,
LARGE_SEND_BYTES - bytes_sent, MSG_DONTWAIT);
if (sent < 0)
ASSERT_EQ(EWOULDBLOCK, errno) << "send failed: " << strerror(errno);
else
bytes_sent += sent;
}
int received = ki_recv(sock_, inbuf + bytes_received,
LARGE_SEND_BYTES - bytes_received, MSG_DONTWAIT);
if (received < 0)
ASSERT_EQ(EWOULDBLOCK, errno) << "recv failed: " << strerror(errno);
else
bytes_received += received;
}
// Make sure there is nothing else to recv at this point
char dummy[10];
ASSERT_EQ(-1, ki_recv(sock_, dummy, 10, MSG_DONTWAIT));
ASSERT_EQ(EWOULDBLOCK, errno);
int errors = 0;
for (int i = 0; i < LARGE_SEND_BYTES/4; i++) {
if (inbuf_int[i] != outbuf_int[i]) {
printf("%d: in=%d out=%d\n", i, inbuf_int[i], outbuf_int[i]);
if (errors++ > 50)
break;
}
}
for (int i = 0; i < LARGE_SEND_BYTES; i++) {
ASSERT_EQ(outbuf[i], inbuf[i]) << "cmp failed at " << i;
}
ASSERT_EQ(0, memcmp(inbuf, outbuf, LARGE_SEND_BYTES));
free(inbuf);
free(outbuf);
}
TEST_F(SocketTestUDP, Listen) {
EXPECT_EQ(-1, ki_listen(sock1_, 10));
EXPECT_EQ(errno, EOPNOTSUPP);
}
TEST_F(SocketTestUDP, Sockopt_BUFSIZE) {
int option = 1024*1024;
socklen_t len = sizeof(option);
ASSERT_EQ(0, Bind(sock1_, LOCAL_HOST, ANY_PORT));
// Modify the test to verify the change by calling getsockopt
// once UDPInterface supports GetOption() call
ASSERT_EQ(0, ki_setsockopt(sock1_, SOL_SOCKET, SO_RCVBUF, &option, len))
<< "failed with: " << strerror(errno);
ASSERT_EQ(0, ki_setsockopt(sock1_, SOL_SOCKET, SO_SNDBUF, &option, len))
<< "failed with: " << strerror(errno);
}
TEST_F(SocketTestUDP, Sockopt_BROADCAST) {
int option = 1;
socklen_t len = sizeof(option);
ASSERT_EQ(0, Bind(sock1_, LOCAL_HOST, ANY_PORT));
// Modify the test to verify the change by calling getsockopt
// once UDPInterface supports GetOption() call
ASSERT_EQ(0, ki_setsockopt(sock1_, SOL_SOCKET, SO_BROADCAST, &option, len))
<< "failed with: " << strerror(errno);
}
TEST_F(SocketTestTCP, AcceptNoParams) {
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int server_sock = sock1_;
// Bind and Listen
ASSERT_EQ(0, Bind(server_sock, LOCAL_HOST, PORT1));
ASSERT_EQ(0, ki_listen(server_sock, 10));
// Connect to listening socket
int client_sock = sock2_;
IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
addrlen = sizeof(addr);
ASSERT_EQ(0, ki_connect(client_sock, (sockaddr*)&addr, addrlen))
<< "Failed with " << errno << ": " << strerror(errno);
// Accept without addr and len should succeed
int new_socket = ki_accept(server_sock, NULL, NULL);
ASSERT_GT(new_socket, -1);
ASSERT_EQ(0, ki_close(new_socket));
}
TEST_F(SocketTestTCP, Listen) {
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
const char* client_greeting = "hello";
const char* server_reply = "reply";
const int greeting_len = strlen(client_greeting);
const int reply_len = strlen(server_reply);
int server_sock = sock1_;
// Accept before listen should fail
ASSERT_EQ(-1, ki_accept(server_sock, (sockaddr*)&addr, &addrlen));
// Listen should fail on unbound socket
ASSERT_EQ(-1, ki_listen(server_sock, 10));
// Bind and Listen
ASSERT_EQ(0, Bind(server_sock, LOCAL_HOST, PORT1));
ASSERT_EQ(0, ki_listen(server_sock, 10))
<< "listen failed with: " << strerror(errno);
// Connect to listening socket, and send greeting
int client_sock = sock2_;
IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
addrlen = sizeof(addr);
ASSERT_EQ(0, ki_connect(client_sock, (sockaddr*)&addr, addrlen))
<< "Failed with " << errno << ": " << strerror(errno);
ASSERT_EQ(greeting_len, ki_send(client_sock, client_greeting,
greeting_len, 0));
// Pass in addrlen that is larger than our actual address to make
// sure that it is correctly set back to sizeof(sockaddr_in)
sockaddr_in client_addr[2];
sockaddr_in cmp_addr;
memset(&client_addr[0], 0, sizeof(client_addr[0]));
memset(&client_addr[1], 0xab, sizeof(client_addr[1]));
memset(&cmp_addr, 0xab, sizeof(cmp_addr));
addrlen = sizeof(client_addr[0]) + 10;
int new_socket = ki_accept(server_sock, (sockaddr*)&client_addr[0],
&addrlen);
ASSERT_GT(new_socket, -1)
<< "accept failed with " << errno << ": " << strerror(errno);
ASSERT_EQ(addrlen, sizeof(sockaddr_in));
// Check that client_addr[1] and cmp_addr are the same (not overwritten).
ASSERT_EQ(0, memcmp(&client_addr[1], &cmp_addr, sizeof(cmp_addr)));
ASSERT_EQ(0xabab, client_addr[1].sin_port);
// Verify addr and addrlen were set correctly
ASSERT_EQ(addrlen, sizeof(sockaddr_in));
ASSERT_EQ(0, ki_getsockname(client_sock, (sockaddr*)&client_addr[1],
&addrlen));
ASSERT_EQ(client_addr[1].sin_family, client_addr[0].sin_family);
ASSERT_EQ(client_addr[1].sin_port, client_addr[0].sin_port);
ASSERT_EQ(client_addr[1].sin_addr.s_addr, client_addr[0].sin_addr.s_addr);
// Try a call where the supplied len is smaller than the expected length.
// The API should only write up to that amount, but should return the
// expected length.
sockaddr_in client_addr2;
memset(&client_addr2, 0, sizeof(client_addr2));
// truncated_len is the size of the structure up to and including sin_family.
// TODO(sbc): Fix this test so it doesn't depend on the layout of the
// sockaddr_in structure.
socklen_t truncated_len = offsetof(sockaddr_in, sin_family) +
sizeof(client_addr2.sin_family);
ASSERT_GT(sizeof(sockaddr_in), truncated_len);
ASSERT_EQ(0, ki_getsockname(client_sock, (sockaddr*)&client_addr2,
&truncated_len));
ASSERT_EQ(sizeof(sockaddr_in), truncated_len);
ASSERT_EQ(client_addr2.sin_family, client_addr[0].sin_family);
ASSERT_EQ(client_addr2.sin_port, 0);
ASSERT_EQ(client_addr2.sin_addr.s_addr, 0);
// Recv greeting from client and send reply
char inbuf[512];
ASSERT_EQ(greeting_len, ki_recv(new_socket, inbuf, sizeof(inbuf), 0));
inbuf[greeting_len] = 0;
ASSERT_STREQ(inbuf, client_greeting);
ASSERT_EQ(reply_len, ki_send(new_socket, server_reply, reply_len, 0));
// Recv reply on client socket
ASSERT_EQ(reply_len, ki_recv(client_sock, inbuf, sizeof(inbuf), 0));
inbuf[reply_len] = 0;
ASSERT_STREQ(inbuf, server_reply);
ASSERT_EQ(0, ki_close(new_socket));
}
TEST_F(SocketTestTCP, BindAndGetSockName) {
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
// Bind
ASSERT_EQ(0, Bind(sock1_, LOCAL_HOST, 0));
EXPECT_EQ(0, ki_getsockname(sock1_, (struct sockaddr*)&addr, &addrlen));
EXPECT_NE(0, addr.sin_port);
}
TEST_F(SocketTestTCP, ListenNonBlocking) {
int server_sock = sock1_;
// Set non-blocking
SetNonBlocking(server_sock);
// bind and listen
ASSERT_EQ(0, Bind(server_sock, LOCAL_HOST, PORT1));
ASSERT_EQ(0, ki_listen(server_sock, 10))
<< "listen failed with: " << strerror(errno);
// Accept should fail with EAGAIN since there is no incomming
// connection.
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
ASSERT_EQ(-1, ki_accept(server_sock, (sockaddr*)&addr, &addrlen));
ASSERT_EQ(EAGAIN, errno);
// If we poll the listening socket it should also return
// not readable to indicate that no connections are available
// to accept.
struct pollfd pollfd = { server_sock, POLLIN|POLLOUT, 0 };
ASSERT_EQ(0, ki_poll(&pollfd, 1, 0));
// Connect to listening socket
int client_sock = sock2_;
IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
addrlen = sizeof(addr);
ASSERT_EQ(0, ki_connect(client_sock, (sockaddr*)&addr, addrlen))
<< "Failed with " << errno << ": " << strerror(errno);
// Not poll again but with an infintie timeout.
pollfd.fd = server_sock;
pollfd.events = POLLIN | POLLOUT;
ASSERT_EQ(1, ki_poll(&pollfd, 1, -1));
// Now non-blocking accept should return the new socket
int new_socket = ki_accept(server_sock, (sockaddr*)&addr, &addrlen);
ASSERT_NE(-1, new_socket)
<< "accept failed with: " << strerror(errno);
ASSERT_EQ(0, ki_close(new_socket));
// Accept calls should once again fail with EAGAIN
ASSERT_EQ(-1, ki_accept(server_sock, (sockaddr*)&addr, &addrlen));
ASSERT_EQ(EAGAIN, errno);
// As should polling the listening socket
pollfd.fd = server_sock;
pollfd.events = POLLIN | POLLOUT;
ASSERT_EQ(0, ki_poll(&pollfd, 1, 0));
}
TEST_F(SocketTestTCP, SendRecvAfterRemoteShutdown) {
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int server_sock = sock1_;
int client_sock = sock2_;
// bind and listen
ASSERT_EQ(0, Bind(server_sock, LOCAL_HOST, PORT1));
ASSERT_EQ(0, ki_listen(server_sock, 10))
<< "listen failed with: " << strerror(errno);
// connect to listening socket
IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
ASSERT_EQ(0, ki_connect(client_sock, (sockaddr*)&addr, addrlen))
<< "Failed with " << errno << ": " << strerror(errno);
addrlen = sizeof(addr);
int new_sock = ki_accept(server_sock, (sockaddr*)&addr, &addrlen);
ASSERT_NE(-1, new_sock);
const char* send_buf = "hello world";
ASSERT_EQ(strlen(send_buf), ki_send(new_sock, send_buf, strlen(send_buf), 0));
// Recv first 10 bytes
char buf[256];
ASSERT_EQ(10, ki_recv(client_sock, buf, 10, 0));
// Close the new socket
ASSERT_EQ(0, ki_close(new_sock));
// Sleep for 10 milliseconds. This is designed to allow the shutdown
// event to make its way to the client socket beofre the recv below().
// TODO(sbc): Find a way to test this that doesn't rely on arbitrary sleep.
usleep(100 * 1000);
// Recv remainder
int bytes_remaining = strlen(send_buf) - 10;
ASSERT_EQ(bytes_remaining, ki_recv(client_sock, buf, 256, 0));
// Attempt to read/write after remote shutdown, with no bytes remaining
ASSERT_EQ(0, ki_recv(client_sock, buf, 10, 0));
ASSERT_EQ(0, ki_recv(client_sock, buf, 10, 0));
// It is still legal to send to the remote socket, even after it is closed.
ASSERT_EQ(10, ki_send(client_sock, buf, 10, 0));
}
TEST_F(SocketTestTCP, SendRecvAfterLocalShutdown) {
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int server_sock = sock1_;
int client_sock = sock2_;
// bind and listen
ASSERT_EQ(0, Bind(server_sock, LOCAL_HOST, PORT1));
ASSERT_EQ(0, ki_listen(server_sock, 10))
<< "listen failed with: " << strerror(errno);
// connect to listening socket
IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
ASSERT_EQ(0, ki_connect(client_sock, (sockaddr*)&addr, addrlen))
<< "Failed with " << errno << ": " << strerror(errno);
addrlen = sizeof(addr);
int new_sock = ki_accept(server_sock, (sockaddr*)&addr, &addrlen);
ASSERT_NE(-1, new_sock);
// Close the new socket
ASSERT_EQ(0, ki_shutdown(client_sock, SHUT_RDWR));
// Attempt to read/write after shutdown
char buffer[10];
ASSERT_EQ(0, ki_recv(client_sock, buffer, sizeof(buffer), 0));
ASSERT_EQ(-1, ki_send(client_sock, buffer, sizeof(buffer), 0));
ASSERT_EQ(errno, EPIPE);
}
#define SEND_BYTES (1024)
TEST_F(SocketTestTCP, SendBufferedDataAfterShutdown) {
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int server_sock = sock1_;
int client_sock = sock2_;
// bind and listen
ASSERT_EQ(0, Bind(server_sock, LOCAL_HOST, PORT1));
ASSERT_EQ(0, ki_listen(server_sock, 10))
<< "listen failed with: " << strerror(errno);
// connect to listening socket
IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
ASSERT_EQ(0, ki_connect(client_sock, (sockaddr*)&addr, addrlen))
<< "Failed with " << errno << ": " << strerror(errno);
addrlen = sizeof(addr);
int new_sock = ki_accept(server_sock, (sockaddr*)&addr, &addrlen);
ASSERT_NE(-1, new_sock);
// send a fairly large amount of data and immediately close
// the socket.
void* buffer = alloca(SEND_BYTES);
ASSERT_EQ(SEND_BYTES, ki_send(client_sock, buffer, SEND_BYTES, 0));
ASSERT_EQ(0, ki_close(client_sock));
// avoid double close of sock2_
sock2_ = -1;
// Attempt to recv() all the sent data. None should be lost.
int remainder = SEND_BYTES;
while (remainder > 0) {
int rtn = ki_recv(new_sock, buffer, remainder, 0);
ASSERT_GT(rtn, 0);
remainder -= rtn;
}
ASSERT_EQ(0, ki_close(new_sock));
}
TEST_F(SocketTestTCP, Sockopt_BUFSIZE) {
int option = 1024*1024;
socklen_t len = sizeof(option);
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int server_sock = sock1_;
int client_sock = sock2_;
// bind and listen
ASSERT_EQ(0, Bind(server_sock, LOCAL_HOST, PORT1));
ASSERT_EQ(0, ki_listen(server_sock, 10))
<< "listen failed with: " << strerror(errno);
// connect to listening socket
IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
ASSERT_EQ(0, ki_connect(client_sock, (sockaddr*)&addr, addrlen))
<< "Failed with " << errno << ": " << strerror(errno);
addrlen = sizeof(addr);
int new_sock = ki_accept(server_sock, (sockaddr*)&addr, &addrlen);
ASSERT_NE(-1, new_sock);
// Modify the test to verify the change by calling getsockopt
// once TCPInterface supports GetOption() call
ASSERT_EQ(0, ki_setsockopt(sock2_, SOL_SOCKET, SO_RCVBUF, &option, len))
<< "failed with: " << strerror(errno);
ASSERT_EQ(0, ki_setsockopt(sock2_, SOL_SOCKET, SO_SNDBUF, &option, len))
<< "failed with: " << strerror(errno);
}
TEST_F(SocketTest, Sockopt_IP_MULTICAST) {
int ttl = 1;
socklen_t ttl_len = sizeof(ttl);
int loop = 1;
socklen_t loop_len = sizeof(loop);
// Modify the test to verify the change by calling getsockopt
// once UDPInterface supports GetOption() call
//
// Setting multicast options on TCP socket should fail
sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);
ASSERT_GT(sock1_, -1);
ASSERT_EQ(-1,
ki_setsockopt(sock1_, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, ttl_len));
ASSERT_EQ(ENOPROTOOPT, errno);
ASSERT_EQ(-1, ki_setsockopt(sock1_, IPPROTO_IP, IP_MULTICAST_LOOP, &loop,
loop_len));
ASSERT_EQ(ENOPROTOOPT, errno);
EXPECT_EQ(0, Bind(sock1_, LOCAL_HOST, PORT1));
// Setting SO_BROADCAST on UDP socket should work
sock2_ = ki_socket(AF_INET, SOCK_DGRAM, 0);
ASSERT_GT(sock2_, -1);
// Test invalid values for IP_MULTICAST_TTL (0 <= ttl < 256)
ttl = -1;
ASSERT_EQ(-1,
ki_setsockopt(sock2_, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, ttl_len));
ASSERT_EQ(EINVAL, errno);
ttl = 256;
ASSERT_EQ(-1,
ki_setsockopt(sock2_, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, ttl_len));
ASSERT_EQ(EINVAL, errno);
// Valid IP_MULTICAST_TTL value
ttl = 1;
ASSERT_EQ(0,
ki_setsockopt(sock2_, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, ttl_len));
ASSERT_EQ(1, ttl);
ASSERT_EQ(sizeof(ttl), ttl_len);
// IP_MULTICAST_LOOP
ASSERT_EQ(
0, ki_setsockopt(sock2_, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, loop_len));
ASSERT_EQ(1, loop);
ASSERT_EQ(sizeof(loop), loop_len);
EXPECT_EQ(0, Bind(sock2_, LOCAL_HOST, PORT2));
}
#endif // PROVIDES_SOCKET_API