blob: 01fb57f78ef3d37ca1e4708adae500d45a4b0a22 [file] [log] [blame]
// Copyright 2014 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/base/sockets/SocketUtils.h"
#include "android/base/sockets/ScopedSocket.h"
#include <gtest/gtest.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
namespace android {
namespace base {
TEST(SocketUtils, socketSendAll) {
char kData[] = "Hello World!";
const size_t kDataLen = sizeof(kData) - 1U;
int sock[2];
ASSERT_EQ(0, socketCreatePair(&sock[0], &sock[1]));
EXPECT_TRUE(socketSendAll(sock[0], kData, kDataLen));
char data[kDataLen] = {};
for (size_t n = 0; n < kDataLen; ++n) {
EXPECT_EQ(1, socketRecv(sock[1], &data[n], 1)) << "#" << n;
EXPECT_EQ(kData[n], data[n]) << "#" << n;
}
socketClose(sock[1]);
socketClose(sock[0]);
}
TEST(SocketUtils, socketRecvAll) {
char kData[] = "Hello World!";
const size_t kDataLen = sizeof(kData) - 1U;
int sock[2];
ASSERT_EQ(0, socketCreatePair(&sock[0], &sock[1]));
for (size_t n = 0; n < kDataLen; ++n) {
EXPECT_EQ(1, socketSend(sock[0], &kData[n], 1)) << "#" << n;
}
char data[kDataLen] = {};
EXPECT_TRUE(socketRecvAll(sock[1], data, kDataLen));
for (size_t n = 0; n < kDataLen; ++n) {
EXPECT_EQ(kData[n], data[n]) << "#" << n;
}
socketClose(sock[1]);
socketClose(sock[0]);
}
TEST(SocketUtils, socketGetPort) {
ScopedSocket s0;
// Find a free TCP IPv4 port and bind to it.
int port;
for (port = 1024; port < 65536; ++port) {
s0.reset(socketTcp4LoopbackServer(port));
if (s0.valid()) {
break;
}
}
ASSERT_TRUE(s0.valid()) << "Could not find free TCP port";
EXPECT_EQ(port, socketGetPort(s0.get()));
// Connect to it.
ScopedSocket s1(socketTcp4LoopbackClient(port));
ASSERT_TRUE(s1.valid());
// Accept it to create a pair of connected socket.
// The port number of |s2| should be |port|.
ScopedSocket s2(socketAcceptAny(s0.get()));
ASSERT_TRUE(s2.valid());
EXPECT_EQ(port, socketGetPort(s2.get()));
}
TEST(SocketUtils, socketGetPeerPort) {
// Create a server socket bound to a random port.
ScopedSocket s0(socketTcp4LoopbackServer(0));
ASSERT_TRUE(s0.valid());
int serverPort = socketGetPort(s0.get());
ASSERT_GT(serverPort, 0);
// Connect to it.
ScopedSocket s1(socketTcp4LoopbackClient(serverPort));
ASSERT_TRUE(s1.valid()) << "Could not connect to server socket";
// Accept it to create a pair of connected sockets.
ScopedSocket s2(socketAcceptAny(s0.get()));
ASSERT_TRUE(s2.valid());
EXPECT_EQ(socketGetPort(s1.get()), socketGetPeerPort(s2.get()));
EXPECT_EQ(socketGetPort(s2.get()), socketGetPeerPort(s1.get()));
}
#ifndef _WIN32
namespace {
// Helper class to temporary install a SIGPIPE signal handler that
// will simply store the signal number into a global variable.
class SigPipeSignalHandler {
public:
SigPipeSignalHandler() {
sSignal = -1;
struct sigaction act = {};
act.sa_handler = myHandler;
::sigaction(SIGPIPE, &act, &mOldAction);
}
~SigPipeSignalHandler() {
::sigaction(SIGPIPE, &mOldAction, nullptr);
}
int signaled() const { return sSignal; }
private:
struct sigaction mOldAction;
static int sSignal;
static void myHandler(int sig) { sSignal = sig; }
};
// static
int SigPipeSignalHandler::sSignal = 0;
} // namespace
TEST(SocketUtils, socketSendDoesNotGenerateSigPipe) {
// Check that writing to a broken pipe does not generate a SIGPIPE
// signal on non-Windows platforms.
// First, check that this is true for socket pairs.
{
SigPipeSignalHandler handler;
int s1, s2;
EXPECT_EQ(-1, handler.signaled());
ASSERT_EQ(0, socketCreatePair(&s1, &s2));
socketClose(s2);
errno = 0;
EXPECT_EQ(-1, socketSend(s1, "x", 1));
EXPECT_EQ(EPIPE, errno);
EXPECT_EQ(-1, handler.signaled());
socketClose(s1);
}
// Second, check that this is true for random TCP sockets.
{
SigPipeSignalHandler handler;
EXPECT_EQ(-1, handler.signaled());
ScopedSocket s1(socketTcp4LoopbackServer(0)); // Bind to random port
ASSERT_TRUE(s1.valid());
int port = socketGetPort(s1.get());
ScopedSocket s2(socketTcp4LoopbackClient(port));
ASSERT_TRUE(s2. valid());
ScopedSocket s3(socketAcceptAny(s1.get()));
ASSERT_TRUE(s3.valid());
// s2 and s3 are now connected. Close s2 immediately, then try to
// send data through s3.
socketShutdownReads(s2.get());
s2.close();
// The EPIPE might not happen on the first send due to
// TCP packet buffering in the kernel. Perform multiple send()
// in a loop to work-around this.
errno = 0;
const int kMaxSendCount = 100;
int n = 0;
while (n < kMaxSendCount) {
int ret = socketSend(s3.get(), "xxxx", 4);
if (ret < 0) {
#ifdef __APPLE__
// On OS X, errno is sometimes EPROTOTYPE instead of EPIPE
// when this happens.
EXPECT_TRUE(errno == EPIPE || errno == EPROTOTYPE)
<< strerror(errno);
#else
EXPECT_EQ(EPIPE, errno) << strerror(errno);
#endif
break;
}
n++;
}
// For the record, the value of |n| would typically be 2 on Linux,
// and 9 or 10 on Darwin, when run on local workstations.
EXPECT_LT(n, kMaxSendCount);
EXPECT_EQ(-1, handler.signaled());
}
}
#endif // !_WIN32
} // namespace base
} // namespace android