xref: /linux/tools/testing/selftests/net/af_unix/unix_connreset.c (revision 84318277d6334c6981ab326d4acc87c6a6ddc9b8)
145a1cd83SSunday Adelodun // SPDX-License-Identifier: GPL-2.0
245a1cd83SSunday Adelodun /*
345a1cd83SSunday Adelodun  * Selftest for AF_UNIX socket close and ECONNRESET behaviour.
445a1cd83SSunday Adelodun  *
545a1cd83SSunday Adelodun  * This test verifies:
645a1cd83SSunday Adelodun  *  1. SOCK_STREAM returns EOF when the peer closes normally.
745a1cd83SSunday Adelodun  *  2. SOCK_STREAM returns ECONNRESET if peer closes with unread data.
845a1cd83SSunday Adelodun  *  3. SOCK_SEQPACKET returns EOF when the peer closes normally.
945a1cd83SSunday Adelodun  *  4. SOCK_SEQPACKET returns ECONNRESET if the peer closes with unread data.
1045a1cd83SSunday Adelodun  *  5. SOCK_DGRAM does not return ECONNRESET when the peer closes.
1145a1cd83SSunday Adelodun  *
1245a1cd83SSunday Adelodun  * These tests document the intended Linux behaviour.
1345a1cd83SSunday Adelodun  *
1445a1cd83SSunday Adelodun  */
1545a1cd83SSunday Adelodun 
1645a1cd83SSunday Adelodun #define _GNU_SOURCE
1745a1cd83SSunday Adelodun #include <string.h>
1845a1cd83SSunday Adelodun #include <fcntl.h>
1945a1cd83SSunday Adelodun #include <unistd.h>
2045a1cd83SSunday Adelodun #include <errno.h>
2145a1cd83SSunday Adelodun #include <sys/socket.h>
2245a1cd83SSunday Adelodun #include <sys/un.h>
2345a1cd83SSunday Adelodun #include "../../kselftest_harness.h"
2445a1cd83SSunday Adelodun 
2545a1cd83SSunday Adelodun #define SOCK_PATH "/tmp/af_unix_connreset.sock"
2645a1cd83SSunday Adelodun 
2745a1cd83SSunday Adelodun static void remove_socket_file(void)
2845a1cd83SSunday Adelodun {
2945a1cd83SSunday Adelodun 	unlink(SOCK_PATH);
3045a1cd83SSunday Adelodun }
3145a1cd83SSunday Adelodun 
3245a1cd83SSunday Adelodun FIXTURE(unix_sock)
3345a1cd83SSunday Adelodun {
3445a1cd83SSunday Adelodun 	int server;
3545a1cd83SSunday Adelodun 	int client;
3645a1cd83SSunday Adelodun 	int child;
3745a1cd83SSunday Adelodun };
3845a1cd83SSunday Adelodun 
3945a1cd83SSunday Adelodun FIXTURE_VARIANT(unix_sock)
4045a1cd83SSunday Adelodun {
4145a1cd83SSunday Adelodun 	int socket_type;
4245a1cd83SSunday Adelodun 	const char *name;
4345a1cd83SSunday Adelodun };
4445a1cd83SSunday Adelodun 
4545a1cd83SSunday Adelodun FIXTURE_VARIANT_ADD(unix_sock, stream) {
4645a1cd83SSunday Adelodun 	.socket_type = SOCK_STREAM,
4745a1cd83SSunday Adelodun 	.name = "SOCK_STREAM",
4845a1cd83SSunday Adelodun };
4945a1cd83SSunday Adelodun 
5045a1cd83SSunday Adelodun FIXTURE_VARIANT_ADD(unix_sock, dgram) {
5145a1cd83SSunday Adelodun 	.socket_type = SOCK_DGRAM,
5245a1cd83SSunday Adelodun 	.name = "SOCK_DGRAM",
5345a1cd83SSunday Adelodun };
5445a1cd83SSunday Adelodun 
5545a1cd83SSunday Adelodun FIXTURE_VARIANT_ADD(unix_sock, seqpacket) {
5645a1cd83SSunday Adelodun 	.socket_type = SOCK_SEQPACKET,
5745a1cd83SSunday Adelodun 	.name = "SOCK_SEQPACKET",
5845a1cd83SSunday Adelodun };
5945a1cd83SSunday Adelodun 
6045a1cd83SSunday Adelodun FIXTURE_SETUP(unix_sock)
6145a1cd83SSunday Adelodun {
6245a1cd83SSunday Adelodun 	struct sockaddr_un addr = {};
6345a1cd83SSunday Adelodun 	int err;
6445a1cd83SSunday Adelodun 
6545a1cd83SSunday Adelodun 	addr.sun_family = AF_UNIX;
6645a1cd83SSunday Adelodun 	strcpy(addr.sun_path, SOCK_PATH);
6745a1cd83SSunday Adelodun 	remove_socket_file();
6845a1cd83SSunday Adelodun 
6945a1cd83SSunday Adelodun 	self->server = socket(AF_UNIX, variant->socket_type, 0);
7045a1cd83SSunday Adelodun 	ASSERT_LT(-1, self->server);
7145a1cd83SSunday Adelodun 
7245a1cd83SSunday Adelodun 	err = bind(self->server, (struct sockaddr *)&addr, sizeof(addr));
7345a1cd83SSunday Adelodun 	ASSERT_EQ(0, err);
7445a1cd83SSunday Adelodun 
7545a1cd83SSunday Adelodun 	if (variant->socket_type == SOCK_STREAM ||
7645a1cd83SSunday Adelodun 	    variant->socket_type == SOCK_SEQPACKET) {
7745a1cd83SSunday Adelodun 		err = listen(self->server, 1);
7845a1cd83SSunday Adelodun 		ASSERT_EQ(0, err);
7945a1cd83SSunday Adelodun 	}
8045a1cd83SSunday Adelodun 
8145a1cd83SSunday Adelodun 	self->client = socket(AF_UNIX, variant->socket_type | SOCK_NONBLOCK, 0);
8245a1cd83SSunday Adelodun 	ASSERT_LT(-1, self->client);
8345a1cd83SSunday Adelodun 
8445a1cd83SSunday Adelodun 	err = connect(self->client, (struct sockaddr *)&addr, sizeof(addr));
8545a1cd83SSunday Adelodun 	ASSERT_EQ(0, err);
8645a1cd83SSunday Adelodun }
8745a1cd83SSunday Adelodun 
8845a1cd83SSunday Adelodun FIXTURE_TEARDOWN(unix_sock)
8945a1cd83SSunday Adelodun {
9045a1cd83SSunday Adelodun 	if (variant->socket_type == SOCK_STREAM ||
9145a1cd83SSunday Adelodun 	    variant->socket_type == SOCK_SEQPACKET)
9245a1cd83SSunday Adelodun 		close(self->child);
9345a1cd83SSunday Adelodun 
9445a1cd83SSunday Adelodun 	close(self->client);
9545a1cd83SSunday Adelodun 	close(self->server);
9645a1cd83SSunday Adelodun 	remove_socket_file();
9745a1cd83SSunday Adelodun }
9845a1cd83SSunday Adelodun 
9945a1cd83SSunday Adelodun /* Test 1: peer closes normally */
10045a1cd83SSunday Adelodun TEST_F(unix_sock, eof)
10145a1cd83SSunday Adelodun {
10245a1cd83SSunday Adelodun 	char buf[16] = {};
10345a1cd83SSunday Adelodun 	ssize_t n;
10445a1cd83SSunday Adelodun 
10545a1cd83SSunday Adelodun 	if (variant->socket_type == SOCK_STREAM ||
10645a1cd83SSunday Adelodun 	    variant->socket_type == SOCK_SEQPACKET) {
10745a1cd83SSunday Adelodun 		self->child = accept(self->server, NULL, NULL);
10845a1cd83SSunday Adelodun 		ASSERT_LT(-1, self->child);
10945a1cd83SSunday Adelodun 
11045a1cd83SSunday Adelodun 		close(self->child);
11145a1cd83SSunday Adelodun 	} else {
11245a1cd83SSunday Adelodun 		close(self->server);
11345a1cd83SSunday Adelodun 	}
11445a1cd83SSunday Adelodun 
11545a1cd83SSunday Adelodun 	n = recv(self->client, buf, sizeof(buf), 0);
11645a1cd83SSunday Adelodun 
11745a1cd83SSunday Adelodun 	if (variant->socket_type == SOCK_STREAM ||
11845a1cd83SSunday Adelodun 	    variant->socket_type == SOCK_SEQPACKET) {
11945a1cd83SSunday Adelodun 		ASSERT_EQ(0, n);
12045a1cd83SSunday Adelodun 	} else {
12145a1cd83SSunday Adelodun 		ASSERT_EQ(-1, n);
12245a1cd83SSunday Adelodun 		ASSERT_EQ(EAGAIN, errno);
12345a1cd83SSunday Adelodun 	}
12445a1cd83SSunday Adelodun }
12545a1cd83SSunday Adelodun 
12645a1cd83SSunday Adelodun /* Test 2: peer closes with unread data */
12745a1cd83SSunday Adelodun TEST_F(unix_sock, reset_unread_behavior)
12845a1cd83SSunday Adelodun {
12945a1cd83SSunday Adelodun 	char buf[16] = {};
13045a1cd83SSunday Adelodun 	ssize_t n;
13145a1cd83SSunday Adelodun 
13245a1cd83SSunday Adelodun 	/* Send data that will remain unread */
13345a1cd83SSunday Adelodun 	send(self->client, "hello", 5, 0);
13445a1cd83SSunday Adelodun 
13545a1cd83SSunday Adelodun 	if (variant->socket_type == SOCK_DGRAM) {
13645a1cd83SSunday Adelodun 		/* No real connection, just close the server */
13745a1cd83SSunday Adelodun 		close(self->server);
13845a1cd83SSunday Adelodun 	} else {
13945a1cd83SSunday Adelodun 		self->child = accept(self->server, NULL, NULL);
14045a1cd83SSunday Adelodun 		ASSERT_LT(-1, self->child);
14145a1cd83SSunday Adelodun 
14245a1cd83SSunday Adelodun 		/* Peer closes before client reads */
14345a1cd83SSunday Adelodun 		close(self->child);
14445a1cd83SSunday Adelodun 	}
14545a1cd83SSunday Adelodun 
14645a1cd83SSunday Adelodun 	n = recv(self->client, buf, sizeof(buf), 0);
14745a1cd83SSunday Adelodun 	ASSERT_EQ(-1, n);
14845a1cd83SSunday Adelodun 
14945a1cd83SSunday Adelodun 	if (variant->socket_type == SOCK_STREAM ||
15045a1cd83SSunday Adelodun 	    variant->socket_type == SOCK_SEQPACKET) {
15145a1cd83SSunday Adelodun 		ASSERT_EQ(ECONNRESET, errno);
15245a1cd83SSunday Adelodun 	} else {
15345a1cd83SSunday Adelodun 		ASSERT_EQ(EAGAIN, errno);
15445a1cd83SSunday Adelodun 	}
15545a1cd83SSunday Adelodun }
15645a1cd83SSunday Adelodun 
15745a1cd83SSunday Adelodun /* Test 3: closing unaccepted (embryo) server socket should reset client. */
15845a1cd83SSunday Adelodun TEST_F(unix_sock, reset_closed_embryo)
15945a1cd83SSunday Adelodun {
16045a1cd83SSunday Adelodun 	char buf[16] = {};
16145a1cd83SSunday Adelodun 	ssize_t n;
16245a1cd83SSunday Adelodun 
163*e254c212SJakub Kicinski 	if (variant->socket_type == SOCK_DGRAM) {
164*e254c212SJakub Kicinski 		snprintf(_metadata->results->reason,
165*e254c212SJakub Kicinski 			 sizeof(_metadata->results->reason),
166*e254c212SJakub Kicinski 			 "Test only applies to SOCK_STREAM and SOCK_SEQPACKET");
167*e254c212SJakub Kicinski 		exit(KSFT_XFAIL);
168*e254c212SJakub Kicinski 	}
16945a1cd83SSunday Adelodun 
17045a1cd83SSunday Adelodun 	/* Close server without accept()ing */
17145a1cd83SSunday Adelodun 	close(self->server);
17245a1cd83SSunday Adelodun 
17345a1cd83SSunday Adelodun 	n = recv(self->client, buf, sizeof(buf), 0);
17445a1cd83SSunday Adelodun 
17545a1cd83SSunday Adelodun 	ASSERT_EQ(-1, n);
17645a1cd83SSunday Adelodun 	ASSERT_EQ(ECONNRESET, errno);
17745a1cd83SSunday Adelodun }
17845a1cd83SSunday Adelodun 
17945a1cd83SSunday Adelodun TEST_HARNESS_MAIN
18045a1cd83SSunday Adelodun 
181