#define _GNU_SOURCE
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "../../kselftest_harness.h"
#define SOCK_PATH "/tmp/af_unix_connreset.sock"
static void remove_socket_file(void)
{
unlink(SOCK_PATH);
}
FIXTURE(unix_sock)
{
int server;
int client;
int child;
};
FIXTURE_VARIANT(unix_sock)
{
int socket_type;
const char *name;
};
FIXTURE_VARIANT_ADD(unix_sock, stream) {
.socket_type = SOCK_STREAM,
.name = "SOCK_STREAM",
};
FIXTURE_VARIANT_ADD(unix_sock, dgram) {
.socket_type = SOCK_DGRAM,
.name = "SOCK_DGRAM",
};
FIXTURE_VARIANT_ADD(unix_sock, seqpacket) {
.socket_type = SOCK_SEQPACKET,
.name = "SOCK_SEQPACKET",
};
FIXTURE_SETUP(unix_sock)
{
struct sockaddr_un addr = {};
int err;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, SOCK_PATH);
remove_socket_file();
self->server = socket(AF_UNIX, variant->socket_type, 0);
ASSERT_LT(-1, self->server);
err = bind(self->server, (struct sockaddr *)&addr, sizeof(addr));
ASSERT_EQ(0, err);
if (variant->socket_type == SOCK_STREAM ||
variant->socket_type == SOCK_SEQPACKET) {
err = listen(self->server, 1);
ASSERT_EQ(0, err);
}
self->client = socket(AF_UNIX, variant->socket_type | SOCK_NONBLOCK, 0);
ASSERT_LT(-1, self->client);
err = connect(self->client, (struct sockaddr *)&addr, sizeof(addr));
ASSERT_EQ(0, err);
}
FIXTURE_TEARDOWN(unix_sock)
{
if (variant->socket_type == SOCK_STREAM ||
variant->socket_type == SOCK_SEQPACKET)
close(self->child);
close(self->client);
close(self->server);
remove_socket_file();
}
TEST_F(unix_sock, eof)
{
char buf[16] = {};
ssize_t n;
if (variant->socket_type == SOCK_STREAM ||
variant->socket_type == SOCK_SEQPACKET) {
self->child = accept(self->server, NULL, NULL);
ASSERT_LT(-1, self->child);
close(self->child);
} else {
close(self->server);
}
n = recv(self->client, buf, sizeof(buf), 0);
if (variant->socket_type == SOCK_STREAM ||
variant->socket_type == SOCK_SEQPACKET) {
ASSERT_EQ(0, n);
} else {
ASSERT_EQ(-1, n);
ASSERT_EQ(EAGAIN, errno);
}
}
TEST_F(unix_sock, reset_unread_behavior)
{
char buf[16] = {};
ssize_t n;
send(self->client, "hello", 5, 0);
if (variant->socket_type == SOCK_DGRAM) {
close(self->server);
} else {
self->child = accept(self->server, NULL, NULL);
ASSERT_LT(-1, self->child);
close(self->child);
}
n = recv(self->client, buf, sizeof(buf), 0);
ASSERT_EQ(-1, n);
if (variant->socket_type == SOCK_STREAM ||
variant->socket_type == SOCK_SEQPACKET) {
ASSERT_EQ(ECONNRESET, errno);
} else {
ASSERT_EQ(EAGAIN, errno);
}
}
TEST_F(unix_sock, reset_closed_embryo)
{
char buf[16] = {};
ssize_t n;
if (variant->socket_type == SOCK_DGRAM) {
snprintf(_metadata->results->reason,
sizeof(_metadata->results->reason),
"Test only applies to SOCK_STREAM and SOCK_SEQPACKET");
exit(KSFT_XFAIL);
}
close(self->server);
n = recv(self->client, buf, sizeof(buf), 0);
ASSERT_EQ(-1, n);
ASSERT_EQ(ECONNRESET, errno);
}
TEST_HARNESS_MAIN