#include <sys/cdefs.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/un.h>
#include <stdio.h>
#include <atf-c.h>
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
static void
do_socketpair(int *sv)
{
int s;
s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv);
ATF_REQUIRE_EQ(0, s);
ATF_REQUIRE(sv[0] >= 0);
ATF_REQUIRE(sv[1] >= 0);
ATF_REQUIRE(sv[0] != sv[1]);
}
static void
do_socketpair_nonblocking(int *sv)
{
int s;
s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv);
ATF_REQUIRE_EQ(0, s);
ATF_REQUIRE(sv[0] >= 0);
ATF_REQUIRE(sv[1] >= 0);
ATF_REQUIRE(sv[0] != sv[1]);
ATF_REQUIRE(-1 != fcntl(sv[0], F_SETFL, O_NONBLOCK));
ATF_REQUIRE(-1 != fcntl(sv[1], F_SETFL, O_NONBLOCK));
}
static const struct sockaddr_un *
mk_listening_socket(int *sv)
{
static const struct sockaddr_un sun = {
.sun_family = AF_LOCAL,
.sun_len = sizeof(sun),
.sun_path = "sock",
};
int s, r, l;
s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
ATF_REQUIRE(s >= 0);
r = bind(s, (struct sockaddr *)&sun, sizeof(sun));
l = listen(s, -1);
ATF_CHECK_EQ(0, r);
ATF_CHECK_EQ(0, l);
if (sv != NULL)
*sv = s;
return (&sun);
}
static const struct sockaddr_un *
mk_pair_of_sockets(int *sv)
{
const struct sockaddr_un *sun;
int s, s2, err, s1;
sun = mk_listening_socket(&s);
s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
ATF_REQUIRE(s2 >= 0);
err = connect(s2, (struct sockaddr *)sun, sizeof(*sun));
if (err != 0) {
perror("connect");
atf_tc_fail("connect(2) failed");
}
s1 = accept(s, NULL, NULL);
if (s1 == -1) {
perror("accept");
atf_tc_fail("accept(2) failed");
}
sv[0] = s1;
sv[1] = s2;
close(s);
return (sun);
}
static volatile sig_atomic_t got_sigpipe = 0;
static void
shutdown_send_sigpipe_handler(int __unused x)
{
got_sigpipe = 1;
}
static void
test_eagain(int sndbufsize, int rcvbufsize)
{
int i;
int sv[2];
const size_t totalsize = (sndbufsize + rcvbufsize) * 2;
const size_t pktsize = MIN(sndbufsize, rcvbufsize) / 4;
const int numpkts = totalsize / pktsize;
char sndbuf[pktsize];
ssize_t ssize;
do_socketpair_nonblocking(sv);
ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize,
sizeof(sndbufsize)));
ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize,
sizeof(rcvbufsize)));
bzero(sndbuf, pktsize);
for(i=0; i < numpkts; i++) {
ssize = send(sv[0], sndbuf, pktsize, MSG_EOR);
if (ssize == -1) {
if (errno == EAGAIN) {
close(sv[0]);
close(sv[1]);
atf_tc_pass();
}
else {
perror("send");
atf_tc_fail("send returned < 0 but not EAGAIN");
}
}
}
atf_tc_fail("Never got EAGAIN");
}
static void
test_sendrecv_symmetric_buffers(int bufsize, int blocking) {
int s;
int sv[2];
const ssize_t pktsize = bufsize / 2;
char sndbuf[pktsize];
char recv_buf[pktsize];
ssize_t ssize, rsize;
if (blocking)
do_socketpair(sv);
else
do_socketpair_nonblocking(sv);
s = setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
ATF_REQUIRE_EQ(0, s);
s = setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize));
ATF_REQUIRE_EQ(0, s);
bzero(sndbuf, pktsize);
ssize = send(sv[0], sndbuf, pktsize, MSG_EOR);
if (ssize < 0) {
perror("send");
atf_tc_fail("send returned < 0");
}
ATF_CHECK_EQ_MSG(pktsize, ssize, "expected %zd=send(...) but got %zd",
pktsize, ssize);
rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL);
if (rsize < 0) {
perror("recv");
atf_tc_fail("recv returned < 0");
}
ATF_CHECK_EQ_MSG(pktsize, rsize, "expected %zd=send(...) but got %zd",
pktsize, rsize);
close(sv[0]);
close(sv[1]);
}
static void
test_pipe_simulator(int sndbufsize, int rcvbufsize)
{
int num_sent, num_received;
int sv[2];
const ssize_t pktsize = MIN(sndbufsize, rcvbufsize) / 4;
int numpkts;
char sndbuf[pktsize];
char rcvbuf[pktsize];
char comparebuf[pktsize];
ssize_t ssize, rsize;
bool currently_sending = true;
do_socketpair_nonblocking(sv);
ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize,
sizeof(sndbufsize)));
ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize,
sizeof(rcvbufsize)));
numpkts = MAX(sndbufsize, rcvbufsize) * 8 / pktsize;
for (num_sent=0, num_received=0;
num_sent < numpkts || num_received < numpkts; ) {
if (currently_sending && num_sent < numpkts) {
memset(sndbuf, num_sent, pktsize);
ssize = send(sv[0], sndbuf, pktsize, MSG_EOR);
if (ssize < 0) {
if (errno == EAGAIN)
currently_sending = false;
else {
perror("send");
atf_tc_fail("send failed");
}
} else {
ATF_CHECK_EQ_MSG(pktsize, ssize,
"expected %zd=send(...) but got %zd",
pktsize, ssize);
num_sent++;
}
} else {
rsize = recv(sv[1], rcvbuf, pktsize, MSG_WAITALL);
if (rsize < 0) {
if (errno == EAGAIN) {
currently_sending = true;
ATF_REQUIRE_MSG(num_sent < numpkts,
"Packets were lost!");
}
else {
perror("recv");
atf_tc_fail("recv failed");
}
} else {
ATF_CHECK_EQ_MSG(pktsize, rsize,
"expected %zd=recv(...) but got %zd",
pktsize, rsize);
memset(comparebuf, num_received, pktsize);
ATF_CHECK_EQ_MSG(0, memcmp(comparebuf, rcvbuf,
pktsize),
"Received data miscompare");
num_received++;
}
}
}
close(sv[0]);
close(sv[1]);
}
typedef struct {
ssize_t pktsize;
int numpkts;
int so;
} test_pipe_thread_data_t;
static void*
test_pipe_writer(void* args)
{
test_pipe_thread_data_t* td = args;
char sndbuf[td->pktsize];
ssize_t ssize;
int i;
for(i=0; i < td->numpkts; i++) {
memset(sndbuf, i, td->pktsize);
ssize = send(td->so, sndbuf, td->pktsize, MSG_EOR);
if (ssize < 0) {
perror("send");
atf_tc_fail("send returned < 0");
}
ATF_CHECK_EQ_MSG(td->pktsize, ssize,
"expected %zd=send(...) but got %zd",
td->pktsize, ssize);
}
return (0);
}
static void*
test_pipe_reader(void* args)
{
test_pipe_thread_data_t* td = args;
char rcvbuf[td->pktsize];
char comparebuf[td->pktsize];
ssize_t rsize;
int i, d;
for(i=0; i < td->numpkts; i++) {
memset(comparebuf, i, td->pktsize);
rsize = recv(td->so, rcvbuf, td->pktsize, MSG_WAITALL);
if (rsize < 0) {
perror("recv");
atf_tc_fail("recv returned < 0");
}
ATF_CHECK_EQ_MSG(td->pktsize, rsize,
"expected %zd=send(...) but got %zd",
td->pktsize, rsize);
d = memcmp(comparebuf, rcvbuf, td->pktsize);
ATF_CHECK_EQ_MSG(0, d,
"Received data miscompare on packet %d", i);
}
return (0);
}
static void
test_pipe(int sndbufsize, int rcvbufsize)
{
test_pipe_thread_data_t writer_data, reader_data;
pthread_t writer, reader;
int sv[2];
const size_t pktsize = MIN(sndbufsize, rcvbufsize) / 4;
int numpkts;
do_socketpair(sv);
ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize,
sizeof(sndbufsize)));
ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize,
sizeof(rcvbufsize)));
numpkts = MAX(sndbufsize, rcvbufsize) * 8 / pktsize;
writer_data.pktsize = pktsize;
writer_data.numpkts = numpkts;
writer_data.so = sv[0];
reader_data.pktsize = pktsize;
reader_data.numpkts = numpkts;
reader_data.so = sv[1];
ATF_REQUIRE_EQ(0, pthread_create(&writer, NULL, test_pipe_writer,
(void*)&writer_data));
usleep(1000);
ATF_REQUIRE_EQ(0, pthread_create(&reader, NULL, test_pipe_reader,
(void*)&reader_data));
ATF_REQUIRE_EQ(0, pthread_join(writer, NULL));
ATF_REQUIRE_EQ(0, pthread_join(reader, NULL));
close(sv[0]);
close(sv[1]);
}
ATF_TC_WITHOUT_HEAD(create_socket);
ATF_TC_BODY(create_socket, tc)
{
int s;
s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
ATF_REQUIRE(s >= 0);
close(s);
}
ATF_TC_WITHOUT_HEAD(create_socketpair);
ATF_TC_BODY(create_socketpair, tc)
{
int sv[2];
int s;
s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv);
ATF_CHECK_EQ(0, s);
ATF_CHECK(sv[0] >= 0);
ATF_CHECK(sv[1] >= 0);
ATF_CHECK(sv[0] != sv[1]);
close(sv[0]);
close(sv[1]);
}
ATF_TC_WITHOUT_HEAD(listen_unbound);
ATF_TC_BODY(listen_unbound, tc)
{
int s, r;
s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
ATF_REQUIRE(s > 0);
r = listen(s, -1);
ATF_CHECK(r != 0);
close(s);
}
ATF_TC_WITHOUT_HEAD(bind);
ATF_TC_BODY(bind, tc)
{
struct sockaddr_un sun;
const char *path = "sock";
int s, r;
s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
ATF_REQUIRE(s >= 0);
bzero(&sun, sizeof(sun));
sun.sun_family = AF_LOCAL;
sun.sun_len = sizeof(sun);
strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
r = bind(s, (struct sockaddr *)&sun, sizeof(sun));
ATF_CHECK_EQ(0, r);
close(s);
}
ATF_TC_WITHOUT_HEAD(listen_bound);
ATF_TC_BODY(listen_bound, tc)
{
int s;
(void)mk_listening_socket(&s);
close(s);
}
ATF_TC_WITHOUT_HEAD(connect);
ATF_TC_BODY(connect, tc)
{
const struct sockaddr_un *sun;
int s, err, s2;
sun = mk_listening_socket(&s);
s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
ATF_REQUIRE(s2 >= 0);
err = connect(s2, (struct sockaddr *)sun, sizeof(*sun));
if (err != 0) {
perror("connect");
atf_tc_fail("connect(2) failed");
}
close(s);
close(s2);
}
ATF_TC_WITHOUT_HEAD(send_before_accept);
ATF_TC_BODY(send_before_accept, tc)
{
const char buf[] = "hello";
char repl[sizeof(buf)];
const struct sockaddr_un *sun;
int l, s, a;
sun = mk_listening_socket(&l);
ATF_REQUIRE((s = socket(PF_LOCAL, SOCK_SEQPACKET, 0)) > 0);
ATF_REQUIRE(connect(s, (struct sockaddr *)sun, sizeof(*sun)) == 0);
ATF_REQUIRE(send(s, &buf, sizeof(buf), 0) == sizeof(buf));
ATF_REQUIRE((a = accept(l, NULL, NULL)) != 1);
ATF_REQUIRE(recv(a, &repl, sizeof(repl), 0) == sizeof(buf));
ATF_REQUIRE(strcmp(buf, repl) == 0);
close(l);
close(s);
close(a);
}
static bool sigpipe_received = false;
static void
sigpipe_handler(int signo __unused)
{
sigpipe_received = true;
}
ATF_TC_WITHOUT_HEAD(send_to_closed);
ATF_TC_BODY(send_to_closed, tc)
{
struct sigaction sa = {
.sa_handler = sigpipe_handler,
};
const struct sockaddr_un *sun;
int l, s, a;
ATF_REQUIRE(sigemptyset(&sa.sa_mask) == 0);
ATF_REQUIRE(sigaction(SIGPIPE, &sa, NULL) == 0);
sun = mk_listening_socket(&l);
ATF_REQUIRE((s = socket(PF_LOCAL, SOCK_SEQPACKET, 0)) > 0);
ATF_REQUIRE(connect(s, (struct sockaddr *)sun, sizeof(*sun)) == 0);
ATF_REQUIRE((a = accept(l, NULL, NULL)) != 1);
close(a);
ATF_REQUIRE(send(s, &s, sizeof(s), 0) == -1);
ATF_REQUIRE(errno == EPIPE);
ATF_REQUIRE(sigpipe_received == true);
close(s);
ATF_REQUIRE((s = socket(PF_LOCAL, SOCK_SEQPACKET, 0)) > 0);
ATF_REQUIRE(connect(s, (struct sockaddr *)sun, sizeof(*sun)) == 0);
close(l);
sigpipe_received = false;
ATF_REQUIRE(send(s, &s, sizeof(s), 0) == -1);
ATF_REQUIRE(errno == EPIPE);
ATF_REQUIRE(sigpipe_received == true);
close(s);
sa.sa_handler = SIG_DFL;
ATF_REQUIRE(sigaction(SIGPIPE, &sa, NULL) == 0);
}
ATF_TC_WITHOUT_HEAD(implied_connect);
ATF_TC_BODY(implied_connect, tc)
{
const struct sockaddr_un *sun;
int l, s;
sun = mk_listening_socket(&l);
ATF_REQUIRE((s = socket(PF_LOCAL, SOCK_SEQPACKET, 0)) > 0);
ATF_REQUIRE(sendto(s, &s, sizeof(s), 0, (struct sockaddr *)sun,
sizeof(*sun)) == -1);
ATF_REQUIRE(errno == ENOTCONN);
close(l);
close(s);
}
ATF_TC_WITHOUT_HEAD(accept);
ATF_TC_BODY(accept, tc)
{
int sv[2];
mk_pair_of_sockets(sv);
close(sv[0]);
close(sv[1]);
}
ATF_TC_WITHOUT_HEAD(fcntl_nonblock);
ATF_TC_BODY(fcntl_nonblock, tc)
{
int s;
s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
ATF_REQUIRE(s >= 0);
if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) {
perror("fcntl");
atf_tc_fail("fcntl failed");
}
close(s);
}
ATF_TC_WITHOUT_HEAD(resize_buffers);
ATF_TC_BODY(resize_buffers, tc)
{
int s;
int sndbuf = 12345;
int rcvbuf = 23456;
int xs, xr;
socklen_t sl = sizeof(xs);
s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
ATF_REQUIRE(s >= 0);
printf(" Socket Buffer Sizes\n");
printf(" | SNDBUF | RCVBUF |\n");
ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl));
ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl));
printf("Default | %7d | %7d |\n", xs, xr);
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) != 0){
perror("setsockopt");
atf_tc_fail("setsockopt(SO_SNDBUF) failed");
}
ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl));
ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl));
printf("After changing SNDBUF | %7d | %7d |\n", xs, xr);
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) != 0){
perror("setsockopt");
atf_tc_fail("setsockopt(SO_RCVBUF) failed");
}
ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl));
ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl));
printf("After changing RCVBUF | %7d | %7d |\n", xs, xr);
close(s);
}
ATF_TC_WITHOUT_HEAD(resize_connected_buffers);
ATF_TC_BODY(resize_connected_buffers, tc)
{
int sv[2];
int sndbuf = 12345;
int rcvbuf = 23456;
int err;
int ls, lr, rs, rr;
socklen_t sl = sizeof(ls);
do_socketpair(sv);
printf(" Socket Buffer Sizes\n");
printf(" | Left Socket | Right Socket |\n");
printf(" | SNDBUF | RCVBUF | SNDBUF | RCVBUF |\n");
ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &ls, &sl));
ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &lr, &sl));
ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &rs, &sl));
ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl));
printf("Default | %7d | %7d | %7d | %7d |\n",
ls, lr, rs, rr);
err = setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
if (err != 0){
perror("setsockopt");
atf_tc_fail("setsockopt(SO_SNDBUF) failed");
}
ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &ls, &sl));
ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &lr, &sl));
ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &rs, &sl));
ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl));
printf("After changing Left's SNDBUF | %7d | %7d | %7d | %7d |\n",
ls, lr, rs, rr);
err = setsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
if (err != 0){
perror("setsockopt");
atf_tc_fail("setsockopt(SO_RCVBUF) failed");
}
ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &ls, &sl));
ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &lr, &sl));
ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &rs, &sl));
ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl));
printf("After changing Left's RCVBUF | %7d | %7d | %7d | %7d |\n",
ls, lr, rs, rr);
close(sv[0]);
close(sv[1]);
}
ATF_TC_WITHOUT_HEAD(send_recv);
ATF_TC_BODY(send_recv, tc)
{
int sv[2];
const int bufsize = 64;
const char *data = "data";
char recv_buf[bufsize];
ssize_t datalen;
ssize_t ssize, rsize;
do_socketpair(sv);
datalen = strlen(data) + 1;
ssize = send(sv[0], data, datalen, MSG_EOR);
if (ssize < 0) {
perror("send");
atf_tc_fail("send returned < 0");
}
ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd",
datalen, ssize);
rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL);
ATF_CHECK_EQ(datalen, rsize);
close(sv[0]);
close(sv[1]);
}
ATF_TC_WITHOUT_HEAD(sendto_recvfrom);
ATF_TC_BODY(sendto_recvfrom, tc)
{
#ifdef TEST_SEQ_PACKET_SOURCE_ADDRESS
const sockaddr_un *sun;
#endif
struct sockaddr_storage from;
int sv[2];
const int bufsize = 64;
const char *data = "data";
char recv_buf[bufsize];
ssize_t datalen;
ssize_t ssize, rsize;
socklen_t fromlen;
#ifdef TEST_SEQ_PACKET_SOURCE_ADDRESS
sun =
#endif
mk_pair_of_sockets(sv);
datalen = strlen(data) + 1;
ssize = sendto(sv[0], data, datalen, MSG_EOR, NULL, 0);
if (ssize < 0) {
perror("send");
atf_tc_fail("send returned < 0");
}
ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd",
datalen, ssize);
fromlen = sizeof(from);
rsize = recvfrom(sv[1], recv_buf, bufsize, MSG_WAITALL,
(struct sockaddr*)&from, &fromlen);
if (ssize < 0) {
perror("recvfrom");
atf_tc_fail("recvfrom returned < 0");
}
ATF_CHECK_EQ(datalen, rsize);
#ifdef TEST_SEQ_PACKET_SOURCE_ADDRESS
ATF_CHECK_EQ(PF_LOCAL, from.ss_family);
ATF_CHECK_STREQ(sun->sun_path, ((struct sockaddr_un*)&from)->sun_path);
#endif
close(sv[0]);
close(sv[1]);
}
ATF_TC_WITHOUT_HEAD(send_recv_with_connect);
ATF_TC_BODY(send_recv_with_connect, tc)
{
int sv[2];
const int bufsize = 64;
const char *data = "data";
char recv_buf[bufsize];
ssize_t datalen;
ssize_t ssize, rsize;
mk_pair_of_sockets(sv);
datalen = strlen(data) + 1;
ssize = send(sv[0], data, datalen, MSG_EOR);
if (ssize < 0) {
perror("send");
atf_tc_fail("send returned < 0");
}
ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd",
datalen, ssize);
rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL);
ATF_CHECK_EQ(datalen, rsize);
close(sv[0]);
close(sv[1]);
}
ATF_TC_WITHOUT_HEAD(shutdown_send);
ATF_TC_BODY(shutdown_send, tc)
{
const struct sockaddr_un *sun;
const char *data = "data";
ssize_t datalen, ssize;
int s, err, s2;
sun = mk_listening_socket(&s);
s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
ATF_REQUIRE(s2 >= 0);
err = connect(s2, (struct sockaddr *)sun, sizeof(*sun));
if (err != 0) {
perror("connect");
atf_tc_fail("connect(2) failed");
}
ATF_CHECK_EQ(0, shutdown(s2, SHUT_RDWR));
datalen = strlen(data) + 1;
ssize = send(s2, data, datalen, MSG_EOR | MSG_NOSIGNAL);
ATF_CHECK_EQ(EPIPE, errno);
ATF_CHECK_EQ(-1, ssize);
close(s);
close(s2);
}
ATF_TC_WITHOUT_HEAD(shutdown_send_sigpipe);
ATF_TC_BODY(shutdown_send_sigpipe, tc)
{
const struct sockaddr_un *sun;
const char *data = "data";
ssize_t datalen;
int s, err, s2;
sun = mk_listening_socket(&s);
s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
ATF_REQUIRE(s2 >= 0);
err = connect(s2, (struct sockaddr *)sun, sizeof(*sun));
if (err != 0) {
perror("connect");
atf_tc_fail("connect(2) failed");
}
ATF_CHECK_EQ(0, shutdown(s2, SHUT_RDWR));
ATF_REQUIRE(SIG_ERR != signal(SIGPIPE, shutdown_send_sigpipe_handler));
datalen = strlen(data) + 1;
(void)send(s2, data, datalen, MSG_EOR);
ATF_CHECK_EQ(1, got_sigpipe);
close(s);
close(s2);
}
ATF_TC_WITHOUT_HEAD(shutdown_o_async);
ATF_TC_BODY(shutdown_o_async, tc)
{
int sv[2];
do_socketpair(sv);
ATF_CHECK_EQ(0, fcntl(sv[0], F_SETFL, O_ASYNC));
ATF_CHECK_EQ(0, shutdown(sv[0], SHUT_WR));
close(sv[0]);
close(sv[1]);
}
ATF_TC_WITHOUT_HEAD(shutdown_recv);
ATF_TC_BODY(shutdown_recv, tc)
{
char buf[10];
int sv[2];
do_socketpair(sv);
ATF_CHECK_EQ(0, shutdown(sv[0], SHUT_WR));
ATF_CHECK_EQ(0, recv(sv[1], buf, sizeof(buf), 0));
close(sv[0]);
close(sv[1]);
}
ATF_TC_WITHOUT_HEAD(send_recv_nonblocking);
ATF_TC_BODY(send_recv_nonblocking, tc)
{
int sv[2];
const int bufsize = 64;
const char *data = "data";
char recv_buf[bufsize];
ssize_t datalen;
ssize_t ssize, rsize;
do_socketpair_nonblocking(sv);
rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL);
ATF_CHECK_EQ(EAGAIN, errno);
ATF_CHECK_EQ(-1, rsize);
datalen = strlen(data) + 1;
ssize = send(sv[0], data, datalen, MSG_EOR);
if (ssize < 0) {
perror("send");
atf_tc_fail("send returned < 0");
}
ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd",
datalen, ssize);
rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL);
ATF_CHECK_EQ(datalen, rsize);
close(sv[0]);
close(sv[1]);
}
ATF_TC_WITHOUT_HEAD(eagain_8k_8k);
ATF_TC_BODY(eagain_8k_8k, tc)
{
test_eagain(8192, 8192);
}
ATF_TC_WITHOUT_HEAD(eagain_8k_128k);
ATF_TC_BODY(eagain_8k_128k, tc)
{
test_eagain(8192, 131072);
}
ATF_TC_WITHOUT_HEAD(eagain_128k_8k);
ATF_TC_BODY(eagain_128k_8k, tc)
{
test_eagain(131072, 8192);
}
ATF_TC_WITHOUT_HEAD(eagain_128k_128k);
ATF_TC_BODY(eagain_128k_128k, tc)
{
test_eagain(131072, 131072);
}
ATF_TC_WITHOUT_HEAD(rcvbuf_oversized);
ATF_TC_BODY(rcvbuf_oversized, tc)
{
int i;
int sv[2];
const ssize_t pktsize = 1024;
const int sndbufsize = 8192;
const int rcvbufsize = 131072;
const size_t geometric_mean_bufsize = 32768;
const int numpkts = geometric_mean_bufsize / pktsize;
char sndbuf[pktsize];
char recv_buf[pktsize];
ssize_t ssize, rsize;
do_socketpair_nonblocking(sv);
ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize,
sizeof(sndbufsize)));
ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize,
sizeof(rcvbufsize)));
for (i=0; i < numpkts; i++) {
memset(sndbuf, i, pktsize);
ssize = send(sv[0], sndbuf, pktsize, MSG_EOR);
if (ssize < 0) {
perror("send");
atf_tc_fail("send returned < 0");
}
ATF_CHECK_EQ_MSG(pktsize, ssize,
"expected %zd=send(...) but got %zd", pktsize, ssize);
rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL);
if (rsize < 0) {
perror("recv");
atf_tc_fail("recv returned < 0");
}
ATF_CHECK_EQ_MSG(pktsize, rsize,
"expected %zd=send(...) but got %zd", pktsize, rsize);
ATF_CHECK_EQ_MSG(0, memcmp(sndbuf, recv_buf, pktsize),
"Received data miscompare");
}
rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL);
ATF_CHECK_EQ(EAGAIN, errno);
ATF_CHECK_EQ(-1, rsize);
close(sv[0]);
close(sv[1]);
}
ATF_TC_WITHOUT_HEAD(pipe_simulator_8k_8k);
ATF_TC_BODY(pipe_simulator_8k_8k, tc)
{
test_pipe_simulator(8192, 8192);
}
ATF_TC_WITHOUT_HEAD(pipe_simulator_8k_128k);
ATF_TC_BODY(pipe_simulator_8k_128k, tc)
{
test_pipe_simulator(8192, 131072);
}
ATF_TC_WITHOUT_HEAD(pipe_simulator_128k_8k);
ATF_TC_BODY(pipe_simulator_128k_8k, tc)
{
test_pipe_simulator(131072, 8192);
}
ATF_TC_WITHOUT_HEAD(pipe_simulator_128k_128k);
ATF_TC_BODY(pipe_simulator_128k_128k, tc)
{
test_pipe_simulator(131072, 131072);
}
ATF_TC_WITHOUT_HEAD(pipe_8k_8k);
ATF_TC_BODY(pipe_8k_8k, tc)
{
test_pipe(8192, 8192);
}
ATF_TC_WITHOUT_HEAD(pipe_8k_128k);
ATF_TC_BODY(pipe_8k_128k, tc)
{
test_pipe(8192, 131072);
}
ATF_TC_WITHOUT_HEAD(pipe_128k_8k);
ATF_TC_BODY(pipe_128k_8k, tc)
{
test_pipe(131072, 8192);
}
ATF_TC_WITHOUT_HEAD(pipe_128k_128k);
ATF_TC_BODY(pipe_128k_128k, tc)
{
test_pipe(131072, 131072);
}
ATF_TC_WITHOUT_HEAD(sendrecv_8k);
ATF_TC_BODY(sendrecv_8k, tc)
{
test_sendrecv_symmetric_buffers(8 * 1024, true);
}
ATF_TC_WITHOUT_HEAD(sendrecv_16k);
ATF_TC_BODY(sendrecv_16k, tc)
{
test_sendrecv_symmetric_buffers(16 * 1024, true);
}
ATF_TC_WITHOUT_HEAD(sendrecv_32k);
ATF_TC_BODY(sendrecv_32k, tc)
{
test_sendrecv_symmetric_buffers(32 * 1024, true);
}
ATF_TC_WITHOUT_HEAD(sendrecv_64k);
ATF_TC_BODY(sendrecv_64k, tc)
{
test_sendrecv_symmetric_buffers(64 * 1024, true);
}
ATF_TC_WITHOUT_HEAD(sendrecv_128k);
ATF_TC_BODY(sendrecv_128k, tc)
{
test_sendrecv_symmetric_buffers(128 * 1024, true);
}
ATF_TC_WITHOUT_HEAD(sendrecv_8k_nonblocking);
ATF_TC_BODY(sendrecv_8k_nonblocking, tc)
{
test_sendrecv_symmetric_buffers(8 * 1024, false);
}
ATF_TC_WITHOUT_HEAD(sendrecv_16k_nonblocking);
ATF_TC_BODY(sendrecv_16k_nonblocking, tc)
{
test_sendrecv_symmetric_buffers(16 * 1024, false);
}
ATF_TC_WITHOUT_HEAD(sendrecv_32k_nonblocking);
ATF_TC_BODY(sendrecv_32k_nonblocking, tc)
{
test_sendrecv_symmetric_buffers(32 * 1024, false);
}
ATF_TC_WITHOUT_HEAD(sendrecv_64k_nonblocking);
ATF_TC_BODY(sendrecv_64k_nonblocking, tc)
{
test_sendrecv_symmetric_buffers(64 * 1024, false);
}
ATF_TC_WITHOUT_HEAD(sendrecv_128k_nonblocking);
ATF_TC_BODY(sendrecv_128k_nonblocking, tc)
{
test_sendrecv_symmetric_buffers(128 * 1024, false);
}
ATF_TC(random_eor_and_waitall);
ATF_TC_HEAD(random_eor_and_waitall, tc)
{
atf_tc_set_md_var(tc, "descr", "Test random sized send/recv with "
"randomly placed MSG_EOR and randomly applied MSG_WAITALL on "
"PF_UNIX/SOCK_SEQPACKET");
}
struct random_eor_params {
u_long recvspace;
char *sendbuf;
size_t *records;
u_int nrecords;
int sock;
u_short seed[6];
};
#define RANDOM_TESTSIZE ((size_t)100 * 1024 * 1024)
#define RANDOM_MAXRECORD 10
#define RANDOM_SENDSIZE 2
#define RANDOM_RECVSIZE 4
static void *
sending_thread(void *arg)
{
struct random_eor_params *params = arg;
size_t off = 0;
int eor = 0;
while (off < RANDOM_TESTSIZE) {
ssize_t len;
int flags;
len = nrand48(¶ms->seed[3]) %
(RANDOM_SENDSIZE * params->recvspace);
if (off + len >= params->records[eor]) {
len = params->records[eor] - off;
flags = MSG_EOR;
eor++;
} else
flags = 0;
ATF_REQUIRE(send(params->sock, ¶ms->sendbuf[off], len,
flags) == len);
off += len;
#ifdef DEBUG
printf("send %zd%s\n", off, flags ? " EOR" : "");
#endif
}
return (NULL);
}
ATF_TC_BODY(random_eor_and_waitall, tc)
{
struct random_eor_params params;
void *recvbuf;
pthread_t t;
size_t off;
int fd[2], eor;
arc4random_buf(params.seed, sizeof(params.seed));
printf("Using seed:");
for (u_int i = 0; i < (u_int)sizeof(params.seed)/sizeof(u_short); i++)
printf(" 0x%.4x,", params.seed[i]);
printf("\n");
ATF_REQUIRE((params.sendbuf = malloc(RANDOM_TESTSIZE)) != NULL);
for (u_int i = 0; i < RANDOM_TESTSIZE / (u_int )sizeof(long); i++)
((long *)params.sendbuf)[i] = nrand48(¶ms.seed[0]);
ATF_REQUIRE(sysctlbyname("net.local.seqpacket.recvspace",
¶ms.recvspace, &(size_t){sizeof(u_long)}, NULL, 0) != -1);
ATF_REQUIRE((recvbuf =
malloc(RANDOM_RECVSIZE * params.recvspace)) != NULL);
params.nrecords = 2 * RANDOM_TESTSIZE /
(RANDOM_MAXRECORD * params.recvspace);
ATF_REQUIRE((params.records =
malloc(params.nrecords * sizeof(size_t *))) != NULL);
off = 0;
for (u_int i = 0; i < params.nrecords; i++) {
off += 1 + nrand48(¶ms.seed[0]) %
(RANDOM_MAXRECORD * params.recvspace);
if (off > RANDOM_TESTSIZE) {
params.nrecords = i;
break;
}
params.records[i] = off;
}
params.records[params.nrecords - 1] = RANDOM_TESTSIZE;
ATF_REQUIRE(socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, fd) == 0);
params.sock = fd[0];
ATF_REQUIRE(pthread_create(&t, NULL, sending_thread, ¶ms) == 0);
off = 0;
eor = 0;
while (off < RANDOM_TESTSIZE) {
struct iovec iov = {
.iov_base = recvbuf,
.iov_len = nrand48(¶ms.seed[0]) %
(RANDOM_RECVSIZE * params.recvspace)
};
struct msghdr hdr = {
.msg_iov = &iov,
.msg_iovlen = 1,
};
size_t len;
int waitall = iov.iov_len & 0x1 ? MSG_WAITALL : 0;
len = recvmsg(fd[1], &hdr, waitall);
if (waitall && !(hdr.msg_flags & MSG_EOR))
ATF_CHECK_EQ_MSG(len, iov.iov_len,
"recvmsg(MSG_WAITALL): %zd, expected %zd",
len, iov.iov_len);
if (off + len == params.records[eor]) {
ATF_REQUIRE_MSG(hdr.msg_flags & MSG_EOR,
"recvmsg(): expected EOR @ %zd", off + len);
eor++;
} else {
ATF_REQUIRE_MSG(off + len < params.records[eor],
"recvmsg() past EOR: %zd, expected %zd",
off + len, params.records[eor]);
ATF_REQUIRE_MSG(!(hdr.msg_flags & MSG_EOR),
"recvmsg() spurious EOR at %zd, expected %zd",
off + len, params.records[eor]);
}
ATF_REQUIRE_MSG(0 == memcmp(params.sendbuf + off, recvbuf, len),
"data corruption past %zd", off);
off += len;
#ifdef DEBUG
printf("recv %zd%s %zd/%zd%s\n", off,
(hdr.msg_flags & MSG_EOR) ? " EOR" : "",
len, iov.iov_len,
waitall ? " WAITALL" : "");
#endif
}
ATF_REQUIRE(pthread_join(t, NULL) == 0);
free(params.sendbuf);
free(recvbuf);
free(params.records);
}
#define PEEK_RACE_SIZE 10
#define PEEK_RACE_TRIES 10000
static void *
peek_race_writer(void *args)
{
struct timespec ts = {};
u_short seed[3];
char buf[PEEK_RACE_SIZE];
int fd = *(int *)args;
arc4random_buf(seed, sizeof(seed));
for (u_int i = 0; i < PEEK_RACE_TRIES; i++) {
ATF_REQUIRE_EQ(PEEK_RACE_SIZE,
send(fd, buf, sizeof(buf), MSG_EOR));
ts.tv_nsec = nrand48(seed) % 20;
(void)clock_nanosleep(CLOCK_MONOTONIC_FAST, 0, &ts, NULL);
}
return (NULL);
}
static void *
peek_race_peeker(void *args)
{
char buf[PEEK_RACE_SIZE * 10];
int fd = *(int *)args;
for (u_int i = 0; i < PEEK_RACE_TRIES; i++) {
ssize_t rcvd;
while ((rcvd = recv(fd, buf, sizeof(buf),
MSG_PEEK | MSG_DONTWAIT)) == -1)
ATF_REQUIRE(errno == EAGAIN);
ATF_REQUIRE(rcvd == PEEK_RACE_SIZE);
ATF_REQUIRE_EQ(PEEK_RACE_SIZE,
recv(fd, buf, sizeof(buf), 0));
}
return (NULL);
}
ATF_TC_WITHOUT_HEAD(peek_race);
ATF_TC_BODY(peek_race, tc)
{
pthread_t peeker, writer;
int sv[2];
do_socketpair(sv);
ATF_REQUIRE_EQ(0, pthread_create(&writer, NULL, peek_race_writer,
&sv[0]));
ATF_REQUIRE_EQ(0, pthread_create(&peeker, NULL, peek_race_peeker,
&sv[1]));
ATF_REQUIRE_EQ(0, pthread_join(writer, NULL));
ATF_REQUIRE_EQ(0, pthread_join(peeker, NULL));
close(sv[0]);
close(sv[1]);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, create_socket);
ATF_TP_ADD_TC(tp, create_socketpair);
ATF_TP_ADD_TC(tp, listen_unbound);
ATF_TP_ADD_TC(tp, bind);
ATF_TP_ADD_TC(tp, listen_bound);
ATF_TP_ADD_TC(tp, connect);
ATF_TP_ADD_TC(tp, accept);
ATF_TP_ADD_TC(tp, fcntl_nonblock);
ATF_TP_ADD_TC(tp, resize_buffers);
ATF_TP_ADD_TC(tp, resize_connected_buffers);
ATF_TP_ADD_TC(tp, send_recv);
ATF_TP_ADD_TC(tp, send_recv_nonblocking);
ATF_TP_ADD_TC(tp, send_recv_with_connect);
ATF_TP_ADD_TC(tp, sendto_recvfrom);
ATF_TP_ADD_TC(tp, send_before_accept);
ATF_TP_ADD_TC(tp, send_to_closed);
ATF_TP_ADD_TC(tp, implied_connect);
ATF_TP_ADD_TC(tp, shutdown_send);
ATF_TP_ADD_TC(tp, shutdown_send_sigpipe);
ATF_TP_ADD_TC(tp, shutdown_o_async);
ATF_TP_ADD_TC(tp, shutdown_recv);
ATF_TP_ADD_TC(tp, eagain_8k_8k);
ATF_TP_ADD_TC(tp, eagain_8k_128k);
ATF_TP_ADD_TC(tp, eagain_128k_8k);
ATF_TP_ADD_TC(tp, eagain_128k_128k);
ATF_TP_ADD_TC(tp, sendrecv_8k);
ATF_TP_ADD_TC(tp, sendrecv_16k);
ATF_TP_ADD_TC(tp, sendrecv_32k);
ATF_TP_ADD_TC(tp, sendrecv_64k);
ATF_TP_ADD_TC(tp, sendrecv_128k);
ATF_TP_ADD_TC(tp, sendrecv_8k_nonblocking);
ATF_TP_ADD_TC(tp, sendrecv_16k_nonblocking);
ATF_TP_ADD_TC(tp, sendrecv_32k_nonblocking);
ATF_TP_ADD_TC(tp, sendrecv_64k_nonblocking);
ATF_TP_ADD_TC(tp, sendrecv_128k_nonblocking);
ATF_TP_ADD_TC(tp, rcvbuf_oversized);
ATF_TP_ADD_TC(tp, pipe_simulator_8k_8k);
ATF_TP_ADD_TC(tp, pipe_simulator_8k_128k);
ATF_TP_ADD_TC(tp, pipe_simulator_128k_8k);
ATF_TP_ADD_TC(tp, pipe_simulator_128k_128k);
ATF_TP_ADD_TC(tp, pipe_8k_8k);
ATF_TP_ADD_TC(tp, pipe_8k_128k);
ATF_TP_ADD_TC(tp, pipe_128k_8k);
ATF_TP_ADD_TC(tp, pipe_128k_128k);
ATF_TP_ADD_TC(tp, random_eor_and_waitall);
ATF_TP_ADD_TC(tp, peek_race);
return atf_no_error();
}