/* 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 #include #include #include #include #include #include #include #include #include #include #include #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 */ }