phunix/minix/tests/test90.c
David van Moolenbroek baa5830f28 tests: add advanced UDS tests (test90)
Change-Id: Ib4b81d441368fd9b7e7c7b9cab802bb01fa04065
2017-03-09 23:39:56 +00:00

4186 lines
99 KiB
C

/* Advanced tests for UNIX Domain Sockets - by D.C. van Moolenbroek */
/*
* This is a somewhat random collection of in-depth tests, complementing the
* more general functionality tests in test56. The overall test set is still
* by no means expected to be "complete." The subtests are in random order.
*/
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>
#include "common.h"
#include "socklib.h"
#define ITERATIONS 1
#define SOCK_PATH_A "sock_a"
#define SOCK_PATH_B "sock_b"
#define SOCK_PATH_C "sock_c"
#define SOCK_PATH_D "sock_d"
#define SOCK_PATH_A_X ".//sock_a"
#define SOCK_PATH_A_Y "./././sock_a"
#define PRINT_STATS 0
/*
* Check that the given returned socket address matches the given path. A NULL
* path may be passed in to indicate the result should be for an unbound
* socket.
*/
static void
check_addr(struct sockaddr_un * sun, socklen_t len, const char * path)
{
if (len < offsetof(struct sockaddr_un, sun_path)) e(0);
if (sun->sun_family != AF_UNIX) e(0);
if (sun->sun_len != len - ((path != NULL) ? 1 : 0)) e(0);
len -= offsetof(struct sockaddr_un, sun_path);
if (path != NULL) {
if (len != strlen(path) + 1) e(0);
if (sun->sun_path[len - 1] != '\0') e(0);
if (strcmp(sun->sun_path, path)) e(0);
} else
if (len != 0) e(0);
}
/*
* Get a socket of the given type, bound to the given path. Return the file
* descriptor, as well as the bound addres in 'sun'.
*/
static int
get_bound_socket(int type, const char * path, struct sockaddr_un * sun)
{
int fd;
if ((fd = socket(AF_UNIX, type, 0)) < 0) e(0);
(void)unlink(path);
memset(sun, 0, sizeof(*sun));
sun->sun_family = AF_UNIX;
strlcpy(sun->sun_path, path, sizeof(sun->sun_path));
if (bind(fd, (struct sockaddr *)sun, sizeof(*sun)) != 0) e(0);
return fd;
}
/*
* Get a pair of connected sockets.
*/
static void
get_socket_pair(int type, int fd[2])
{
struct sockaddr_un sunA, sunB;
if ((type & ~SOCK_FLAGS_MASK) == SOCK_DGRAM) {
fd[0] = get_bound_socket(type, SOCK_PATH_A, &sunA);
fd[1] = get_bound_socket(type, SOCK_PATH_B, &sunB);
if (connect(fd[0], (struct sockaddr *)&sunB,
sizeof(sunB)) != 0) e(0);
if (connect(fd[1], (struct sockaddr *)&sunA,
sizeof(sunA)) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
if (unlink(SOCK_PATH_B) != 0) e(0);
} else
if (socketpair(AF_UNIX, type, 0, fd) != 0) e(0);
}
/*
* Return the receive buffer size of the given socket.
*/
static int
get_rcvbuf_len(int fd)
{
socklen_t len;
int val;
len = sizeof(val);
if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, &len) != 0) e(0);
if (len != sizeof(val)) e(0);
if (val <= 0) e(0);
return val;
}
static const enum state unix_connect_states[] = {
S_NEW, S_N_SHUT_R, S_N_SHUT_W, S_N_SHUT_RW,
S_BOUND, S_LISTENING, S_L_SHUT_R, S_L_SHUT_W,
S_L_SHUT_RW, S_CONNECTING, S_CONNECTED, S_ACCEPTED,
S_SHUT_R, S_SHUT_W, S_SHUT_RW, S_RSHUT_R,
S_RSHUT_W, S_RSHUT_RW, S_SHUT2_R, S_SHUT2_W,
S_SHUT2_RW, S_PRE_EOF, S_AT_EOF, S_POST_EOF,
S_PRE_SHUT_R, S_EOF_SHUT_R, S_POST_SHUT_R, S_PRE_SHUT_W,
S_EOF_SHUT_W, S_POST_SHUT_W, S_PRE_SHUT_RW, S_EOF_SHUT_RW,
S_POST_SHUT_RW, S_AT_RESET, S_POST_RESET, S_POST_FAILED
/*
* It is impossible to generate the S_PRE_RESET state: we can
* only generate a reset on a connected socket for which the
* other end is pending acceptance, by closing the listening
* socket. That means we cannot send data to the connected end
* (from the listening socket) before triggering the reset.
*
* It is impossible to generate the S_FAILED state: even a non-
* blocking connect will always fail immediately when it cannot
* connect to the target.
*/
};
static const int unix_connect_results[][__arraycount(unix_connect_states)] = {
[C_ACCEPT] = {
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EAGAIN, -ECONNABORTED, -ECONNABORTED,
-ECONNABORTED, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
},
[C_BIND] = {
0, 0, 0, 0,
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, 0, 0, -EINVAL,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_CONNECT] = {
0, 0, 0, 0,
0, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP, -EALREADY, -EISCONN, -EISCONN,
-EISCONN, -EISCONN, -EISCONN, -EISCONN,
-EISCONN, -EISCONN, -EISCONN, -EISCONN,
-EISCONN, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_GETPEERNAME] = {
-ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN,
-ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN,
-ENOTCONN, -ENOTCONN, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, -ENOTCONN, -ENOTCONN, -ENOTCONN,
-ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN,
-ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN,
-ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN,
},
[C_GETSOCKNAME] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_GETSOCKOPT_ERR] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, -ECONNRESET, 0, 0,
},
[C_GETSOCKOPT_KA] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_GETSOCKOPT_RB] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_IOCTL_NREAD] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 1, 0, 0,
0, 0, 0, 1,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_LISTEN] = {
-EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ,
0, 0, 0, 0,
0, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ,
-EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ,
-EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ,
-EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ,
},
[C_RECV] = {
-ENOTCONN, 0, -ENOTCONN, 0,
-ENOTCONN, -ENOTCONN, 0, -ENOTCONN,
0, -EAGAIN, -EAGAIN, -EAGAIN,
0, -EAGAIN, 0, -EAGAIN,
0, 0, 0, 0,
0, 1, 0, 0,
0, 0, 0, 1,
0, 0, 0, 0,
0, -ECONNRESET, 0, -ENOTCONN,
},
[C_RECVFROM] = {
-ENOTCONN, 0, -ENOTCONN, 0,
-ENOTCONN, -ENOTCONN, 0, -ENOTCONN,
0, -EAGAIN, -EAGAIN, -EAGAIN,
0, -EAGAIN, 0, -EAGAIN,
0, 0, 0, 0,
0, 1, 0, 0,
0, 0, 0, 1,
0, 0, 0, 0,
0, -ECONNRESET, 0, -ENOTCONN,
},
[C_SEND] = {
-ENOTCONN, -ENOTCONN, -EPIPE, -EPIPE,
-ENOTCONN, -ENOTCONN, -ENOTCONN, -EPIPE,
-EPIPE, -EAGAIN, 1, 1,
1, -EPIPE, -EPIPE, -EPIPE,
1, -EPIPE, -EPIPE, -EPIPE,
-EPIPE, -EPIPE, -EPIPE, -EPIPE,
-EPIPE, -EPIPE, -EPIPE, -EPIPE,
-EPIPE, -EPIPE, -EPIPE, -EPIPE,
-EPIPE, -ECONNRESET, -EPIPE, -ENOTCONN,
},
[C_SENDTO] = {
-ENOTCONN, -ENOTCONN, -EPIPE, -EPIPE,
-ENOTCONN, -ENOTCONN, -ENOTCONN, -EPIPE,
-EPIPE, -EAGAIN, 1, 1,
1, -EPIPE, -EPIPE, -EPIPE,
1, -EPIPE, -EPIPE, -EPIPE,
-EPIPE, -EPIPE, -EPIPE, -EPIPE,
-EPIPE, -EPIPE, -EPIPE, -EPIPE,
-EPIPE, -EPIPE, -EPIPE, -EPIPE,
-EPIPE, -ECONNRESET, -EPIPE, -ENOTCONN,
},
[C_SELECT_R] = {
1, 1, 1, 1,
1, 0, 1, 1,
1, 0, 0, 0,
1, 0, 1, 0,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
},
[C_SELECT_W] = {
1, 1, 1, 1,
1, 1, 1, 1,
1, 0, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
},
[C_SELECT_X] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_SETSOCKOPT_BC] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_SETSOCKOPT_KA] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_SETSOCKOPT_L] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_SETSOCKOPT_RA] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_SHUTDOWN_R] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_SHUTDOWN_RW] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
[C_SHUTDOWN_W] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
};
/*
* Set up a connection-oriented socket file descriptor in the requested state
* and pass it to socklib_sweep_call() along with local and remote addresses
* and their length.
*/
static int
unix_connect_sweep(int domain, int type, int protocol __unused,
enum state state, enum call call)
{
struct sockaddr_un sunA, sunB, sunC;
char buf[1];
socklen_t len;
fd_set fds;
int r, fd, fd2, fd3, tmpfd, val, fl;
(void)unlink(SOCK_PATH_A);
(void)unlink(SOCK_PATH_B);
(void)unlink(SOCK_PATH_C);
memset(&sunA, 0, sizeof(sunA));
sunA.sun_family = AF_UNIX;
strlcpy(sunA.sun_path, SOCK_PATH_A, sizeof(sunA.sun_path));
fd = fd3 = -1;
fd2 = get_bound_socket(type | SOCK_NONBLOCK, SOCK_PATH_B, &sunB);
if (listen(fd2, 1) == -1) e(0);
switch (state) {
case S_NEW:
case S_N_SHUT_R:
case S_N_SHUT_W:
case S_N_SHUT_RW:
if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0);
switch (state) {
case S_N_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break;
case S_N_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break;
case S_N_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break;
default: break;
}
break;
case S_BOUND:
case S_LISTENING:
case S_L_SHUT_R:
case S_L_SHUT_W:
case S_L_SHUT_RW:
fd = get_bound_socket(type | SOCK_NONBLOCK, SOCK_PATH_A,
&sunA);
if (state == S_BOUND)
break;
if (listen(fd, 1) == -1) e(0);
switch (state) {
case S_L_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break;
case S_L_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break;
case S_L_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break;
default: break;
}
break;
case S_CONNECTING:
/*
* The following block is nonportable. On NetBSD, the
* LOCAL_CONNWAIT socket option is present but seems somewhat..
* under-tested. On Linux, it is not possible to put a UNIX
* domain socket in a connecting state.
*/
if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0);
val = 1;
if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0)
e(0);
if (connect(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != -1)
e(0);
if (errno != EINPROGRESS) e(0);
break;
case S_CONNECTED:
case S_ACCEPTED:
case S_SHUT_R:
case S_SHUT_W:
case S_SHUT_RW:
case S_RSHUT_R:
case S_RSHUT_W:
case S_RSHUT_RW:
case S_SHUT2_R:
case S_SHUT2_W:
case S_SHUT2_RW:
if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0);
if (connect(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != 0)
e(0);
len = sizeof(sunC);
if ((fd3 = accept(fd2, (struct sockaddr *)&sunC, &len)) < 0)
e(0);
if ((fl = fcntl(fd3, F_GETFL)) == -1) e(0);
if (fcntl(fd3, F_SETFL, fl | O_NONBLOCK) != 0) e(0);
/* Just to make sure, wait for the socket to be connected. */
FD_ZERO(&fds);
FD_SET(fd, &fds);
if (select(fd + 1, NULL, &fds, NULL, NULL) != 1) e(0);
switch (state) {
case S_SHUT_R:
case S_SHUT2_R: if (shutdown(fd, SHUT_RD)) e(0); break;
case S_SHUT_W:
case S_SHUT2_W: if (shutdown(fd, SHUT_WR)) e(0); break;
case S_SHUT_RW:
case S_SHUT2_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break;
default: break;
}
switch (state) {
case S_RSHUT_R:
case S_SHUT2_R: if (shutdown(fd3, SHUT_RD)) e(0); break;
case S_RSHUT_W:
case S_SHUT2_W: if (shutdown(fd3, SHUT_WR)) e(0); break;
case S_RSHUT_RW:
case S_SHUT2_RW: if (shutdown(fd3, SHUT_RDWR)) e(0); break;
default: break;
}
if (state == S_ACCEPTED) {
tmpfd = fd;
fd = fd3;
fd3 = tmpfd;
}
break;
case S_PRE_EOF:
case S_AT_EOF:
case S_POST_EOF:
case S_PRE_SHUT_R:
case S_EOF_SHUT_R:
case S_POST_SHUT_R:
case S_PRE_SHUT_W:
case S_EOF_SHUT_W:
case S_POST_SHUT_W:
case S_PRE_SHUT_RW:
case S_EOF_SHUT_RW:
case S_POST_SHUT_RW:
if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0);
if (connect(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != 0)
e(0);
len = sizeof(sunC);
if ((fd3 = accept(fd2, (struct sockaddr *)&sunC, &len)) < 0)
e(0);
if ((fl = fcntl(fd3, F_GETFL)) == -1) e(0);
if (fcntl(fd3, F_SETFL, fl | O_NONBLOCK) != 0) e(0);
if (send(fd3, "", 1, 0) != 1) e(0);
if (close(fd3) != 0) e(0);
fd3 = -1;
FD_ZERO(&fds);
FD_SET(fd, &fds);
if (select(fd + 1, &fds, NULL, NULL, NULL) != 1) e(0);
switch (state) {
case S_AT_EOF:
case S_EOF_SHUT_R:
case S_EOF_SHUT_W:
case S_EOF_SHUT_RW:
if (recv(fd, buf, sizeof(buf), 0) != 1) e(0);
break;
case S_POST_EOF:
case S_POST_SHUT_R:
case S_POST_SHUT_W:
case S_POST_SHUT_RW:
if (recv(fd, buf, sizeof(buf), 0) != 1) e(0);
if (recv(fd, buf, sizeof(buf), 0) != 0) e(0);
break;
default:
break;
}
switch (state) {
case S_PRE_SHUT_R:
case S_EOF_SHUT_R:
case S_POST_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break;
case S_PRE_SHUT_W:
case S_EOF_SHUT_W:
case S_POST_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break;
case S_PRE_SHUT_RW:
case S_EOF_SHUT_RW:
case S_POST_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break;
default: break;
}
break;
case S_AT_RESET:
case S_POST_RESET:
if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0);
if (connect(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != 0)
e(0);
/*
* Closing the listening socket before the connection has been
* accepted should generate ECONNRESET on the connected socket.
* Well, should.. we choose to do that. So does Linux. NetBSD
* just returns EOF for that case. There are really no strong
* arguments for either behavior.
*/
if (close(fd2) != 0) e(0);
if (state == S_POST_RESET)
(void)recv(fd, buf, sizeof(buf), 0);
/* Recreate the listening socket just for consistency. */
fd2 = get_bound_socket(type | SOCK_NONBLOCK, SOCK_PATH_B,
&sunB);
if (listen(fd2, 1) == -1) e(0);
break;
case S_POST_FAILED:
if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0);
memset(&sunC, 0, sizeof(sunC));
sunC.sun_family = AF_UNIX;
strlcpy(sunC.sun_path, SOCK_PATH_C, sizeof(sunC.sun_path));
r = connect(fd, (struct sockaddr *)&sunC, sizeof(sunC));
if (r != -1 || errno != ENOENT)
e(0);
break;
default:
e(0);
}
r = socklib_sweep_call(call, fd, (struct sockaddr *)&sunA,
(struct sockaddr *)&sunB, sizeof(struct sockaddr_un));
if (fd >= 0 && close(fd) != 0) e(0);
if (fd2 >= 0 && close(fd2) != 0) e(0);
if (fd3 >= 0 && close(fd3) != 0) e(0);
(void)unlink(SOCK_PATH_A);
(void)unlink(SOCK_PATH_B);
return r;
}
static const enum state unix_dgram_states[] = {
S_NEW, S_N_SHUT_R, S_N_SHUT_W, S_N_SHUT_RW,
S_BOUND, S_CONNECTED, S_SHUT_R, S_SHUT_W,
S_SHUT_RW, S_RSHUT_R, S_RSHUT_W, S_RSHUT_RW,
S_SHUT2_R, S_SHUT2_W, S_SHUT2_RW, S_PRE_RESET,
S_AT_RESET, S_POST_RESET
};
static const int unix_dgram_results[][__arraycount(unix_dgram_states)] = {
[C_ACCEPT] = {
-EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP, -EOPNOTSUPP,
},
[C_BIND] = {
0, 0, 0, 0,
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EINVAL, -EINVAL, -EINVAL,
-EINVAL, -EINVAL,
},
[C_CONNECT] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, -ECONNREFUSED,
-ECONNREFUSED, -ECONNREFUSED,
},
[C_GETPEERNAME] = {
-ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN,
-ENOTCONN, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, -ENOTCONN,
-ENOTCONN, -ENOTCONN,
},
[C_GETSOCKNAME] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0,
},
[C_GETSOCKOPT_ERR] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, -ECONNRESET,
-ECONNRESET, 0,
},
[C_GETSOCKOPT_KA] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0,
},
[C_GETSOCKOPT_RB] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0,
},
[C_IOCTL_NREAD] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 1,
0, 0,
},
[C_LISTEN] = {
-EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP, -EOPNOTSUPP,
},
[C_RECV] = {
-EAGAIN, 0, -EAGAIN, 0,
-EAGAIN, -EAGAIN, 0, -EAGAIN,
0, -EAGAIN, -EAGAIN, -EAGAIN,
0, -EAGAIN, 0, 1,
-ECONNRESET, -EAGAIN,
},
[C_RECVFROM] = {
-EAGAIN, 0, -EAGAIN, 0,
-EAGAIN, -EAGAIN, 0, -EAGAIN,
0, -EAGAIN, -EAGAIN, -EAGAIN,
0, -EAGAIN, 0, 1,
-ECONNRESET, -EAGAIN,
},
[C_SEND] = {
-EDESTADDRREQ, -EDESTADDRREQ, -EPIPE, -EPIPE,
-EDESTADDRREQ, 1, 1, -EPIPE,
-EPIPE, -ENOBUFS, 1, -ENOBUFS,
-ENOBUFS, -EPIPE, -EPIPE, -ECONNRESET,
-ECONNRESET, -EDESTADDRREQ,
},
[C_SENDTO] = {
1, 1, -EPIPE, -EPIPE,
1, 1, 1, -EPIPE,
-EPIPE, -ENOBUFS, 1, -ENOBUFS,
-ENOBUFS, -EPIPE, -EPIPE, -ECONNRESET,
-ECONNRESET, -ECONNREFUSED,
},
[C_SELECT_R] = {
0, 1, 0, 1,
0, 0, 1, 0,
1, 0, 0, 0,
1, 0, 1, 1,
1, 0,
},
[C_SELECT_W] = {
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1,
},
[C_SELECT_X] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0,
},
[C_SETSOCKOPT_BC] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0,
},
[C_SETSOCKOPT_KA] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0,
},
[C_SETSOCKOPT_L] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0,
},
[C_SETSOCKOPT_RA] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0,
},
[C_SHUTDOWN_R] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0,
},
[C_SHUTDOWN_RW] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0,
},
[C_SHUTDOWN_W] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0,
},
};
/*
* Set up a datagram socket file descriptor in the requested state and pass it
* to socklib_sweep_call() along with local and remote addresses and their
* length.
*/
static int
unix_dgram_sweep(int domain __unused, int type, int protocol __unused,
enum state state, enum call call)
{
struct sockaddr_un sunA, sunB;
char buf[1];
int r, fd, fd2;
(void)unlink(SOCK_PATH_A);
(void)unlink(SOCK_PATH_B);
/* Create a bound remote socket. */
fd2 = get_bound_socket(type | SOCK_NONBLOCK, SOCK_PATH_B, &sunB);
switch (state) {
case S_NEW:
case S_N_SHUT_R:
case S_N_SHUT_W:
case S_N_SHUT_RW:
memset(&sunA, 0, sizeof(sunA));
sunA.sun_family = AF_UNIX;
strlcpy(sunA.sun_path, SOCK_PATH_A, sizeof(sunA.sun_path));
if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0)
e(0);
switch (state) {
case S_N_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break;
case S_N_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break;
case S_N_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break;
default: break;
}
break;
case S_BOUND:
case S_CONNECTED:
case S_SHUT_R:
case S_SHUT_W:
case S_SHUT_RW:
case S_RSHUT_R:
case S_RSHUT_W:
case S_RSHUT_RW:
case S_SHUT2_R:
case S_SHUT2_W:
case S_SHUT2_RW:
case S_PRE_RESET:
case S_AT_RESET:
case S_POST_RESET:
fd = get_bound_socket(type | SOCK_NONBLOCK, SOCK_PATH_A,
&sunA);
if (state == S_BOUND)
break;
if (connect(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != 0)
e(0);
switch (state) {
case S_SHUT_R:
case S_SHUT2_R: if (shutdown(fd, SHUT_RD)) e(0); break;
case S_SHUT_W:
case S_SHUT2_W: if (shutdown(fd, SHUT_WR)) e(0); break;
case S_SHUT_RW:
case S_SHUT2_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break;
default: break;
}
switch (state) {
case S_RSHUT_R:
case S_SHUT2_R: if (shutdown(fd2, SHUT_RD)) e(0); break;
case S_RSHUT_W:
case S_SHUT2_W: if (shutdown(fd2, SHUT_WR)) e(0); break;
case S_RSHUT_RW:
case S_SHUT2_RW: if (shutdown(fd2, SHUT_RDWR)) e(0); break;
case S_PRE_RESET:
case S_AT_RESET:
case S_POST_RESET:
if (sendto(fd2, "", 1, 0, (struct sockaddr *)&sunA,
sizeof(sunA)) != 1) e(0);
if (close(fd2) != 0) e(0);
fd2 = -1;
if (state != S_PRE_RESET) {
if (recv(fd, buf, sizeof(buf), 0) != 1) e(0);
}
if (state == S_POST_RESET) {
(void)recv(fd, buf, sizeof(buf), 0);
}
default:
break;
}
break;
default:
fd = -1;
e(0);
}
r = socklib_sweep_call(call, fd, (struct sockaddr *)&sunA,
(struct sockaddr *)&sunB, sizeof(struct sockaddr_un));
if (close(fd) != 0) e(0);
if (fd2 != -1 && close(fd2) != 0) e(0);
(void)unlink(SOCK_PATH_A);
(void)unlink(SOCK_PATH_B);
return r;
}
/*
* Sweep test for socket calls versus socket states of all socket types.
*/
static void
test90a(void)
{
subtest = 1;
socklib_sweep(AF_UNIX, SOCK_STREAM, 0, unix_connect_states,
__arraycount(unix_connect_states),
(const int *)unix_connect_results, unix_connect_sweep);
socklib_sweep(AF_UNIX, SOCK_SEQPACKET, 0, unix_connect_states,
__arraycount(unix_connect_states),
(const int *)unix_connect_results, unix_connect_sweep);
socklib_sweep(AF_UNIX, SOCK_DGRAM, 0, unix_dgram_states,
__arraycount(unix_dgram_states), (const int *)unix_dgram_results,
unix_dgram_sweep);
}
/*
* Test for large sends and receives with MSG_WAITALL.
*/
static void
test90b(void)
{
int fd[2];
subtest = 2;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) e(0);
socklib_large_transfers(fd);
}
/*
* A randomized producer-consumer test for datagram sockets.
*/
static void
sub90c(int type)
{
char *buf;
time_t t;
socklen_t len, size;
ssize_t r;
pid_t pid;
unsigned int count;
int i, fd[2], rcvlen, status, exp, flags, num, stat[2] = { 0, 0 };
get_socket_pair(type, fd);
size = rcvlen = get_rcvbuf_len(fd[0]);
if ((buf = malloc(size)) == NULL) e(0);
t = time(NULL);
/*
* We vary small versus large (random) send and receive sizes,
* splitting the entire transfer in four phases along those lines.
*
* In theory, the use of an extra system call and the use of MSG_PEEK
* both contribute to the expectation that the consumer side will fall
* behind the producer. In this case, we cannot vary receive sizes to
* compensate. This not appear to be a major problem here, though.
*/
#define NR_PACKETS (256 * 1024)
pid = fork();
switch (pid) {
case 0:
errct = 0;
if (close(fd[0]) != 0) e(0);
srand48(t + 1);
for (count = 0; count < NR_PACKETS; ) {
if (count < NR_PACKETS / 2)
len = lrand48() % 64;
else
len = lrand48() % size;
num = lrand48() % 16;
flags = 0;
if (num & 1) flags |= MSG_PEEK;
if (num & 2) flags |= MSG_WAITALL;
if (num & 4) flags |= MSG_DONTWAIT;
if (num & 8) {
/*
* Obviously there are race conditions here but
* the returned number should be accurate if
* not zero. Hopefully it's not always zero.
*/
if (ioctl(fd[1], FIONREAD, &exp) != 0) e(0);
if (exp < 0 || exp > rcvlen) e(0);
} else
exp = 0;
stat[0]++;
/*
* A lame approach to preventing unbounded spinning on
* ENOBUFS on the producer side.
*/
if (type == SOCK_DGRAM)
(void)send(fd[1], "", 1, MSG_DONTWAIT);
if ((r = recv(fd[1], buf, len, flags)) == -1) {
if (errno != EWOULDBLOCK) e(0);
if (exp > 0) e(0);
stat[1]++;
continue;
}
if (exp != 0) {
if (r == len && exp < r) e(0);
else if (r < len && exp != r) e(0);
}
if (r >= 2 &&
r > ((size_t)(unsigned char)buf[0] << 8) +
(size_t)(unsigned char)buf[1]) e(0);
for (i = 2; i < r; i++)
if (buf[i] != (char)i) e(0);
if (!(flags & MSG_PEEK))
count++;
}
#if PRINT_STATS
/*
* The second and third numbers should ideally be a large but
* non-dominating fraction of the first one.
*/
printf("RECV: total %d again %d\n", stat[0], stat[1]);
#endif
if (close(fd[1]) != 0) e(0);
exit(errct);
case -1:
e(0);
}
if (close(fd[1]) != 0) e(0);
srand48(t);
for (count = 0; count < NR_PACKETS; ) {
if (count < NR_PACKETS / 4 ||
(count >= NR_PACKETS / 2 && count < NR_PACKETS * 3 / 4))
len = lrand48() % 64;
else
len = lrand48() % size;
buf[0] = (len >> 8) & 0xff;
buf[1] = len & 0xff;
for (i = 2; i < len; i++)
buf[i] = i;
flags = (lrand48() % 2) ? MSG_DONTWAIT : 0;
r = send(fd[0], buf, len, flags);
if (r != len) {
if (r != -1) e(0);
if (errno != EMSGSIZE && errno != EWOULDBLOCK &&
errno != ENOBUFS) e(0);
if (errno == ENOBUFS || errno == EWOULDBLOCK) {
/*
* As stated above: lame. Ideally we would
* continue only when the receiver side drains
* the queue, but it may block once it has done
* so. Instead, by going through consumer
* "tokens" we will ultimately block here and
* let the receiver catch up.
*/
if (type == SOCK_DGRAM && errno == ENOBUFS)
(void)recv(fd[0], buf, 1, 0);
stat[0]++;
stat[1]++;
}
continue;
} else
stat[0]++;
if (count % (NR_PACKETS / 4) == 0)
sleep(1);
count++;
}
#if PRINT_STATS
/*
* The second number should ideally be a large but non-dominating
* fraction of the first one.
*/
printf("SEND: total %d again %d\n", stat[0], stat[1]);
#endif
free(buf);
if (close(fd[0]) != 0) e(0);
if (waitpid(pid, &status, 0) != pid) e(0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0);
}
/*
* A randomized producer-consumer test. As part of this, we also perform very
* basic bulk functionality tests of FIONREAD, MSG_PEEK, MSG_DONTWAIT, and
* MSG_WAITALL.
*/
static void
test90c(void)
{
int fd[2];
subtest = 4;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) e(0);
socklib_producer_consumer(fd);
sub90c(SOCK_SEQPACKET);
sub90c(SOCK_DGRAM);
}
/*
* Test that immediately accepted non-blocking connect requests to a listening
* socket with LOCAL_CONNWAIT turned on, return OK rather than EINPROGRESS.
* This requires a hack in libsockevent.
*/
static void
test90d(void)
{
struct sockaddr_un sunA, sunB;
socklen_t len;
pid_t pid;
int fd, fd2, fd3, val, status;
subtest = 4;
/*
* First ensure that a non-blocking connect to a listening socket that
* does not have a accept call blocked on it, fails with EINPROGRESS.
*/
fd = get_bound_socket(SOCK_STREAM, SOCK_PATH_A, &sunA);
val = 1;
if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0);
if (listen(fd, 1) != 0) e(0);
if ((fd2 = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != -1) e(0);
if (errno != EINPROGRESS) e(0);
len = sizeof(sunB);
if ((fd3 = accept(fd, (struct sockaddr *)&sunB, &len)) < 0) e(0);
check_addr(&sunB, len, NULL);
if (close(fd) != 0) e(0);
if (close(fd2) != 0) e(0);
if (close(fd3) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
/*
* Second, ensure that a blocking connect eventually does return
* success if an accept call is made later on.
*/
fd = get_bound_socket(SOCK_STREAM, SOCK_PATH_A, &sunA);
val = 1;
if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0);
if (listen(fd, 1) != 0) e(0);
pid = fork();
switch (pid) {
case 0:
errct = 0;
sleep(1);
len = sizeof(sunB);
if ((fd2 = accept(fd, (struct sockaddr *)&sunB, &len)) < 0)
e(0);
check_addr(&sunB, len, NULL);
exit(errct);
case -1:
e(0);
}
if (close(fd) != 0) e(0);
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) e(0);
if (connect(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
if (close(fd) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
if (waitpid(pid, &status, 0) != pid) e(0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0);
/*
* Finally, test the most implementation-complex case: a non-blocking
* connect should succeed (i.e., yield return code 0) immediately if
* there is a accept call blocked on it.
*/
fd = get_bound_socket(SOCK_STREAM, SOCK_PATH_A, &sunA);
val = 1;
if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0);
if (listen(fd, 1) != 0) e(0);
pid = fork();
switch (pid) {
case 0:
errct = 0;
len = sizeof(sunB);
if ((fd2 = accept(fd, (struct sockaddr *)&sunB, &len)) < 0)
e(0);
check_addr(&sunB, len, SOCK_PATH_B);
exit(errct);
case -1:
e(0);
}
if (close(fd) != 0) e(0);
sleep(1);
fd = get_bound_socket(SOCK_STREAM | SOCK_NONBLOCK, SOCK_PATH_B, &sunB);
if (connect(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
if (close(fd) != 0) e(0);
if (waitpid(pid, &status, 0) != pid) e(0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
if (unlink(SOCK_PATH_B) != 0) e(0);
}
/*
* Test self-connecting datagram sockets.
*/
static void
test90e(void)
{
struct sockaddr_un sunA, sunB, sunC;
socklen_t len;
char buf[3];
pid_t pid;
int fdA, fdB, val, status;
subtest = 5;
fdA = get_bound_socket(SOCK_DGRAM, SOCK_PATH_A, &sunA);
fdB = get_bound_socket(SOCK_DGRAM, SOCK_PATH_B, &sunB);
/* Connect the socket to itself, and attempt to communicate. */
if (connect(fdA, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
if (send(fdA, "abc", 3, 0) != 3) e(0);
if (recv(fdA, buf, sizeof(buf), 0) != 3) e(0);
if (strncmp(buf, "abc", 3)) e(0);
/* Reconnect the socket to another target. */
if (connect(fdA, (struct sockaddr *)&sunB, sizeof(sunB)) != 0) e(0);
len = sizeof(val);
if (getsockopt(fdA, SOL_SOCKET, SO_ERROR, &val, &len) != 0) e(0);
if (val != 0) e(0);
if (send(fdA, "def", 3, 0) != 3) e(0);
memset(&sunC, 0, sizeof(sunC));
len = sizeof(sunC);
if (recvfrom(fdB, buf, sizeof(buf), 0, (struct sockaddr *)&sunC,
&len) != 3) e(0);
check_addr(&sunC, len, SOCK_PATH_A);
if (strncmp(buf, "def", 3)) e(0);
/* Reconnect the socket to itself again. */
if (connect(fdA, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
len = sizeof(val);
if (getsockopt(fdA, SOL_SOCKET, SO_ERROR, &val, &len) != 0) e(0);
if (val != 0) e(0);
pid = fork();
switch (pid) {
case 0:
errct = 0;
if (recv(fdA, buf, sizeof(buf), 0) != 3) e(0);
if (strncmp(buf, "ghi", 3)) e(0);
exit(errct);
case -1:
e(0);
}
sleep(1);
if (send(fdA, "ghi", 3, 0) != 3) e(0);
if (waitpid(pid, &status, 0) != pid) e(0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0);
if (close(fdA) != 0) e(0);
if (close(fdB) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
if (unlink(SOCK_PATH_B) != 0) e(0);
}
/*
* Test multiple blocked calls getting resumed (or not) upon connect(2) success
* or failure. This test uses LOCAL_CONNWAIT. TODO: rewrite this to use
* interprocess communication rather than the current carefully arranged and
* rather brittle timing approach.
*/
static void
sub90f(unsigned int test)
{
struct sockaddr_un sun;
pid_t pid[4], apid;
char buf[1];
unsigned int i;
socklen_t len;
int r, fd, fd2, fl, val, status;
fd = get_bound_socket(SOCK_STREAM, SOCK_PATH_A, &sun);
val = 1;
if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0);
if (listen(fd, 1) != 0) e(0);
apid = fork();
switch (apid) {
case 0:
errct = 0;
sleep(3);
if (test < 2) {
len = sizeof(sun);
if ((fd2 = accept(fd, (struct sockaddr *)&sun,
&len)) < 0) e(0);
sleep(2);
if (close(fd2) != 0) e(0);
}
if (close(fd) != 0) e(0);
exit(errct);
case -1:
e(0);
}
if (close(fd) != 0) e(0);
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) e(0);
for (i = 0; i < __arraycount(pid); i++) {
pid[i] = fork();
switch (pid[i]) {
case 0:
errct = 0;
sleep((i == 0) ? 1 : 2);
if ((i & 1) == 0) {
r = send(fd, "", 1, 0);
switch (test) {
case 0:
case 1:
if (r != 1) e(0);
break;
case 3:
if (i == 0) {
if (r != -1) e(0);
if (errno != ECONNRESET) e(0);
break;
}
/* FALLTHROUGH */
case 2:
if (r != -1) e(0);
if (errno != ENOTCONN) e(0);
}
} else {
r = recv(fd, buf, sizeof(buf), 0);
if (test >= 2) {
if (r != -1) e(0);
if (errno != ENOTCONN) e(0);
} else
if (r != 0) e(0);
}
exit(errct);
case -1:
e(0);
}
}
if (test & 1) {
if ((fl = fcntl(fd, F_GETFL)) == -1) e(0);
if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) != 0) e(0);
}
r = connect(fd, (struct sockaddr *)&sun, sizeof(sun));
if (test & 1) {
if (r != -1) e(0);
if (errno != EINPROGRESS) e(0);
if (fcntl(fd, F_SETFL, fl) != 0) e(0);
sleep(4);
} else {
if (test >= 2) {
if (r != -1) e(0);
if (errno != ECONNRESET) e(0);
} else
if (r != 0) e(0);
sleep(1);
}
/*
* If the connect failed, collect the senders and receivers.
* Otherwise, collect just the senders.
*/
for (i = 0; i < __arraycount(pid); i++) {
r = waitpid(pid[i], &status, WNOHANG);
if (r == pid[i]) {
if (test < 2 && (i & 1)) e(0);
if (!WIFEXITED(status)) e(0);
if (WEXITSTATUS(status) != 0) e(0);
} else if (r == 0) {
if (test >= 2 || !(i & 1)) e(0);
} else
e(0);
}
if (close(fd) != 0) e(0);
/* Wait for, and collect the accepting child. */
if (waitpid(apid, &status, 0) != apid) e(0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0);
/*
* If the connect succeeded, collect the receivers, which will
* terminate once the accepting child closes the accepted socket.
*/
if (test < 2) {
if (waitpid(pid[1], &status, 0) != pid[1]) e(0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0);
if (waitpid(pid[3], &status, 0) != pid[3]) e(0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0);
}
if (unlink(SOCK_PATH_A) != 0) e(0);
}
/*
* Test multiple blocked calls getting resumed (or not) upon connect(2) success
* or failure. In particular, ensure that the error code ends up with the
* right call.
*/
static void
test90f(void)
{
subtest = 6;
/* If a connect succeeds, sends continue but reads block until EOF. */
sub90f(0); /* blocking connect */
sub90f(1); /* non-blocking connect */
/* If a blocking connect fails, the connect call gets the error. */
sub90f(2);
/* If a non-blocking connect fails, the first blocked call gets it. */
sub90f(3);
}
/*
* Test whether various calls all return the same expected error code.
*/
static void
sub90g(struct sockaddr_un * sun, int err)
{
int fd;
if ((fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0) e(0);
if (connect(fd, (struct sockaddr *)sun, sizeof(*sun)) != -1) e(0);
if (errno != err) e(0);
if (close(fd) != 0) e(0);
if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) e(0);
if (sendto(fd, "", 1, 0, (struct sockaddr *)sun, sizeof(*sun)) != -1)
e(0);
if (errno != err) e(0);
if (connect(fd, (struct sockaddr *)sun, sizeof(*sun)) != -1) e(0);
if (errno != err) e(0);
if (close(fd) != 0) e(0);
}
/*
* Test for error codes thrown by connect(2) and sendto(2) with problematic
* destinations. In particular, we verify that the errors for sendto(2) are
* the same as for connect(2), just like on NetBSD and Linux, even though
* POSIX does not document all of these under sendto(2).
*/
static void
test90g(void)
{
struct sockaddr_un sun;
int fd;
subtest = 7;
fd = get_bound_socket(SOCK_STREAM, SOCK_PATH_A, &sun);
sub90g(&sun, EPROTOTYPE);
if (listen(fd, 1) != 0) e(0);
sub90g(&sun, EPROTOTYPE);
if (close(fd) != 0) e(0);
sub90g(&sun, ECONNREFUSED);
if (unlink(SOCK_PATH_A) != 0) e(0);
sub90g(&sun, ENOENT);
}
/*
* Test addresses returned for unbound connection-type sockets by various
* calls.
*/
static void
sub90h(int type)
{
struct sockaddr_un sun;
socklen_t len;
char buf[1];
int fd, fd2, fd3;
fd = get_bound_socket(type, SOCK_PATH_A, &sun);
if (listen(fd, 5) != 0) e(0);
if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != 0) e(0);
/* Test for accept(2), which returns an empty address. */
memset(&sun, 0, sizeof(sun));
len = sizeof(sun);
if ((fd3 = accept(fd, (struct sockaddr *)&sun, &len)) < 0) e(0);
check_addr(&sun, len, NULL);
/* Test for recvfrom(2), which ignores the address pointer. */
if (send(fd2, "", 1, 0) != 1) e(0);
memset(&sun, 0, sizeof(sun));
len = sizeof(sun);
if (recvfrom(fd3, buf, sizeof(buf), 0, (struct sockaddr *)&sun,
&len) != 1) e(0);
if (len != 0) e(0);
if (sun.sun_family != 0) e(0);
if (sun.sun_len != 0) e(0);
/* Test for getsockname(2), which returns an empty address. */
memset(&sun, 0, sizeof(sun));
len = sizeof(sun);
if (getsockname(fd2, (struct sockaddr *)&sun, &len) != 0) e(0);
check_addr(&sun, len, NULL);
/* Test for getpeername(2), which returns an empty address. */
memset(&sun, 0, sizeof(sun));
len = sizeof(sun);
if (getpeername(fd3, (struct sockaddr *)&sun, &len) != 0) e(0);
check_addr(&sun, len, NULL);
if (close(fd) != 0) e(0);
if (close(fd2) != 0) e(0);
if (close(fd3) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
}
/*
* Test addresses returned for unbound sockets by various calls.
*/
static void
test90h(void)
{
struct sockaddr_un sun;
socklen_t len;
char buf[1];
int fd, fd2;
subtest = 8;
/* Connection-type socket tests. */
sub90h(SOCK_STREAM);
sub90h(SOCK_SEQPACKET);
/* Datagram socket tests. */
fd = get_bound_socket(SOCK_DGRAM, SOCK_PATH_A, &sun);
if ((fd2 = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) e(0);
if (sendto(fd2, "", 1, 0, (struct sockaddr *)&sun, sizeof(sun)) != 1)
e(0);
/*
* Datagram test for recvfrom(2), which returns no address. This is
* the one result in this subtest that is not specified by POSIX and
* (not so coincidentally) is different between NetBSD and Linux.
* MINIX3 happens to follow Linux behavior for now, but this may be
* changed in the future.
*/
memset(&sun, 0, sizeof(sun));
len = sizeof(sun);
if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sun,
&len) != 1) e(0);
if (len != 0) e(0);
if (sun.sun_family != 0) e(0);
if (sun.sun_len != 0) e(0);
/* Datagram test for getsockname(2), which returns an empty address. */
memset(&sun, 0, sizeof(sun));
len = sizeof(sun);
if (getsockname(fd2, (struct sockaddr *)&sun, &len) != 0) e(0);
check_addr(&sun, len, NULL);
if (close(fd) != 0) e(0);
if (close(fd2) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
}
#define MAX_FDS 7
/*
* Send anywhere from zero to MAX_FDS file descriptors onto a socket, possibly
* along with regular data. Return the result of the sendmsg(2) call, with
* errno preserved. Written to be reusable outside this test set.
*/
static int
send_fds(int fd, const char * data, size_t len, int flags,
struct sockaddr * addr, socklen_t addr_len, int * fds, int nfds)
{
union {
char buf[CMSG_SPACE(MAX_FDS * sizeof(int))];
struct cmsghdr cmsg;
} control;
struct msghdr msg;
struct iovec iov;
assert(nfds >= 0 && nfds <= MAX_FDS);
iov.iov_base = __UNCONST(data);
iov.iov_len = len;
memset(&control.cmsg, 0, sizeof(control.cmsg));
control.cmsg.cmsg_len = CMSG_LEN(nfds * sizeof(int));
control.cmsg.cmsg_level = SOL_SOCKET;
control.cmsg.cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(&control.cmsg), fds, nfds * sizeof(int));
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &control;
msg.msg_controllen = control.cmsg.cmsg_len;
msg.msg_name = addr;
msg.msg_namelen = addr_len;
return sendmsg(fd, &msg, flags);
}
/*
* Receive anywhere from zero to up to MAX_FDS file descriptors from a socket,
* possibly along with regular data. The 'nfds' parameter must point to the
* maximum number of file descriptors to be received. Return the result of the
* recvmsg(2) call, with errno preserved. On success, return the received
* flags in 'rflags', the received file descriptors stored in 'fds' and their
* number stored in 'nfds'. Written to be (somewhat) reusable.
*/
static int
recv_fds(int fd, char * buf, size_t size, int flags, int * rflags, int * fds,
int * nfds)
{
union {
char buf[CMSG_SPACE(MAX_FDS * sizeof(int))];
struct cmsghdr cmsg;
} control;
struct msghdr msg;
struct iovec iov;
size_t len;
int r, rnfds;
assert(*nfds >= 0 && *nfds <= MAX_FDS);
iov.iov_base = buf;
iov.iov_len = size;
memset(&control.cmsg, 0, sizeof(control.cmsg));
control.cmsg.cmsg_len = CMSG_LEN(*nfds * sizeof(int));
control.cmsg.cmsg_level = SOL_SOCKET;
control.cmsg.cmsg_type = SCM_RIGHTS;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &control;
msg.msg_controllen = control.cmsg.cmsg_len;
if ((r = recvmsg(fd, &msg, flags)) < 0)
return r;
if (msg.msg_controllen > 0) {
assert(msg.msg_controllen <= sizeof(control));
assert(msg.msg_controllen >= sizeof(control.cmsg));
len = control.cmsg.cmsg_len - CMSG_LEN(0);
assert(len % sizeof(int) == 0);
rnfds = len / sizeof(int);
assert(rnfds <= *nfds);
memcpy(fds, CMSG_DATA(&control.cmsg), rnfds * sizeof(int));
} else
rnfds = 0;
*rflags = msg.msg_flags;
*nfds = rnfds;
return r;
}
/*
* Generate and send zero or more file descriptors onto a socket, possibly
* along with regular data. Return the result of the sendmsg(2) call, with
* errno preserved. Also return a set of peer FDs for each of the sent file
* descriptors, which should later be used in a call to close_test_fds().
*/
static int
send_test_fds(int fd, const char * data, size_t len, int flags, int * peers,
int nfds)
{
int i, r, saved_errno, fds[MAX_FDS], pfd[2];
if (nfds > MAX_FDS) e(0);
for (i = 0; i < nfds; i++) {
if (pipe2(pfd, O_NONBLOCK) != 0) e(0);
peers[i] = pfd[0];
fds[i] = pfd[1];
}
r = send_fds(fd, data, len, flags, NULL, 0, fds, nfds);
saved_errno = errno;
for (i = 0; i < nfds; i++)
if (close(fds[i]) != 0) e(0);
errno = saved_errno;
return r;
}
/*
* Given an array of peer file descriptors as returned from a call to
* send_test_fds(), test if the original file descriptors have correctly been
* closed, and close all peer file descriptors. The ultimate goal here is to
* detect any possible file descriptor leaks in the UDS service.
*/
static void
close_test_fds(int * peers, int nfds)
{
char buf[1];
unsigned int i;
int fd;
for (i = 0; i < nfds; i++) {
fd = peers[i];
/* If the other side is still open, we would get EAGAIN. */
if (read(fd, buf, sizeof(buf)) != 0) e(0);
if (close(peers[i]) != 0) e(0);
}
}
/*
* Receive and close zero or more file descriptors from a socket, possibly
* along with regular data. Return the result of the recvmsg(2) call, with
* errno preserved.
*/
static int
recv_test_fds(int fd, char * buf, size_t size, int flags, int * rflags,
int * nfds)
{
int i, r, saved_errno, fds[MAX_FDS];
if (*nfds > MAX_FDS) e(0);
if ((r = recv_fds(fd, buf, size, flags, rflags, fds, nfds)) < 0)
return r;
saved_errno = errno;
for (i = 0; i < *nfds; i++)
if (close(fds[i]) != 0) e(0);
errno = saved_errno;
return r;
}
/*
* Test receive requests on various socket states and in various forms.
* Following this function requires a very close look at what is in the
* receive queue versus what is being received.
*/
static void
sub90i_recv(int fd, int type, int state, int test, int sub, int sentfds)
{
struct msghdr msg;
struct iovec iov;
char data[4];
unsigned int i;
int res, err, nfds, rflags;
memset(data, 0, sizeof(data));
if (sub & 2) {
rflags = 0;
nfds = sentfds;
res = recv_test_fds(fd, data, (sub & 1) ? 0 : sizeof(data), 0,
&rflags, &nfds);
if (rflags & MSG_CTRUNC) e(0);
if (nfds != 0 && nfds != sentfds) e(0);
if ((type == SOCK_STREAM) && (rflags & MSG_TRUNC)) e(0);
} else {
iov.iov_base = data;
iov.iov_len = (sub & 1) ? 0 : sizeof(data);
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
res = recvmsg(fd, &msg, 0);
if (res >= 0)
rflags = msg.msg_flags;
else
rflags = 0;
nfds = 0;
}
err = errno;
if (res < -1 || res > (int)sizeof(data)) e(0);
if (type == SOCK_STREAM) {
if (sub & 1) {
/*
* Zero-size requests should receive no regular data
* and no control data, even if the tail segment is
* zero-sized and terminated. This policy is in place
* for simplicity reasons.
*/
if (res != 0) e(0);
if (nfds != 0) e(0);
if (rflags & MSG_CTRUNC) e(0);
/*
* Since nothing happened yet, do another, now non-
* zero receive call immediately, and continue as if
* that was the first call.
*/
sub = (sub & ~1) | 2;
nfds = sentfds;
rflags = 0;
res = recv_test_fds(fd, data, sizeof(data), 0, &rflags,
&nfds);
if (rflags & (MSG_TRUNC | MSG_CTRUNC)) e(0);
if (nfds != 0 && nfds != sentfds) e(0);
err = errno;
if (res < -1 || res > (int)sizeof(data)) e(0);
}
if (state == 0 && !(test & 1) && !(sub & 13)) {
/*
* There are no regular data bytes to be received, and
* the current segment may still be extended (i.e.,
* there is no EOF condition), and we are trying to
* receive at least one data byte. This is the
* SO_RCVLOWAT test.
*/
if (res != -1) e(0);
if (err != EWOULDBLOCK) e(0);
if (test == 4) {
/*
* There are still pending file descriptors but
* we cannot get them, due to the SO_RCVLOWAT
* test. This is proper behavior but somewhat
* annoying, because we want to see if UDS
* forgot to close any file descriptors. So,
* we let it force-close them here.
*/
if (shutdown(fd, SHUT_RD) != 0) e(0);
sub |= 8;
}
} else {
i = 0;
if (state == 1) {
if (res < 1) e(0);
if (data[i] != 'A') e(0);
i++;
}
if ((state == 0 && (test & 1)) ||
(state == 1 && (test == 1 || test == 3))) {
if (res < i + 1) e(0);
if (data[i] != 'B') e(0);
i++;
}
if ((sub & 4) && (state != 1 || test < 4)) {
if (res < i + 1) e(0);
if (data[i] != 'C') e(0);
i++;
}
if (i != res) e(0);
if (state == 0 && test >= 4) {
if (sub & 2) {
if (nfds != sentfds) e(0);
} else
if (!(rflags & MSG_CTRUNC)) e(0);
}
}
if (state == 1 && test >= 4) {
/*
* We just read the first segment, but there is a
* second segment with ancillary data. Read it too.
*/
nfds = sentfds;
rflags = 0;
res = recv_test_fds(fd, data, sizeof(data), 0, &rflags,
&nfds);
if (rflags & (MSG_TRUNC | MSG_CTRUNC)) e(0);
if (nfds != sentfds) e(0); /* untouched on failure */
if (res < -1 || res > (int)sizeof(data)) e(0);
if (test != 5 && !(sub & 12)) {
if (res != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
/* As above. */
if (shutdown(fd, SHUT_RD) != 0) e(0);
sub |= 8;
} else {
if (res != (test == 5) + !!(sub & 4)) e(0);
if (test == 5 && data[0] != 'B') e(0);
if ((sub & 4) && data[res - 1] != 'C') e(0);
}
}
} else {
if (res != ((state == 1 || (test & 1)) && !(sub & 1))) e(0);
if (state == 0 && test >= 4) {
if (sub & 2) {
if (nfds != sentfds) e(0);
} else
if (!(rflags & MSG_CTRUNC)) e(0);
}
if (res > 0 && data[0] != ((state == 1) ? 'A' : 'B')) e(0);
if (state == 1) {
nfds = sentfds;
rflags = 0;
res = recv_test_fds(fd, data, sizeof(data), 0, &rflags,
&nfds);
if (res != (test & 1)) e(0);
if (res > 0 && data[0] != 'B') e(0);
if (nfds != ((test >= 4) ? sentfds : 0)) e(0);
}
if (sub & 4) {
nfds = sentfds;
rflags = 0;
res = recv_test_fds(fd, data, sizeof(data), 0, &rflags,
&nfds);
if (res != 1) e(0);
if (data[0] != 'C') e(0);
if (nfds != 0) e(0);
}
}
/*
* At this point, there is nothing to receive. Depending on
* whether we closed the socket, we expect EOF or EWOULDBLOCK.
*/
res = recv(fd, data, sizeof(data), 0);
if (type == SOCK_DGRAM || !(sub & 8)) {
if (res != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
} else
if (res != 0) e(0);
}
/*
* Test send requests on various socket states and in various forms.
*/
static void
sub90i_send(int type, int state, int test, int sub)
{
char *buf;
int r, res, err, fd[2], peers[2], rcvlen;
get_socket_pair(type | SOCK_NONBLOCK, fd);
/*
* State 0: an empty buffer.
* State 1: a non-empty, non-full buffer.
* State 2: a full buffer.
*/
if (state == 2) {
if (type == SOCK_STREAM) {
rcvlen = get_rcvbuf_len(fd[0]);
if ((buf = malloc(rcvlen)) == NULL) e(0);
memset(buf, 'A', rcvlen);
if (send(fd[0], buf, rcvlen, 0) != rcvlen) e(0);
free(buf);
} else {
while ((r = send(fd[0], "A", 1, 0)) == 1);
if (r != -1) e(0);
if (errno !=
((type == SOCK_SEQPACKET) ? EAGAIN : ENOBUFS))
e(0);
}
} else if (state == 1)
if (send(fd[0], "A", 1, 0) != 1) e(0);
/*
* Test 0: no data, no control data.
* Test 1: data, no control data.
* Test 2: no data, empty control data.
* Test 3: data, empty control data.
* Test 4: no data, control data with a file descriptor.
* Test 5: data, control data with a file descriptor.
*/
switch (test) {
case 0:
case 1:
res = send(fd[0], "B", test % 2, 0);
err = errno;
break;
case 2:
case 3:
res = send_test_fds(fd[0], "B", test % 2, 0, NULL, 0);
err = errno;
break;
case 4:
case 5:
res = send_test_fds(fd[0], "B", test % 2, 0, peers,
__arraycount(peers));
err = errno;
break;
default:
res = -1;
err = EINVAL;
e(0);
}
if (res < -1 || res > 1) e(0);
switch (state) {
case 0:
case 1:
if (res != (test % 2)) e(0);
/*
* Subtest bit 0x1: try a zero-size receive first.
* Subtest bit 0x2: try receiving control data.
* Subtest bit 0x4: send an extra segment with no control data.
* Subtest bit 0x8: after completing receives, expect EOF.
*/
if (sub & 4)
if (send(fd[0], "C", 1, 0) != 1) e(0);
if (sub & 8)
if (shutdown(fd[0], SHUT_WR) != 0) e(0);
/*
* Assuming (sub&4), which means there is an extra "C"..
*
* For stream sockets, we should now receive:
* - state 0:
* - test 0, 2: "C"
* - test 1, 3: "BC"
* - test 4: "C" (w/fds)
* - test 5: "BC" (w/fds)
* - state 1:
* - test 0, 2: "AC"
* - test 1, 3: "ABC"
* - test 4: "A", "C" (w/fds)
* - test 5: "A", "BC" (w/fds)
*
* For packet sockets, we should now receive:
* - state 1:
* - all tests: "A", followed by..
* - state 0, 1:
* - test 0, 2: "" (no fds), "C"
* - test 1, 3: "B" (no fds), "C"
* - test 4: "" (w/fds), "C"
* - test 5: "B" (w/fds), "C"
*/
sub90i_recv(fd[1], type, state, test, sub,
__arraycount(peers));
break;
case 2:
/*
* Alright, the results are a bit tricky to interpret here,
* because UDS's current strict send admission control prevents
* the receive buffer from being fully utilized. We therefore
* only test the following aspects:
*
* - if we sent no regular or control data to a stream socket,
* the call should have succeeded (note that the presence of
* empty control data may cause the call to fail);
* - if we sent either regular or control data to a stream
* socket, the call should have failed with EWOULDBLOCK;
* - if the call failed, the error should have been EWOULDBLOCK
* for connection-type sockets and ENOBUFS for connectionless
* sockets.
*
* Everything else gets a pass; we can't even be sure that for
* packet-oriented sockets we completely filled up the buffer.
*/
if (res == -1) {
if (type == SOCK_STREAM && test == 0) e(0);
if (type != SOCK_DGRAM && err != EWOULDBLOCK) e(0);
if (type == SOCK_DGRAM && err != ENOBUFS) e(0);
} else
if (type == SOCK_STREAM && test != 0) e(0);
break;
}
/*
* Make sure there are no more in-flight file descriptors now, even
* before closing the socket.
*/
if (res >= 0 && test >= 4)
close_test_fds(peers, __arraycount(peers));
close(fd[0]);
close(fd[1]);
}
/*
* Test send and receive requests with regular data, control data, both, or
* neither, and test segment boundaries.
*/
static void
test90i(void)
{
int state, test, sub;
subtest = 9;
for (state = 0; state < 3; state++) {
for (test = 0; test < 6; test++) {
for (sub = 0; sub < ((state < 2) ? 16 : 1); sub++) {
sub90i_send(SOCK_STREAM, state, test, sub);
sub90i_send(SOCK_SEQPACKET, state, test, sub);
sub90i_send(SOCK_DGRAM, state, test, sub);
}
}
}
}
/*
* Test segmentation of file descriptor transfer on a particular socket type.
*/
static void
sub90j(int type)
{
char path[PATH_MAX], buf[2];
int i, fd[2], out[7], in[7], rflags, nfds;
ssize_t len;
get_socket_pair(type, fd);
for (i = 0; i < __arraycount(out); i++) {
snprintf(path, sizeof(path), "file%d", i);
out[i] = open(path, O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0644);
if (out[i] < 0) e(0);
if (write(out[i], path, strlen(path)) != strlen(path)) e(0);
if (lseek(out[i], 0, SEEK_SET) != 0) e(0);
}
if (send_fds(fd[1], "A", 1, 0, NULL, 0, &out[0], 1) != 1) e(0);
if (send_fds(fd[1], "B", 1, 0, NULL, 0, &out[1], 3) != 1) e(0);
if (send_fds(fd[1], "C", 1, 0, NULL, 0, &out[4], 2) != 1) e(0);
nfds = 2;
if (recv_fds(fd[0], buf, sizeof(buf), 0, &rflags, &in[0], &nfds) != 1)
e(0);
if (buf[0] != 'A') e(0);
if (rflags != 0) e(0);
if (nfds != 1) e(0);
nfds = 5;
if (recv_fds(fd[0], buf, sizeof(buf), 0, &rflags, &in[1], &nfds) != 1)
e(0);
if (buf[0] != 'B') e(0);
if (rflags != 0) e(0);
if (nfds != 3) e(0);
if (send_fds(fd[1], "D", 1, 0, NULL, 0, &out[6], 1) != 1) e(0);
nfds = 2;
if (recv_fds(fd[0], buf, sizeof(buf), 0, &rflags, &in[4], &nfds) != 1)
e(0);
if (buf[0] != 'C') e(0);
if (rflags != 0) e(0);
if (nfds != 2) e(0);
nfds = 2;
if (recv_fds(fd[0], buf, sizeof(buf), 0, &rflags, &in[6], &nfds) != 1)
e(0);
if (buf[0] != 'D') e(0);
if (rflags != 0) e(0);
if (nfds != 1) e(0);
for (i = 0; i < __arraycount(in); i++) {
len = read(in[i], path, sizeof(path));
if (len < 5 || len > 7) e(0);
path[len] = '\0';
if (strncmp(path, "file", 4) != 0) e(0);
if (atoi(&path[4]) != i) e(0);
if (unlink(path) != 0) e(0);
if (close(in[i]) != 0) e(0);
}
for (i = 0; i < __arraycount(out); i++)
if (close(out[i]) != 0) e(0);
/*
* While we're here, see if UDS properly closes any remaining in-flight
* file descriptors when the socket is closed.
*/
if (send_test_fds(fd[1], "E", 1, 0, out, 7) != 1) e(0);
close(fd[0]);
close(fd[1]);
close_test_fds(out, 7);
}
/*
* Test segmentation of file descriptor transfer. That is, there are multiple
* in-flight file descriptors, they must each be associated with their
* respective segments.
*/
static void
test90j(void)
{
subtest = 10;
sub90j(SOCK_STREAM);
sub90j(SOCK_SEQPACKET);
sub90j(SOCK_DGRAM);
}
/*
* Test whether we can deadlock UDS by making it close the last reference to
* an in-flight file descriptor for a UDS socket. Currently we allow VFS/UDS
* to get away with throwing EDEADLK as a sledgehammer approach to preventing
* problems with in-flight UDS sockets.
*/
static void
test90k(void)
{
int r, fd[2], fd2;
subtest = 11;
get_socket_pair(SOCK_STREAM, fd);
if ((fd2 = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) e(0);
if ((r = send_fds(fd[0], "X", 1, 0, NULL, 0, &fd2, 1)) != 1) {
if (r != -1) e(0);
if (errno != EDEADLK) e(0); /* whew */
}
if (close(fd2) != 0) e(0);
if (close(fd[0]) != 0) e(0);
if (close(fd[1]) != 0) e(0); /* boom */
}
/*
* Test whether we can make UDS run out of file descriptors by transferring a
* UDS socket over itself and then closing all other references while it is
* in-flight. Currently we allow VFS/UDS to get away with throwing EDEADLK as
* a sledgehammer approach to preventing problems with in-flight UDS sockets.
*/
static void
test90l(void)
{
struct sockaddr_un sun;
int i, r, fd, fd2;
subtest = 12;
fd = get_bound_socket(SOCK_DGRAM, SOCK_PATH_A, &sun);
for (i = 0; i < OPEN_MAX + 1; i++) {
if ((fd2 = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) e(0);
if ((r = send_fds(fd2, "X", 1, 0, (struct sockaddr *)&sun,
sizeof(sun), &fd2, 1)) != 1) {
if (r != -1) e(0);
if (errno != EDEADLK) e(0); /* whew */
}
if (close(fd2) != 0) e(0); /* have fun in limbo.. */
}
if (close(fd) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
}
/*
* Receive with credentials.
*/
static int
recv_creds(int fd, char * buf, size_t size, int flags, int * rflags,
struct sockcred * sc, socklen_t * sc_len)
{
union {
char buf[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX))];
struct cmsghdr cmsg;
} control;
struct msghdr msg;
struct iovec iov;
size_t len;
int r;
iov.iov_base = buf;
iov.iov_len = size;
memset(&control.cmsg, 0, sizeof(control.cmsg));
control.cmsg.cmsg_len = CMSG_LEN(SOCKCREDSIZE(NGROUPS_MAX));
control.cmsg.cmsg_level = SOL_SOCKET;
control.cmsg.cmsg_type = SCM_RIGHTS;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &control;
msg.msg_controllen = control.cmsg.cmsg_len;
if ((r = recvmsg(fd, &msg, flags)) < 0)
return r;
if (msg.msg_controllen > 0) {
assert(msg.msg_controllen <= sizeof(control));
assert(msg.msg_controllen >= sizeof(control.cmsg));
assert(control.cmsg.cmsg_len <= msg.msg_controllen);
len = control.cmsg.cmsg_len - CMSG_LEN(0);
assert(len >= sizeof(struct sockcred));
assert(len <= SOCKCREDSIZE(NGROUPS_MAX));
if (*sc_len > len)
*sc_len = len;
memcpy(sc, CMSG_DATA(&control.cmsg), *sc_len);
} else
*sc_len = 0;
*rflags = msg.msg_flags;
return r;
}
/*
* Test basic credentials passing on connection-oriented sockets.
*/
static void
sub90m(int type)
{
struct sockaddr_un sun;
struct sockcred sc;
struct msghdr msg;
struct iovec iov;
socklen_t len;
char buf[1];
int fd, fd2, fd3, val, rflags;
fd = get_bound_socket(type, SOCK_PATH_A, &sun);
val = 1;
if (setsockopt(fd, 0, LOCAL_CREDS, &val, sizeof(val)) != 0) e(0);
if (listen(fd, 1) != 0) e(0);
if ((fd2 = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != 0) e(0);
len = sizeof(sun);
if ((fd3 = accept(fd, (struct sockaddr *)&sun, &len)) < 0) e(0);
if (send(fd2, "A", 1, 0) != 1) e(0);
if (send(fd2, "B", 1, 0) != 1) e(0);
len = sizeof(sc);
if (recv_creds(fd3, buf, sizeof(buf), 0, &rflags, &sc, &len) != 1)
e(0);
if (buf[0] != 'A') e(0);
if (rflags != 0) e(0);
if (len != sizeof(sc)) e(0);
if (sc.sc_uid != getuid()) e(0);
if (sc.sc_euid != geteuid()) e(0);
len = sizeof(sc);
if (recv_creds(fd3, buf, sizeof(buf), 0, &rflags, &sc, &len) != 1)
e(0);
if (buf[0] != 'B') e(0);
if (rflags != 0) e(0);
if (len != 0) e(0);
if (send(fd3, "C", 1, 0) != 1) e(0);
val = 1;
if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) != 0) e(0);
if (send(fd3, "D", 1, 0) != 1) e(0);
val = 1;
if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) != 0) e(0);
if (send(fd3, "E", 1, 0) != 1) e(0);
len = sizeof(sc);
if (recv_creds(fd2, buf, sizeof(buf), 0, &rflags, &sc, &len) != 1)
e(0);
if (buf[0] != 'C') e(0);
if (rflags != 0) e(0);
if (len != 0) e(0);
len = sizeof(sc);
if (recv_creds(fd2, buf, sizeof(buf), 0, &rflags, &sc, &len) != 1)
e(0);
if (buf[0] != 'D') e(0);
if (rflags != 0) e(0);
if (len != sizeof(sc)) e(0);
if (sc.sc_uid != getuid()) e(0);
if (sc.sc_euid != geteuid()) e(0);
memset(&msg, 0, sizeof(msg));
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
if (recvmsg(fd2, &msg, 0) != 1) e(0);
if (buf[0] != 'E') e(0);
if (msg.msg_flags != MSG_CTRUNC) e(0);
if (close(fd2) != 0) e(0);
if (close(fd3) != 0) e(0);
val = 0;
if (setsockopt(fd, 0, LOCAL_CREDS, &val, sizeof(val)) != 0) e(0);
if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != 0) e(0);
len = sizeof(sun);
if ((fd3 = accept(fd, (struct sockaddr *)&sun, &len)) < 0) e(0);
if (send(fd2, "F", 1, 0) != 1) e(0);
len = sizeof(sc);
if (recv_creds(fd3, buf, sizeof(buf), 0, &rflags, &sc, &len) != 1)
e(0);
if (buf[0] != 'F') e(0);
if (rflags != 0) e(0);
if (len != 0) e(0);
if (close(fd) != 0) e(0);
if (close(fd2) != 0) e(0);
if (close(fd3) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
}
/*
* A few tests for credentials passing that matter to some applications:
* the credentials passing setting is inherited by accepted connections from
* their listening socket, and, credentials are passed only once on a
* connection-oriented socket.
*/
static void
test90m(void)
{
struct sockcred sc;
socklen_t len;
char buf[1];
int fd[2], val, rflags;
subtest = 13;
sub90m(SOCK_STREAM);
sub90m(SOCK_SEQPACKET);
get_socket_pair(SOCK_DGRAM, fd);
val = 1;
if (setsockopt(fd[0], 0, LOCAL_CREDS, &val, sizeof(val)) != 0) e(0);
if (send(fd[1], "A", 1, 0) != 1) e(0);
if (send(fd[0], "B", 1, 0) != 1) e(0);
if (send(fd[1], "C", 1, 0) != 1) e(0);
len = sizeof(sc);
if (recv_creds(fd[0], buf, sizeof(buf), 0, &rflags, &sc, &len) != 1)
e(0);
if (buf[0] != 'A') e(0);
if (rflags != 0) e(0);
if (len != sizeof(sc)) e(0);
if (sc.sc_uid != getuid()) e(0);
if (sc.sc_euid != geteuid()) e(0);
len = sizeof(sc);
if (recv_creds(fd[1], buf, sizeof(buf), 0, &rflags, &sc, &len) != 1)
e(0);
if (buf[0] != 'B') e(0);
if (rflags != 0) e(0);
if (len != 0) e(0);
len = sizeof(sc);
if (recv_creds(fd[0], buf, sizeof(buf), 0, &rflags, &sc, &len) != 1)
e(0);
if (buf[0] != 'C') e(0);
if (rflags != 0) e(0);
if (len != sizeof(sc)) e(0);
if (sc.sc_uid != getuid()) e(0);
if (sc.sc_euid != geteuid()) e(0);
if (close(fd[0]) != 0) e(0);
if (close(fd[1]) != 0) e(0);
}
/*
* Test whether MSG_CMSG_CLOEXEC is honored when copying in file descriptors.
* We do not bother to test with execve(2w); obtaining the FD flags suffices.
*/
static void
test90n(void)
{
char buf[1];
int i, fd[2], sfd, rfd, fl, rflags, nfds;
subtest = 14;
get_socket_pair(SOCK_STREAM, fd);
if ((sfd = open("/dev/null", O_RDONLY)) < 0) e(0);
if (send_fds(fd[0], "A", 1, 0, NULL, 0, &sfd, 1) != 1) e(0);
if (send_fds(fd[0], "B", 1, 0, NULL, 0, &sfd, 1) != 1) e(0);
if ((fl = fcntl(sfd, F_GETFD, 0)) < 0) e(0);
if (fcntl(sfd, F_SETFD, fl | FD_CLOEXEC) != 0) e(0);
if (send_fds(fd[0], "C", 1, 0, NULL, 0, &sfd, 1) != 1) e(0);
if (send_fds(fd[0], "D", 1, 0, NULL, 0, &sfd, 1) != 1) e(0);
for (i = 0; i < 4; i++) {
fl = (i & 1) ? MSG_CMSG_CLOEXEC : 0;
nfds = 1;
if (recv_fds(fd[1], buf, sizeof(buf), fl, &rflags, &rfd,
&nfds) != 1) e(0);
if (buf[0] != 'A' + i) e(0);
if (rflags != 0) e(0);
if (nfds != 1) e(0);
if ((fl = fcntl(rfd, F_GETFD, 0)) < 0) e(0);
if (!!(fl & FD_CLOEXEC) != (i & 1)) e(0);
if (close(rfd) != 0) e(0);
}
if (close(sfd) != 0) e(0);
if (close(fd[0]) != 0) e(0);
if (close(fd[1]) != 0) e(0);
}
/*
* Test failures sending and receiving sets of file descriptors.
*/
static void
sub90o(int type)
{
static int ofd[OPEN_MAX];
char buf[1];
int i, fd[2], sfd[2], rfd[2], rflags, nfds;
get_socket_pair(type, fd);
if ((sfd[0] = open("/dev/null", O_RDONLY)) < 0) e(0);
sfd[1] = -1;
if (send_fds(fd[0], "A", 1, 0, NULL, 0, &sfd[1], 1) != -1) e(0);
if (errno != EBADF) e(0);
if (send_fds(fd[0], "B", 1, 0, NULL, 0, &sfd[0], 2) != -1) e(0);
if (errno != EBADF) e(0);
if ((sfd[1] = dup(sfd[0])) < 0) e(0);
if (send_fds(fd[0], "C", 1, 0, NULL, 0, &sfd[0], 2) != 1) e(0);
for (i = 0; i < __arraycount(ofd); i++) {
if ((ofd[i] = dup(sfd[0])) < 0) {
/* Either will do. */
if (errno != EMFILE && errno != ENFILE) e(0);
break;
}
}
nfds = 2;
if (recv_fds(fd[1], buf, sizeof(buf), 0, &rflags, rfd, &nfds) != -1)
e(0);
if (errno != ENFILE && errno != EMFILE) e(0);
if (close(sfd[1]) != 0) e(0);
nfds = 2;
if (recv_fds(fd[1], buf, sizeof(buf), 0, &rflags, rfd, &nfds) != -1)
e(0);
if (errno != ENFILE && errno != EMFILE) e(0);
if (close(sfd[0]) != 0) e(0);
nfds = 2;
if (recv_fds(fd[1], buf, sizeof(buf), 0, &rflags, rfd, &nfds) != 1)
e(0);
if (buf[0] != 'C') e(0);
if (rflags != 0) e(0);
if (nfds != 2) e(0);
if (close(rfd[1]) != 0) e(0);
if (close(rfd[0]) != 0) e(0);
while (i-- > 0)
if (close(ofd[i]) != 0) e(0);
if (close(fd[1]) != 0) e(0);
if (close(fd[0]) != 0) e(0);
}
/*
* Test failures sending and receiving sets of file descriptors.
*/
static void
test90o(void)
{
const int types[] = { SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM };
int i;
subtest = 15;
for (i = 0; i < OPEN_MAX + 1; i++)
sub90o(types[i % __arraycount(types)]);
}
/*
* Test socket reuse for a particular socket type.
*/
static void
sub90p(int type)
{
struct sockaddr_un sunA, sunB, sunC;
socklen_t len;
char buf[1];
uid_t euid;
gid_t egid;
int fd, fd2, fd3, val;
if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0);
/* Unconnected. */
if (getpeereid(fd, &euid, &egid) != -1) e(0);
if (errno != ENOTCONN) e(0);
fd2 = get_bound_socket(type, SOCK_PATH_A, &sunA);
val = 1;
if (setsockopt(fd2, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0);
if (listen(fd2, 5) != 0) e(0);
if (connect(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != -1) e(0);
if (errno != EINPROGRESS) e(0);
/* Connecting. */
if (getpeereid(fd, &euid, &egid) != -1) e(0);
if (errno != ENOTCONN) e(0);
len = sizeof(sunB);
if ((fd3 = accept(fd2, (struct sockaddr *)&sunB, &len)) < 0) e(0);
/* Connected. */
len = sizeof(sunC);
if (getpeername(fd, (struct sockaddr *)&sunC, &len) != 0) e(0);
check_addr(&sunC, len, SOCK_PATH_A);
if (getpeereid(fd, &euid, &egid) != 0) e(0);
if (euid == -1 || egid == -1) e(0);
if (getpeereid(fd3, &euid, &egid) != 0) e(0);
if (euid == -1 || egid == -1) e(0);
if (send(fd3, "A", 1, 0) != 1) e(0);
if (send(fd3, "B", 1, 0) != 1) e(0);
if (send(fd3, "C", 1, 0) != 1) e(0);
if (close(fd3) != 0) e(0);
/* Disconnected. */
if (getpeereid(fd, &euid, &egid) != -1) e(0);
if (errno != ENOTCONN) e(0);
if (close(fd2) != 0) e(0);
fd2 = get_bound_socket(type, SOCK_PATH_B, &sunA);
if (listen(fd2, 5) != 0) e(0);
val = 1;
if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0);
if (connect(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != -1) e(0);
if (errno != EINPROGRESS) e(0);
/* Connecting. */
if (getpeereid(fd, &euid, &egid) != -1) e(0);
if (errno != ENOTCONN) e(0);
if (recv(fd, buf, 1, 0) != 1) e(0);
if (buf[0] != 'A') e(0);
len = sizeof(sunB);
if ((fd3 = accept(fd2, (struct sockaddr *)&sunB, &len)) < 0) e(0);
/* Connected. */
if (send(fd3, "D", 1, 0) != 1) e(0);
if (send(fd3, "E", 1, 0) != 1) e(0);
len = sizeof(sunC);
if (getpeername(fd, (struct sockaddr *)&sunC, &len) != 0) e(0);
check_addr(&sunC, len, SOCK_PATH_B);
if (getpeereid(fd, &euid, &egid) != 0) e(0);
if (euid == -1 || egid == -1) e(0);
if (close(fd2) != 0) e(0);
if (unlink(SOCK_PATH_B) != 0) e(0);
if (close(fd3) != 0) e(0);
/* Disconnected. */
if (connect(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != -1) e(0);
if (errno != ENOENT) e(0);
/* Unconnected. */
if (getpeereid(fd, &euid, &egid) != -1) e(0);
if (errno != ENOTCONN) e(0);
if (recv(fd, buf, 1, 0) != 1) e(0);
if (buf[0] != 'B') e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
if (bind(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
if (recv(fd, buf, 1, 0) != 1) e(0);
if (buf[0] != 'C') e(0);
val = 0;
if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0);
if (listen(fd, 1) != 0) e(0);
/* Listening. */
if (recv(fd, buf, 1, 0) != 1) e(0);
if (buf[0] != 'D') e(0);
if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
len = sizeof(sunC);
if ((fd3 = accept(fd, (struct sockaddr *)&sunC, &len)) < 0) e(0);
if (send(fd2, "F", 1, 0) != 1) e(0);
if (recv(fd3, buf, 1, 0) != 1) e(0);
if (buf[0] != 'F') e(0);
if (recv(fd, buf, 1, 0) != 1) e(0);
if (buf[0] != 'E') e(0);
if (recv(fd, buf, 1, 0) != -1) e(0);
if (errno != ENOTCONN) e(0);
/* It should be possible to obtain peer credentials now. */
if (getpeereid(fd2, &euid, &egid) != 0) e(0);
if (euid == -1 || egid == -1) e(0);
if (getpeereid(fd3, &euid, &egid) != 0) e(0);
if (euid == -1 || egid == -1) e(0);
if (close(fd3) != 0) e(0);
if (close(fd2) != 0) e(0);
if (close(fd) != 0) e(0);
/* Closed. */
if (unlink(SOCK_PATH_B) != 0) e(0);
if ((fd = socket(AF_UNIX, type, 0)) < 0) e(0);
/* Unconnected. */
fd2 = get_bound_socket(type, SOCK_PATH_A, &sunA);
if (listen(fd2, 5) != 0) e(0);
if (connect(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
/* Connected. */
len = sizeof(sunB);
if ((fd3 = accept(fd2, (struct sockaddr *)&sunB, &len)) < 0) e(0);
if (close(fd2) != 0) e(0);
memset(&sunB, 0, sizeof(sunB));
sunB.sun_family = AF_UNIX;
strlcpy(sunB.sun_path, SOCK_PATH_B, sizeof(sunB.sun_path));
if (bind(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != 0) e(0);
if (close(fd3) != 0) e(0);
/* Disconnected. */
if (listen(fd, 1) != 0) e(0);
/* Listening. */
if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sunB, sizeof(sunB)) != 0) e(0);
len = sizeof(sunC);
if ((fd3 = accept(fd, (struct sockaddr *)&sunC, &len)) < 0) e(0);
/* It should NOT be possible to obtain peer credentials now. */
if (getpeereid(fd2, &euid, &egid) != -1) e(0);
if (errno != EINVAL) e(0);
if (getpeereid(fd3, &euid, &egid) != 0) e(0);
if (euid == -1 || egid == -1) e(0);
if (close(fd3) != 0) e(0);
if (close(fd2) != 0) e(0);
if (close(fd) != 0) e(0);
/* Closed. */
if (unlink(SOCK_PATH_A) != 0) e(0);
if (unlink(SOCK_PATH_B) != 0) e(0);
}
/*
* Test socket reuse, receiving left-overs in the receive buffer, and the
* (in)ability to obtain peer credentials.
*/
static void
test90p(void)
{
subtest = 16;
sub90p(SOCK_STREAM);
sub90p(SOCK_SEQPACKET);
}
/*
* Test state changes and errors related to connected datagram sockets.
*/
static void
test90q(void)
{
struct sockaddr_un sunA, sunB, sunC, sunD;
socklen_t len;
char buf[1];
int fd, fd2, fd3, val;
subtest = 17;
/*
* Sending a datagram to a datagram socket connected elsewhere should
* fail explicitly (specifically, EPERM).
*/
fd = get_bound_socket(SOCK_DGRAM, SOCK_PATH_A, &sunA);
fd2 = get_bound_socket(SOCK_DGRAM, SOCK_PATH_B, &sunB);
fd3 = get_bound_socket(SOCK_DGRAM, SOCK_PATH_C, &sunC);
if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
if (sendto(fd3, "A", 1, 0, (struct sockaddr *)&sunB,
sizeof(sunB)) != -1) e(0);
if (errno != EPERM) e(0);
/* Similarly, connecting to such a socket should fail. */
if (connect(fd3, (struct sockaddr *)&sunB, sizeof(sunB)) != -1) e(0);
if (errno != EPERM) e(0);
if (send(fd2, "B", 1, 0) != 1) e(0);
if (recv(fd, buf, 1, 0) != 1) e(0);
if (buf[0] != 'B') e(0);
/* Reconnection of a socket's target should result in ECONNRESET. */
if (connect(fd, (struct sockaddr *)&sunC, sizeof(sunC)) != 0) e(0);
len = sizeof(val);
if (getsockopt(fd2, SOL_SOCKET, SO_ERROR, &val, &len) != 0) e(0);
if (val != ECONNRESET) e(0);
if (send(fd2, "C", 1, 0) != -1) e(0);
if (errno != EDESTADDRREQ) e(0);
if (send(fd, "D", 1, 0) != 1) e(0);
len = sizeof(sunD);
if (recvfrom(fd3, buf, 1, 0, (struct sockaddr *)&sunD, &len) != 1)
e(0);
if (buf[0] != 'D') e(0);
check_addr(&sunD, len, SOCK_PATH_A);
if (connect(fd2, (struct sockaddr *)&sunC, sizeof(sunC)) != 0) e(0);
if (connect(fd3, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
memset(&sunD, 0, sizeof(sunD));
sunD.sun_family = AF_UNIX;
strlcpy(sunD.sun_path, SOCK_PATH_D, sizeof(sunD.sun_path));
/* A failed reconnection attempt should not break the previous one. */
if (connect(fd3, (struct sockaddr *)&sunD, sizeof(sunD)) != -1) e(0);
if (errno != ENOENT) e(0);
/* The destination address should be ignored here. */
if (sendto(fd3, "E", 1, 0, (struct sockaddr *)&sunB,
sizeof(sunB)) != 1) e(0);
if (recv(fd2, buf, 1, 0) != -1) e(0);
if (errno != ECONNRESET) e(0);
if (recv(fd, buf, 1, 0) != 1) e(0);
if (buf[0] != 'E') e(0);
if (close(fd3) != 0) e(0);
len = sizeof(val);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len) != 0) e(0);
if (val != ECONNRESET) e(0);
if (close(fd2) != 0) e(0);
len = sizeof(val);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len) != 0) e(0);
if (val != 0) e(0);
if (close(fd) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
if (unlink(SOCK_PATH_B) != 0) e(0);
if (unlink(SOCK_PATH_C) != 0) e(0);
/*
* Finally, test unconnecting sockets.
*/
fd = get_bound_socket(SOCK_DGRAM, SOCK_PATH_A, &sunA);
fd2 = get_bound_socket(SOCK_DGRAM, SOCK_PATH_B, &sunB);
if (connect(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != 0) e(0);
if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
if (sendto(fd2, "F", 1, 0, NULL, 0) != 1) e(0);
if (recv(fd, buf, 1, 0) != 1) e(0);
if (buf[0] != 'F') e(0);
memset(&sunC, 0, sizeof(sunC));
sunC.sun_family = AF_UNSPEC;
if (connect(fd2, (struct sockaddr *)&sunC, sizeof(sunC)) != 0) e(0);
len = sizeof(val);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len) != 0) e(0);
if (val != 0) e(0);
if (send(fd, "G", 1, 0) != 1) e(0);
if (sendto(fd2, "H", 1, 0, NULL, 0) != -1) e(0);
if (errno != EDESTADDRREQ) e(0);
if (sendto(fd2, "I", 1, 0, (struct sockaddr *)&sunA, sizeof(sunA)) != 1)
e(0);
if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
if (sendto(fd2, "J", 1, 0, NULL, 0) != 1) e(0);
if (recv(fd, buf, 1, 0) != 1) e(0);
if (buf[0] != 'I') e(0);
if (recv(fd, buf, 1, 0) != 1) e(0);
if (buf[0] != 'J') e(0);
if (recv(fd2, buf, 1, 0) != 1) e(0);
if (buf[0] != 'G') e(0);
if (close(fd) != 0) e(0);
if (close(fd2) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
if (unlink(SOCK_PATH_B) != 0) e(0);
}
/*
* Test socket file name reuse.
*/
static void
test90r(void)
{
struct sockaddr_un sun;
socklen_t len;
int fd, fd2, fd3, fd4;
subtest = 18;
fd = get_bound_socket(SOCK_STREAM | SOCK_NONBLOCK, SOCK_PATH_A, &sun);
if (rename(SOCK_PATH_A, SOCK_PATH_B) != 0) e(0);
if ((fd3 = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) e(0);
if (connect(fd3, (struct sockaddr *)&sun, sizeof(sun)) != -1) e(0);
if (errno != ENOENT) e(0);
if (listen(fd, 1) != 0) e(0);
len = sizeof(sun);
if (getsockname(fd, (struct sockaddr *)&sun, &len) != 0) e(0);
check_addr(&sun, len, SOCK_PATH_A);
fd2 = get_bound_socket(SOCK_STREAM | SOCK_NONBLOCK, SOCK_PATH_A, &sun);
if (listen(fd2, 1) != 0) e(0);
len = sizeof(sun);
if (getsockname(fd2, (struct sockaddr *)&sun, &len) != 0) e(0);
check_addr(&sun, len, SOCK_PATH_A);
len = sizeof(sun);
if (getsockname(fd, (struct sockaddr *)&sun, &len) != 0) e(0);
check_addr(&sun, len, SOCK_PATH_A);
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strlcpy(sun.sun_path, SOCK_PATH_B, sizeof(sun.sun_path));
if (connect(fd3, (struct sockaddr *)&sun, sizeof(sun)) != 0) e(0);
len = sizeof(sun);
if ((fd4 = accept(fd2, (struct sockaddr *)&sun, &len)) >= 0) e(0);
if (errno != EWOULDBLOCK) e(0);
if ((fd4 = accept(fd, (struct sockaddr *)&sun, &len)) < 0) e(0);
len = sizeof(sun);
if (getpeername(fd3, (struct sockaddr *)&sun, &len) != 0) e(0);
check_addr(&sun, len, SOCK_PATH_A);
if (close(fd) != 0) e(0);
if (close(fd2) != 0) e(0);
if (close(fd3) != 0) e(0);
if (close(fd4) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
if (unlink(SOCK_PATH_B) != 0) e(0);
}
/*
* Test that non-canonized path names are accepted and returned.
* Also test datagram send errors on disconnect.
*/
static void
test90s(void)
{
struct sockaddr_un sun;
socklen_t len;
int fd, fd2;
subtest = 19;
fd = get_bound_socket(SOCK_DGRAM, SOCK_PATH_A_X, &sun);
if ((fd2 = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) e(0);
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strlcpy(sun.sun_path, SOCK_PATH_A_Y, sizeof(sun.sun_path));
if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != 0) e(0);
len = sizeof(sun);
if (getpeername(fd2, (struct sockaddr *)&sun, &len) != 0) e(0);
check_addr(&sun, len, SOCK_PATH_A_X);
if (send(fd2, "A", 1, 0) != 1) e(0);
if (close(fd) != 0) e(0);
if (send(fd2, "B", 1, 0) != -1) e(0);
if (errno != ECONNRESET) e(0);
if (send(fd2, "B", 1, 0) != -1) e(0);
if (errno != EDESTADDRREQ) e(0);
if (close(fd2) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
}
/*
* Test basic sysctl(2) socket enumeration for a specific socket type.
*/
static void
sub90t(int type, const char * path)
{
struct kinfo_pcb *ki;
size_t i, len, oldlen;
int fd, mib[8];
if ((fd = socket(AF_UNIX, type, 0)) < 0) e(0);
memset(mib, 0, sizeof(mib));
len = __arraycount(mib);
if (sysctlnametomib(path, mib, &len) != 0) e(0);
if (len != 4) e(0);
if (sysctl(mib, __arraycount(mib), NULL, &oldlen, NULL, 0) != 0) e(0);
if (oldlen == 0) e(0);
if (oldlen % sizeof(*ki)) e(0);
if ((ki = (struct kinfo_pcb *)malloc(oldlen)) == NULL) e(0);
if (sysctl(mib, __arraycount(mib), ki, &oldlen, NULL, 0) != 0) e(0);
if (oldlen == 0) e(0);
if (oldlen % sizeof(*ki)) e(0);
/*
* We cannot check a whole lot of things, because we have no way of
* knowing which is the socket we created. Check some basics and leave
* it at that. This subtest is mostly trying to guarantee that
* netstat(1) will not show nothing, anyway.
*/
for (i = 0; i < oldlen / sizeof(*ki); i++) {
if (ki[i].ki_pcbaddr == 0) e(0);
if (ki[i].ki_sockaddr == 0) e(0);
if (ki[i].ki_family != AF_UNIX) e(0);
if (ki[i].ki_type != type) e(0);
if (ki[i].ki_protocol != 0) e(0);
}
free(ki);
if (close(fd) != 0) e(0);
}
/*
* Test basic sysctl(2) socket enumeration support.
*/
static void
test90t(void)
{
subtest = 20;
/*
* We test that for each of the socket types, when we create a socket,
* we can find at least one socket of that type in the respective
* sysctl(2) out.
*/
sub90t(SOCK_STREAM, "net.local.stream.pcblist");
sub90t(SOCK_SEQPACKET, "net.local.seqpacket.pcblist");
sub90t(SOCK_DGRAM, "net.local.dgram.pcblist");
}
/*
* Cause a pending recv() call to return. Here 'fd' is the file descriptor
* identifying the other end of the socket pair. If breaking the recv()
* requires sending data, 'data' and 'len' identify the data that should be
* sent. Return 'fd' if it is still open, or -1 if it is closed.
*/
static int
break_uds_recv(int fd, const char * data, size_t len)
{
int fd2;
/*
* This UDS-specific routine makes the recv() in one of two ways
* depending on whether the recv() call already made partial progress:
* if it did, this send call creates a segment boundary which should
* cut short the current receive call. If it did not, the send call
* will simply satisfy the receive call with regular data.
*/
if ((fd2 = open("/dev/null", O_RDONLY)) < 0) e(0);
if (send_fds(fd, data, len, 0, NULL, 0, &fd2, 1) != len) e(0);
if (close(fd2) != 0) e(0);
return fd;
}
/*
* Test for receiving on stream sockets. In particular, test SO_RCVLOWAT,
* MSG_PEEK, MSG_DONTWAIT, and MSG_WAITALL.
*/
static void
test90u(void)
{
subtest = 21;
socklib_stream_recv(socketpair, AF_UNIX, SOCK_STREAM, break_uds_recv);
}
#define MAX_BYTES 2 /* set to 3 for slightly better(?) testing */
#define USLEEP_TIME 250000 /* increase on wimpy platforms if needed */
/*
* Signal handler which just needs to exist, so that invoking it will interrupt
* an ongoing system call.
*/
static void
test90_got_signal(int sig __unused)
{
/* Nothing. */
}
/*
* Test for sending on stream sockets. The quick summary here is that send()
* should basically act as the mirror of recv(MSG_WAITALL), i.e., it should
* keep suspending until all data is sent (or the call is interrupted or no
* more can possibly be sent), and, SO_SNDLOWAT, mirroring SO_RCVLOWAT, acts as
* an admission test for the send: nothing is sent until there is room in the
* send buffer (i.e., the peer's receive buffer) for at least the low send
* watermark, or the whole send request length, whichever is smaller. In
* addition, select(2) should use the same threshold.
*/
static void
sub90v(int iroom, int istate, int slowat, int len, int bits, int act)
{
const char *data = "ABC"; /* this limits MAX_BYTES to 3 */
struct sigaction sa;
struct timeval tv;
fd_set fds;
char buf[2], *sndbuf;
pid_t pid;
int fd[2], rcvlen, min, flags, res, err;
int pfd[2], eroom, tstate, fl, status;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) e(0);
/*
* Set up the initial condition on the sockets.
*/
rcvlen = get_rcvbuf_len(fd[1]);
if (rcvlen <= iroom) e(0);
rcvlen -= iroom;
if ((sndbuf = malloc(rcvlen)) == NULL) e(0);
memset(sndbuf, 'X', rcvlen);
if (send(fd[0], sndbuf, rcvlen, 0) != rcvlen) e(0);
free(sndbuf);
switch (istate) {
case 0: break;
case 1: if (shutdown(fd[0], SHUT_WR) != 0) e(0); break;
case 2: if (shutdown(fd[1], SHUT_RD) != 0) e(0); break;
case 3: if (close(fd[1]) != 0) e(0); break;
}
if (setsockopt(fd[0], SOL_SOCKET, SO_SNDLOWAT, &slowat,
sizeof(slowat)) != 0) e(0);
/* SO_SNDLOWAT is always bounded by the actual send length. */
min = MIN(len, slowat);
flags = MSG_NOSIGNAL;
if (bits & 1) flags |= MSG_DONTWAIT;
/*
* Do a quick select test to see if its result indeed matches whether
* the available space in the "send" buffer meets the threshold.
*/
FD_ZERO(&fds);
FD_SET(fd[0], &fds);
tv.tv_sec = 0;
tv.tv_usec = 0;
res = select(fd[0] + 1, NULL, &fds, NULL, &tv);
if (res < 0 || res > 1) e(0);
if (res != (iroom >= slowat || istate > 0)) e(0);
if (res == 1 && !FD_ISSET(fd[0], &fds)) e(0);
/*
* Cut short a whole lot of cases, to avoid the overhead of forking,
* namely when we know the call should return immediately. This is the
* case when the socket state disallows further sending, or when all
* data could be sent, or when the call was non-blocking. The low
* send watermark only helps determine whether anything was sent here.
*/
if (istate > 0 || iroom >= len || (flags & MSG_DONTWAIT)) {
res = send(fd[0], data, len, flags);
if (istate > 0) {
if (res != -1) e(0);
if (errno != EPIPE) e(0);
} else if (iroom >= len) {
if (res != len) e(0);
} else if (iroom >= min) {
if (res != iroom) e(0);
} else {
if (res != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
}
/* Early cleanup and return to avoid even more code clutter. */
if (istate != 3 && close(fd[1]) != 0) e(0);
if (close(fd[0]) != 0) e(0);
return;
}
/*
* Now starts the interesting stuff: the send call should now block,
* even though if we add MSG_DONTWAIT it may not return EWOULDBLOCK,
* because MSG_DONTWAIT prevents the send from blocking after partial
* completion. As such, we can only test our expectations by letting
* the call block, in a child process, and waiting. We do test as much
* of the above assumption as we can for safety right here, but this is
* not a substitute for actually blocking even in these cases!
*/
if (iroom < min) {
if (send(fd[0], data, len, flags | MSG_DONTWAIT) != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
}
/*
* If (act < 9), we receive 0, 1, or 2 bytes from the receive queue
* before forcing the send call to terminate in one of three ways.
*
* If (act == 9), we use a signal to interrupt the send call.
*/
if (act < 9) {
eroom = act % 3;
tstate = act / 3;
} else
eroom = tstate = 0;
if (pipe2(pfd, O_NONBLOCK) != 0) e(0);
pid = fork();
switch (pid) {
case 0:
errct = 0;
if (close(fd[1]) != 0) e(0);
if (close(pfd[0]) != 0) e(0);
if (act == 9) {
memset(&sa, 0, sizeof(sa));
sa.sa_handler = test90_got_signal;
if (sigaction(SIGUSR1, &sa, NULL) != 0) e(0);
}
res = send(fd[0], data, len, flags);
err = errno;
if (write(pfd[1], &res, sizeof(res)) != sizeof(res)) e(0);
if (write(pfd[1], &err, sizeof(err)) != sizeof(err)) e(0);
exit(errct);
case -1:
e(0);
}
if (close(pfd[1]) != 0) e(0);
/*
* Allow the child to enter the blocking send(2), and check the pipe
* to see if it is really blocked.
*/
if (usleep(USLEEP_TIME) != 0) e(0);
if (read(pfd[0], &res, sizeof(res)) != -1) e(0);
if (errno != EAGAIN) e(0);
if (eroom > 0) {
if (recv(fd[1], buf, eroom, 0) != eroom) e(0);
/*
* The threshold for the send is now met if the entire request
* has been satisfied.
*/
if (iroom + eroom >= len) {
if ((fl = fcntl(pfd[0], F_GETFL)) == -1) e(0);
if (fcntl(pfd[0], F_SETFL, fl & ~O_NONBLOCK) != 0)
e(0);
if (read(pfd[0], &res, sizeof(res)) != sizeof(res))
e(0);
if (read(pfd[0], &err, sizeof(err)) != sizeof(err))
e(0);
if (res != len) e(0);
/* Bail out. */
goto cleanup;
}
}
if (act < 9) {
/*
* Now test various ways to terminate the send call. Ideally
* we would also like to have a case that raises a socket error
* here, but with UDS there is currently no way to do that.
*/
switch (tstate) {
case 0: if (shutdown(fd[0], SHUT_WR) != 0) e(0); break;
case 1: if (shutdown(fd[1], SHUT_RD) != 0) e(0); break;
case 2: if (close(fd[1]) != 0) e(0); fd[1] = -1; break;
}
} else
if (kill(pid, SIGUSR1) != 0) e(0);
if ((fl = fcntl(pfd[0], F_GETFL)) == -1) e(0);
if (fcntl(pfd[0], F_SETFL, fl & ~O_NONBLOCK) != 0) e(0);
if (read(pfd[0], &res, sizeof(res)) != sizeof(res)) e(0);
if (read(pfd[0], &err, sizeof(err)) != sizeof(err)) e(0);
/*
* If the send met the threshold before being terminate or interrupted,
* we should at least have sent something. Otherwise, the send was
* never admitted and should return EPIPE (if the send was terminated)
* or EINTR (if the child was killed).
*/
if (iroom + eroom >= min) {
if (res != MIN(iroom + eroom, len)) e(0);
} else {
if (res != -1) e(0);
if (act < 9) {
if (err != EPIPE) e(0);
} else
if (err != EINTR) e(0);
}
cleanup:
if (close(pfd[0]) != 0) e(0);
if (wait(&status) != pid) e(0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0);
if (fd[1] != -1 && close(fd[1]) != 0) e(0);
if (close(fd[0]) != 0) e(0);
}
/*
* Test for sending on stream sockets. In particular, test SO_SNDLOWAT and
* MSG_DONTWAIT.
*/
static void
test90v(void)
{
int iroom, istate, slowat, len, bits, act;
subtest = 22;
/* Insanity. */
for (iroom = 0; iroom <= MAX_BYTES; iroom++)
for (istate = 0; istate <= 3; istate++)
for (slowat = 1; slowat <= MAX_BYTES; slowat++)
for (len = 1; len <= MAX_BYTES; len++)
for (bits = 0; bits < 2; bits++)
for (act = 0; act <= 9; act++)
sub90v(iroom, istate,
slowat, len, bits,
act);
}
/*
* Test that SO_RCVLOWAT is limited to the size of the receive buffer.
*/
static void
sub90w_recv(int fill_delta, int rlowat_delta, int exp_delta)
{
char *buf;
int fd[2], rcvlen, fill, rlowat, res;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) e(0);
rcvlen = get_rcvbuf_len(fd[0]);
if ((buf = malloc(rcvlen + 1)) == NULL) e(0);
fill = rcvlen + fill_delta;
rlowat = rcvlen + rlowat_delta;
memset(buf, 0, fill);
if (send(fd[1], buf, fill, 0) != fill) e(0);
if (setsockopt(fd[0], SOL_SOCKET, SO_RCVLOWAT, &rlowat,
sizeof(rlowat)) != 0) e(0);
res = recv(fd[0], buf, rcvlen + 1, MSG_DONTWAIT);
if (exp_delta < 0) {
if (res != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
} else
if (res != rcvlen - exp_delta) e(0);
free(buf);
if (close(fd[0]) != 0) e(0);
if (close(fd[1]) != 0) e(0);
}
/*
* Test that SO_SNDLOWAT is limited to the size of the "send" buffer.
*/
static void
sub90w_send(int fill, int slowat_delta, int exp_delta)
{
char *buf;
socklen_t len;
int fd[2], sndlen, slowat, res;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) e(0);
len = sizeof(sndlen);
if (getsockopt(fd[0], SOL_SOCKET, SO_SNDBUF, &sndlen, &len) != 0) e(0);
if (len != sizeof(sndlen)) e(0);
if ((buf = malloc(sndlen + 1)) == NULL) e(0);
slowat = sndlen + slowat_delta;
if (fill > 0) {
memset(buf, 0, fill);
if (send(fd[0], buf, fill, 0) != fill) e(0);
}
if (setsockopt(fd[0], SOL_SOCKET, SO_SNDLOWAT, &slowat,
sizeof(slowat)) != 0) e(0);
res = send(fd[0], buf, sndlen + 1, MSG_DONTWAIT);
if (exp_delta < 0) {
if (res != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
} else
if (res != sndlen - exp_delta) e(0);
free(buf);
if (close(fd[0]) != 0) e(0);
if (close(fd[1]) != 0) e(0);
}
/*
* Test that on stream sockets, SO_RCVLOWAT and SO_SNDLOWAT are limited to
* their respective buffer sizes.
*/
static void
test90w(void)
{
subtest = 23;
/*
* With the receive buffer filled except for one byte, all data should
* be retrieved unless the threshold is not met.
*/
sub90w_recv(-1, -1, 1);
sub90w_recv(-1, 0, -1);
sub90w_recv(-1, 1, -1);
/*
* With the receive buffer filled completely, all data should be
* retrieved in all cases.
*/
sub90w_recv(0, -1, 0);
sub90w_recv(0, 0, 0);
sub90w_recv(0, 1, 0);
/*
* With a "send" buffer that contains one byte, all data should be sent
* unless the threshold is not met.
*/
sub90w_send(1, -1, 1);
sub90w_send(1, 0, -1);
sub90w_send(1, 1, -1);
/*
* With the "send" buffer filled completely, all data should be sent
* in all cases.
*/
sub90w_send(0, -1, 0);
sub90w_send(0, 0, 0);
sub90w_send(0, 1, 0);
}
/*
* Test shutdown on listening sockets.
*/
static void
sub90x(int type, int how, int connwait)
{
struct sockaddr_un sun;
socklen_t len;
char buf[1];
int fd, fd2, fd3, val, fl;
subtest = 24;
fd = get_bound_socket(type, SOCK_PATH_A, &sun);
if (listen(fd, 5) != 0) e(0);
if (!connwait) {
if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != 0)
e(0);
} else {
val = 1;
if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0)
e(0);
if ((fd2 = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != -1)
e(0);
if (errno != EINPROGRESS) e(0);
}
if (shutdown(fd, how) != 0) e(0);
len = sizeof(sun);
if ((fd3 = accept(fd, (struct sockaddr *)&sun, &len)) < 0) e(0);
if (write(fd2, "A", 1) != 1) e(0);
if (read(fd3, buf, 1) != 1) e(0);
if (buf[0] != 'A') e(0);
if (write(fd3, "B", 1) != 1) e(0);
if (read(fd2, buf, 1) != 1) e(0);
if (buf[0] != 'B') e(0);
len = sizeof(sun);
if (accept(fd, (struct sockaddr *)&sun, &len) != -1) e(0);
if (errno != ECONNABORTED) e(0);
/*
* Strangely, both NetBSD and Linux (yes, my two reference platforms)
* return EWOULDBLOCK from non-blocking accept(2) calls even though
* they always return ECONNABORTED when blocking. For consistency and
* select(2), we always return ECONNABORTED.
*/
if ((fl = fcntl(fd, F_GETFL)) == -1) e(0);
if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) != 0) e(0);
len = sizeof(sun);
if (accept(fd, (struct sockaddr *)&sun, &len) != -1) e(0);
if (errno != ECONNABORTED) e(0);
if (fcntl(fd, F_SETFL, fl) != 0) e(0);
if (close(fd3) != 0) e(0);
if (close(fd2) != 0) e(0);
if ((fd2 = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != -1) e(0);
if (errno != ECONNREFUSED) e(0);
len = sizeof(sun);
if (accept(fd, (struct sockaddr *)&sun, &len) != -1) e(0);
if (errno != ECONNABORTED) e(0);
if (close(fd2) != 0) e(0);
if (close(fd) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
}
/*
* Test shutdown on listening sockets. Pending connections should still be
* acceptable (and not inherit the shutdown flags), but new connections must be
* refused, and the accept call must no longer ever block.
*/
static void
test90x(void)
{
const int types[] = { SOCK_STREAM, SOCK_SEQPACKET };
const int hows[] = { SHUT_RD, SHUT_WR, SHUT_RDWR };
unsigned int i, j, k;
for (i = 0; i < __arraycount(types); i++)
for (j = 0; j < __arraycount(hows); j++)
for (k = 0; k <= 1; k++)
sub90x(types[i], hows[j], k);
}
/*
* Test accepting connections without LOCAL_CONNWAIT for the given socket type.
*/
static void
sub90y(int type)
{
struct sockaddr_un sunA, sunB, sunC;
socklen_t len;
struct timeval tv;
fd_set fds;
char buf[7];
uid_t uid;
gid_t gid;
int fd, fd2, fd3, fd4, val;
fd = get_bound_socket(type | SOCK_NONBLOCK, SOCK_PATH_A, &sunA);
len = sizeof(val);
if (getsockopt(fd, 0, LOCAL_CONNWAIT, &val, &len) != 0) e(0);
if (len != sizeof(val)) e(0);
if (val != 0) e(0);
if (listen(fd, 5) != 0) e(0);
/*
* Any socket options should be inherited from the listening socket at
* connect time, and not be re-inherited at accept time. It does not
* really matter what socket option we set here, as long as it is
* supposed to be inherited.
*/
val = 123;
if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)) != 0)
e(0);
fd2 = get_bound_socket(type, SOCK_PATH_B, &sunB);
if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
val = 456;
if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)) != 0)
e(0);
/*
* Obtaining the peer name should work. As always, the name should be
* inherited from the listening socket.
*/
len = sizeof(sunC);
if (getpeername(fd2, (struct sockaddr *)&sunC, &len) != 0) e(0);
check_addr(&sunC, len, SOCK_PATH_A);
/*
* Obtaining peer credentials should work. This is why NetBSD obtains
* the peer credentials at bind time, not at accept time.
*/
if (getpeereid(fd2, &uid, &gid) != 0) e(0);
if (uid != geteuid()) e(0);
if (gid != getegid()) e(0);
/*
* Sending to the socket should work, and it should be possible to
* receive the data from the other side once accepted.
*/
if (send(fd2, "Hello, ", 7, 0) != 7) e(0);
if (send(fd2, "world!", 6, 0) != 6) e(0);
/* Shutdown settings should be visible after accepting, too. */
if (shutdown(fd2, SHUT_RDWR) != 0) e(0);
len = sizeof(sunB);
if ((fd3 = accept(fd, (struct sockaddr *)&sunB, &len)) < 0) e(0);
check_addr(&sunB, len, SOCK_PATH_B);
len = sizeof(val);
if (getsockopt(fd3, SOL_SOCKET, SO_SNDLOWAT, &val, &len) != 0) e(0);
if (len != sizeof(val)) e(0);
if (val != 123) e(0);
if (recv(fd3, buf, 7, 0) != 7) e(0);
if (memcmp(buf, "Hello, ", 7) != 0) e(0);
if (recv(fd3, buf, 7, 0) != 6) e(0);
if (memcmp(buf, "world!", 6) != 0) e(0);
if (recv(fd3, buf, sizeof(buf), 0) != 0) e(0);
if (send(fd3, "X", 1, MSG_NOSIGNAL) != -1) e(0);
if (errno != EPIPE) e(0);
if (close(fd2) != 0) e(0);
if (close(fd3) != 0) e(0);
if (unlink(SOCK_PATH_B) != 0) e(0);
/*
* If the socket pending acceptance is closed, the listening socket
* should pretend as though the connection was never there.
*/
if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
FD_ZERO(&fds);
FD_SET(fd, &fds);
tv.tv_sec = 0;
tv.tv_usec = 0;
if (select(fd + 1, &fds, NULL, NULL, &tv) != 1) e(0);
if (!FD_ISSET(fd, &fds)) e(0);
if (close(fd2) != 0) e(0);
if (select(fd + 1, &fds, NULL, NULL, &tv) != 0) e(0);
if (FD_ISSET(fd, &fds)) e(0);
len = sizeof(sunB);
if (accept(fd, (struct sockaddr *)&sunB, &len) != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
/*
* Try the same thing, but now with the connection sandwiched between
* two different pending connections, which should be left intact.
*/
if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
if (send(fd2, "A", 1, 0) != 1) e(0);
if ((fd3 = socket(AF_UNIX, type, 0)) < 0) e(0);
if (connect(fd3, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
if (send(fd3, "B", 1, 0) != 1) e(0);
if ((fd4 = socket(AF_UNIX, type, 0)) < 0) e(0);
if (connect(fd4, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
if (send(fd4, "C", 1, 0) != 1) e(0);
if (close(fd3) != 0) e(0);
len = sizeof(sunB);
if ((fd3 = accept(fd, (struct sockaddr *)&sunB, &len)) < 0) e(0);
if (recv(fd3, buf, sizeof(buf), 0) != 1) e(0);
if (buf[0] != 'A') e(0);
if (close(fd3) != 0) e(0);
if (close(fd2) != 0) e(0);
FD_ZERO(&fds);
FD_SET(fd, &fds);
tv.tv_sec = 0;
tv.tv_usec = 0;
if (select(fd + 1, &fds, NULL, NULL, &tv) != 1) e(0);
if (!FD_ISSET(fd, &fds)) e(0);
len = sizeof(sunB);
if ((fd3 = accept(fd, (struct sockaddr *)&sunB, &len)) < 0) e(0);
if (recv(fd3, buf, sizeof(buf), 0) != 1) e(0);
if (buf[0] != 'C') e(0);
if (close(fd3) != 0) e(0);
if (close(fd4) != 0) e(0);
if (select(fd + 1, &fds, NULL, NULL, &tv) != 0) e(0);
if (FD_ISSET(fd, &fds)) e(0);
len = sizeof(sunB);
if (accept(fd, (struct sockaddr *)&sunB, &len) != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
/*
* If the listening socket is closed, the socket pending acceptance
* should be reset. We actually rely on this behavior in the sweep
* test, but we test this with more than one socket this time.
*/
if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
if ((fd3 = socket(AF_UNIX, type, 0)) < 0) e(0);
if (connect(fd3, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0);
if (close(fd) != 0) e(0);
if (recv(fd2, buf, sizeof(buf), 0) != -1) e(0);
if (errno != ECONNRESET) e(0);
if (recv(fd2, buf, sizeof(buf), 0) != 0) e(0);
if (recv(fd3, buf, sizeof(buf), 0) != -1) e(0);
if (errno != ECONNRESET) e(0);
if (recv(fd3, buf, sizeof(buf), 0) != 0) e(0);
if (close(fd3) != 0) e(0);
if (close(fd2) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
}
/*
* Test accepting connections without LOCAL_CONNWAIT. Since both the old UDS
* service and the initial version of the new UDS service supported only the
* LOCAL_CONNWAIT behavior, the alternative (which is now the default, as it is
* on other platforms) has been a bit under-tested so far.
*/
static void
test90y(void)
{
subtest = 25;
sub90y(SOCK_STREAM);
sub90y(SOCK_SEQPACKET);
}
/*
* Test that SO_LINGER has no effect on sockets of the given type.
*/
static void
sub90z(int type)
{
struct sockaddr_un sun;
socklen_t len;
struct linger l;
char buf[1];
int fd, fd2, fd3;
fd = get_bound_socket(type, SOCK_PATH_A, &sun);
if (listen(fd, 1) != 0) e(0);
if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0);
if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != 0) e(0);
len = sizeof(sun);
if ((fd3 = accept(fd, (struct sockaddr *)&sun, &len)) < 0) e(0);
if (close(fd) != 0) e(0);
if (send(fd2, "A", 1, 0) != 1) e(0);
l.l_onoff = 1;
l.l_linger = 0;
if (setsockopt(fd2, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) != 0) e(0);
if (close(fd2) != 0) e(0);
if (recv(fd3, buf, sizeof(buf), 0) != 1) e(0);
if (buf[0] != 'A') e(0);
/* We should not get ECONNRESET now. */
if (recv(fd3, buf, sizeof(buf), 0) != 0) e(0);
if (close(fd3) != 0) e(0);
if (unlink(SOCK_PATH_A) != 0) e(0);
}
/*
* Test that SO_LINGER has no effect on UNIX domain sockets. In particular, a
* timeout of zero does not cause the connection to be reset forcefully.
*/
static void
test90z(void)
{
subtest = 26;
sub90z(SOCK_STREAM);
sub90z(SOCK_SEQPACKET);
}
/*
* Test program for UDS.
*/
int
main(int argc, char ** argv)
{
int i, m;
start(90);
if (argc == 2)
m = atoi(argv[1]);
else
m = 0xFFFFFFF;
for (i = 0; i < ITERATIONS; i++) {
if (m & 0x0000001) test90a();
if (m & 0x0000002) test90b();
if (m & 0x0000004) test90c();
if (m & 0x0000008) test90d();
if (m & 0x0000010) test90e();
if (m & 0x0000020) test90f();
if (m & 0x0000040) test90g();
if (m & 0x0000080) test90h();
if (m & 0x0000100) test90i();
if (m & 0x0000200) test90j();
if (m & 0x0000400) test90k();
if (m & 0x0000800) test90l();
if (m & 0x0001000) test90m();
if (m & 0x0002000) test90n();
if (m & 0x0004000) test90o();
if (m & 0x0008000) test90p();
if (m & 0x0010000) test90q();
if (m & 0x0020000) test90r();
if (m & 0x0040000) test90s();
if (m & 0x0080000) test90t();
if (m & 0x0100000) test90u();
if (m & 0x0200000) test90v();
if (m & 0x0400000) test90w();
if (m & 0x0800000) test90x();
if (m & 0x1000000) test90y();
if (m & 0x2000000) test90z();
}
quit();
/* NOTREACHED */
}