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