aboutsummaryrefslogtreecommitdiff
path: root/tools/regression/sockets
diff options
context:
space:
mode:
Diffstat (limited to 'tools/regression/sockets')
-rw-r--r--tools/regression/sockets/accept_fd_leak/Makefile7
-rw-r--r--tools/regression/sockets/accept_fd_leak/accept_fd_leak.c213
-rw-r--r--tools/regression/sockets/accept_fd_leak/accept_fd_leak.t9
-rw-r--r--tools/regression/sockets/accf_data_attach/Makefile7
-rw-r--r--tools/regression/sockets/accf_data_attach/accf_data_attach.c297
-rw-r--r--tools/regression/sockets/accf_data_attach/accf_data_attach.t9
-rw-r--r--tools/regression/sockets/fstat/Makefile7
-rw-r--r--tools/regression/sockets/fstat/fstat.c69
-rw-r--r--tools/regression/sockets/kqueue/Makefile7
-rw-r--r--tools/regression/sockets/kqueue/kqueue.c366
-rw-r--r--tools/regression/sockets/kqueue/kqueue.t9
-rw-r--r--tools/regression/sockets/listen_backlog/Makefile4
-rw-r--r--tools/regression/sockets/listen_backlog/listen_backlog.c381
-rw-r--r--tools/regression/sockets/listen_kqueue/Makefile4
-rw-r--r--tools/regression/sockets/listen_kqueue/listen_kqueue.c112
-rw-r--r--tools/regression/sockets/listenclose/Makefile7
-rw-r--r--tools/regression/sockets/listenclose/listenclose.c109
-rw-r--r--tools/regression/sockets/pr_atomic/Makefile4
-rw-r--r--tools/regression/sockets/pr_atomic/pr_atomic.c107
-rw-r--r--tools/regression/sockets/reconnect/Makefile7
-rw-r--r--tools/regression/sockets/reconnect/reconnect.c131
-rw-r--r--tools/regression/sockets/rtsocket/Makefile4
-rw-r--r--tools/regression/sockets/rtsocket/rtsocket.c99
-rw-r--r--tools/regression/sockets/sblock/Makefile7
-rw-r--r--tools/regression/sockets/sblock/sblock.c205
-rw-r--r--tools/regression/sockets/sendfile/Makefile8
-rw-r--r--tools/regression/sockets/sendfile/sendfile.c481
-rw-r--r--tools/regression/sockets/shutdown/Makefile7
-rw-r--r--tools/regression/sockets/shutdown/shutdown.c108
-rw-r--r--tools/regression/sockets/sigpipe/Makefile7
-rw-r--r--tools/regression/sockets/sigpipe/sigpipe.c320
-rw-r--r--tools/regression/sockets/so_setfib/Makefile13
-rw-r--r--tools/regression/sockets/so_setfib/so_setfib.c198
-rw-r--r--tools/regression/sockets/udp_pingpong/Makefile4
-rw-r--r--tools/regression/sockets/udp_pingpong/udp_pingpong.c621
-rw-r--r--tools/regression/sockets/unix_bindconnect/Makefile4
-rw-r--r--tools/regression/sockets/unix_bindconnect/unix_bindconnect.c316
-rw-r--r--tools/regression/sockets/unix_close_race/Makefile4
-rw-r--r--tools/regression/sockets/unix_close_race/unix_close_race.c141
-rw-r--r--tools/regression/sockets/unix_cmsg/Makefile37
-rw-r--r--tools/regression/sockets/unix_cmsg/README159
-rw-r--r--tools/regression/sockets/unix_cmsg/t_cmsg_len.c135
-rw-r--r--tools/regression/sockets/unix_cmsg/t_cmsg_len.h30
-rw-r--r--tools/regression/sockets/unix_cmsg/t_cmsgcred.c136
-rw-r--r--tools/regression/sockets/unix_cmsg/t_cmsgcred.h28
-rw-r--r--tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.c122
-rw-r--r--tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.h28
-rw-r--r--tools/regression/sockets/unix_cmsg/t_generic.c70
-rw-r--r--tools/regression/sockets/unix_cmsg/t_generic.h28
-rw-r--r--tools/regression/sockets/unix_cmsg/t_peercred.c150
-rw-r--r--tools/regression/sockets/unix_cmsg/t_peercred.h27
-rw-r--r--tools/regression/sockets/unix_cmsg/t_sockcred.c212
-rw-r--r--tools/regression/sockets/unix_cmsg/t_sockcred.h29
-rw-r--r--tools/regression/sockets/unix_cmsg/t_xxxtime.c.in160
-rw-r--r--tools/regression/sockets/unix_cmsg/t_xxxtime.h.in30
-rw-r--r--tools/regression/sockets/unix_cmsg/uc_check_time.c85
-rw-r--r--tools/regression/sockets/unix_cmsg/uc_check_time.h34
-rw-r--r--tools/regression/sockets/unix_cmsg/uc_common.c742
-rw-r--r--tools/regression/sockets/unix_cmsg/uc_common.h84
-rw-r--r--tools/regression/sockets/unix_cmsg/unix_cmsg.c449
-rw-r--r--tools/regression/sockets/unix_cmsg/unix_cmsg.t87
-rw-r--r--tools/regression/sockets/unix_gc/Makefile4
-rw-r--r--tools/regression/sockets/unix_gc/unix_gc.c806
-rw-r--r--tools/regression/sockets/unix_sendtorace/Makefile4
-rw-r--r--tools/regression/sockets/unix_sendtorace/unix_sendtorace.c213
-rw-r--r--tools/regression/sockets/unix_socket/Makefile4
-rw-r--r--tools/regression/sockets/unix_socket/unix_socket.c82
-rw-r--r--tools/regression/sockets/unix_sorflush/Makefile4
-rw-r--r--tools/regression/sockets/unix_sorflush/unix_sorflush.c97
-rw-r--r--tools/regression/sockets/zerosend/Makefile4
-rw-r--r--tools/regression/sockets/zerosend/zerosend.c288
71 files changed, 8781 insertions, 0 deletions
diff --git a/tools/regression/sockets/accept_fd_leak/Makefile b/tools/regression/sockets/accept_fd_leak/Makefile
new file mode 100644
index 000000000000..2614e92a1d17
--- /dev/null
+++ b/tools/regression/sockets/accept_fd_leak/Makefile
@@ -0,0 +1,7 @@
+#
+#
+
+PROG= accept_fd_leak
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/accept_fd_leak/accept_fd_leak.c b/tools/regression/sockets/accept_fd_leak/accept_fd_leak.c
new file mode 100644
index 000000000000..f1de967a9f39
--- /dev/null
+++ b/tools/regression/sockets/accept_fd_leak/accept_fd_leak.c
@@ -0,0 +1,213 @@
+/*-
+ * Copyright (c) 2004 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define BIND_ATTEMPTS 10
+#define LOOPS 500
+#define NUM_ATTEMPTS 1000
+
+static volatile int quit;
+
+static void
+child_died(int sig __unused)
+{
+
+ quit = 1;
+}
+
+/*
+ * This test is intended to detect a leak of a file descriptor in the process
+ * following a failed non-blocking accept. It measures an available fd
+ * baseline, then performs 1000 failing accepts, then checks to see what the
+ * next fd is. It relies on sequential fd allocation, and will test for it
+ * briefly before beginning (not 100% reliable, but a good start).
+ */
+int
+main(void)
+{
+ struct sockaddr_in sin;
+ socklen_t size;
+ pid_t child;
+ int fd1, fd2, fd3, i, listen_port, s, status;
+
+ printf("1..2\n");
+
+ /*
+ * Check for sequential fd allocation, and give up early if not.
+ */
+ fd1 = dup(STDIN_FILENO);
+ fd2 = dup(STDIN_FILENO);
+ if (fd2 != fd1 + 1)
+ errx(-1, "Non-sequential fd allocation\n");
+
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if (s == -1)
+ errx(-1, "socket: %s", strerror(errno));
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ srandomdev();
+
+ for (i = 0; i < BIND_ATTEMPTS; i++) {
+ /* Pick a random unprivileged port 1025-65535 */
+ listen_port = MAX((int)random() % 65535, 1025);
+ sin.sin_port = htons(listen_port);
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0)
+ break;
+ warn("bind with %d failed", listen_port);
+ usleep(1000);
+ }
+ if (i >= BIND_ATTEMPTS) {
+ printf("Bail out!\n");
+ exit(1);
+ }
+
+ if (listen(s, -1) != 0)
+ errx(-1, "listen: %s", strerror(errno));
+
+ i = fcntl(s, F_GETFL);
+ if (i == -1)
+ errx(-1, "ioctl(F_GETFL): %s", strerror(errno));
+ i |= O_NONBLOCK;
+ if (fcntl(s, F_SETFL, i) != 0)
+ errx(-1, "ioctl(F_SETFL): %s", strerror(errno));
+ i = fcntl(s, F_GETFL);
+ if (i == -1)
+ errx(-1, "ioctl(F_GETFL): %s", strerror(errno));
+ if ((i & O_NONBLOCK) != O_NONBLOCK)
+ errx(-1, "Failed to set O_NONBLOCK (i=0x%x)\n", i);
+
+ for (i = 0; i < LOOPS; i++) {
+ size = sizeof(sin);
+ if (accept(s, (struct sockaddr *)&sin, &size) != -1)
+ errx(-1, "accept succeeded\n");
+ if (errno != EAGAIN)
+ errx(-1, "accept: %s", strerror(errno));
+ }
+
+ /*
+ * Allocate a file descriptor and make sure it's fd2+2. 2 because
+ * we allocate an fd for the socket.
+ */
+ fd3 = dup(STDIN_FILENO);
+ if (fd3 != fd2 + 2)
+ printf("not ok 1 - (%d, %d, %d)\n", fd1, fd2, fd3);
+ else
+ printf("ok 1\n");
+
+ /*
+ * Try failing accept's w/o non-blocking where the destination
+ * address pointer is invalid.
+ */
+ close(fd3);
+ signal(SIGCHLD, child_died);
+ child = fork();
+ if (child < 0)
+ errx(-1, "fork: %s", strerror(errno));
+
+ /*
+ * Child process does `NUM_ATTEMPTS` connects.
+ */
+ if (child == 0) {
+ close(fd1);
+ close(fd2);
+ close(s);
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(listen_port);
+
+ for (i = 0; i < NUM_ATTEMPTS; i++) {
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if (s == -1)
+ errx(-1, "socket: %s", strerror(errno));
+ if (connect(s, (struct sockaddr *)&sin,
+ sizeof(sin)) < 0)
+ errx(-1, "connect: %s", strerror(errno));
+ close(s);
+ }
+ _exit(0);
+ }
+
+ /* Reset back to a blocking socket. */
+ i = fcntl(s, F_GETFL);
+ if (i == -1)
+ errx(-1, "ioctl(F_GETFL): %s", strerror(errno));
+ i &= ~O_NONBLOCK;
+ if (fcntl(s, F_SETFL, i) != 0)
+ errx(-1, "ioctl(F_SETFL): %s", strerror(errno));
+ i = fcntl(s, F_GETFL);
+ if (i == -1)
+ errx(-1, "ioctl(F_GETFL): %s", strerror(errno));
+ if (i & O_NONBLOCK)
+ errx(-1, "Failed to clear O_NONBLOCK (i=0x%x)\n", i);
+
+ /* Do `NUM_ATTEMPTS` accepts with an invalid pointer. */
+ for (i = 0; !quit && i < NUM_ATTEMPTS; i++) {
+ size = sizeof(sin);
+ if (accept(s, (struct sockaddr *)(uintptr_t)(0x100),
+ &size) != -1)
+ errx(-1, "accept succeeded\n");
+ if (errno != EFAULT)
+ errx(-1, "accept: %s", strerror(errno));
+ }
+
+ if (waitpid(child, &status, 0) < 0)
+ errx(-1, "waitpid: %s", strerror(errno));
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ warnx("child process died");
+
+ /*
+ * Allocate a file descriptor and make sure it's fd2+2. 2 because
+ * we allocate an fd for the socket.
+ */
+ fd3 = dup(STDIN_FILENO);
+ if (fd3 != fd2 + 2)
+ printf("not ok 2 - (%d, %d, %d)\n", fd1, fd2, fd3);
+ else
+ printf("ok 2\n");
+
+ return (0);
+}
diff --git a/tools/regression/sockets/accept_fd_leak/accept_fd_leak.t b/tools/regression/sockets/accept_fd_leak/accept_fd_leak.t
new file mode 100644
index 000000000000..74eb8c9c581d
--- /dev/null
+++ b/tools/regression/sockets/accept_fd_leak/accept_fd_leak.t
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+cd `dirname $0`
+
+executable=`basename $0 .t`
+
+make $executable 2>&1 > /dev/null
+
+exec ./$executable
diff --git a/tools/regression/sockets/accf_data_attach/Makefile b/tools/regression/sockets/accf_data_attach/Makefile
new file mode 100644
index 000000000000..0df72455fa69
--- /dev/null
+++ b/tools/regression/sockets/accf_data_attach/Makefile
@@ -0,0 +1,7 @@
+#
+#
+
+PROG= accf_data_attach
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/accf_data_attach/accf_data_attach.c b/tools/regression/sockets/accf_data_attach/accf_data_attach.c
new file mode 100644
index 000000000000..68a8d278f825
--- /dev/null
+++ b/tools/regression/sockets/accf_data_attach/accf_data_attach.c
@@ -0,0 +1,297 @@
+/*-
+ * Copyright (c) 2004 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define ACCF_NAME "dataready"
+
+/*
+ * A number of small tests to confirm that attaching ACCF_DATA accept filters
+ * to inet4 ports works as expected. We test:
+ *
+ * - That no accept filter is attached on a newly created socket.
+ * - That bind() has no affect on the accept filter state.
+ * - That we can't attach an accept filter to a socket that isn't in the
+ * listen state.
+ * - That after we fail to attach the filter, querying the kernel shows no
+ * filter attached.
+ * - That we can attach an accept filter to a socket that is in the listen
+ * state.
+ * - That once an accept filter is attached, we can query to make sure it is
+ * attached.
+ * - That once an accept filter is attached, we can remove it and query to
+ * make sure it is removed.
+ */
+int
+main(void)
+{
+ struct accept_filter_arg afa;
+ struct sockaddr_in sin;
+ struct linger linger;
+ socklen_t len;
+ int lso, so, i, ret;
+
+ /* XXX: PLAIN_TEST_REQUIRE_MODULE "backport" for stable/9 */
+ const char *_mod_name = "accf_data";
+
+ if (modfind(_mod_name) == -1) {
+ printf("1..0 # SKIP - module %s could not be resolved: %s\n",
+ _mod_name, strerror(errno));
+ _exit(0);
+ }
+ /* XXX: PLAIN_TEST_REQUIRE_MODULE for stable/9 */
+
+ printf("1..12\n");
+
+ /*
+ * Step 0. Open socket().
+ */
+ lso = socket(PF_INET, SOCK_STREAM, 0);
+ if (lso == -1)
+ errx(-1, "not ok 1 - socket: %s", strerror(errno));
+ printf("ok 1 - socket\n");
+
+ /*
+ * Step 1. After socket(). Should return EINVAL, since no accept
+ * filter should be attached.
+ */
+ bzero(&afa, sizeof(afa));
+ len = sizeof(afa);
+ ret = getsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, &len);
+ if (ret != -1)
+ errx(-1, "not ok 2 - getsockopt() after socket() succeeded");
+ if (errno != EINVAL)
+ errx(-1, "not ok 2 - getsockopt() after socket() failed with "
+ "%d (%s)", errno, strerror(errno));
+ printf("ok 2 - getsockopt\n");
+
+ /*
+ * Step 2. Bind(). Ideally this will succeed.
+ */
+ setsockopt(lso, SOL_SOCKET, SO_REUSEADDR, &lso, sizeof(lso));
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(8080);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ if (bind(lso, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ errx(-1, "not ok 3 - bind %s", strerror(errno));
+ printf("ok 3 - bind\n");
+
+ /*
+ * Step 3: After bind(). getsockopt() should return EINVAL, since no
+ * accept filter should be attached.
+ */
+ len = sizeof(afa);
+ ret = getsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, &len);
+ if (ret != -1)
+ errx(-1, "not ok 4 - getsockopt() after bind() succeeded");
+ if (errno != EINVAL)
+ errx(-1, "not ok 4 - getsockopt() after bind() failed with %d (%s)",
+ errno, strerror(errno));
+ printf("ok 4 - getsockopt\n");
+
+ /*
+ * Step 4: Setsockopt() before listen(). Should fail, since it's not
+ * yet a listen() socket.
+ */
+ bzero(&afa, sizeof(afa));
+ strncpy(afa.af_name, ACCF_NAME, sizeof(afa.af_name));
+ ret = setsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
+ if (ret == 0)
+ errx(-1, "not ok 5 - setsockopt() before listen() succeeded");
+ printf("ok 5 - setsockopt\n");
+
+ /*
+ * Step 5: Getsockopt() after pre-listen() setsockopt(). Should
+ * fail with EINVAL, since setsockopt() should have failed.
+ */
+ len = sizeof(afa);
+ ret = getsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, &len);
+ if (ret == 0)
+ errx(-1, "not ok 6 - getsockopt() after pre-listen() setsockopt() "
+ "succeeded");
+ if (errno != EINVAL)
+ errx(-1, "not ok 6 - pre-listen() getsockopt() failed with %d (%s)",
+ errno, strerror(errno));
+ printf("ok 6 - getsockopt\n");
+
+ /*
+ * Step 6: listen().
+ */
+ if (listen(lso, -1) < 0)
+ errx(-1, "not ok 7 - listen: %s", strerror(errno));
+ printf("ok 7 - listen\n");
+
+ /*
+ * Step 7: Getsockopt() after listen(). Should fail with EINVAL,
+ * since we have not installed accept filter yet.
+ */
+ len = sizeof(afa);
+ ret = getsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, &len);
+ if (ret == 0)
+ errx(-1, "not ok 8 - getsockopt() after listen() but before "
+ "setsockopt() succeeded");
+ if (errno != EINVAL)
+ errx(-1, "not ok 8 - getsockopt() after listen() but before "
+ "setsockopt() failed with %d (%s)", errno, strerror(errno));
+ printf("ok 8 - getsockopt\n");
+
+ /*
+ * Step 8: After listen(). This call to setsockopt() should succeed.
+ */
+ bzero(&afa, sizeof(afa));
+ strncpy(afa.af_name, ACCF_NAME, sizeof(afa.af_name));
+ ret = setsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
+ if (ret != 0)
+ errx(-1, "not ok 9 - setsockopt() after listen() failed with %d "
+ "(%s)", errno, strerror(errno));
+ printf("ok 9 - setsockopt\n");
+
+ /*
+ * Step 9: After setsockopt(). Should succeed and identify
+ * ACCF_NAME.
+ */
+ bzero(&afa, sizeof(afa));
+ len = sizeof(afa);
+ ret = getsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, &len);
+ if (ret != 0)
+ errx(-1, "not ok 10 - getsockopt() after listen() setsockopt() "
+ "failed with %d (%s)", errno, strerror(errno));
+ if (len != sizeof(afa))
+ errx(-1, "not ok 10 - getsockopt() after setsockopet() after "
+ "listen() returned wrong size (got %d expected %zd)", len,
+ sizeof(afa));
+ if (strcmp(afa.af_name, ACCF_NAME) != 0)
+ errx(-1, "not ok 10 - getsockopt() after setsockopt() after "
+ "listen() mismatch (got %s expected %s)", afa.af_name,
+ ACCF_NAME);
+ printf("ok 10 - getsockopt\n");
+
+ /*
+ * Step 10: Set listening socket to non blocking mode. Open
+ * connection to our listening socket and try to accept. Should
+ * no succeed. Write a byte of data and try again. Should accept.
+ */
+ i = fcntl(lso, F_GETFL);
+ if (i < 0)
+ errx(-1, "not ok 11 - ioctl(F_GETFL): %s", strerror(errno));
+ i |= O_NONBLOCK;
+ if (fcntl(lso, F_SETFL, i) != 0)
+ errx(-1, "not ok 11 - ioctl(F_SETFL): %s", strerror(errno));
+ so = socket(PF_INET, SOCK_STREAM, 0);
+ if (so == -1)
+ errx(-1, "not ok 11 - socket: %s", strerror(errno));
+ if (connect(so, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ errx(-1, "not ok 11 - connect %s", strerror(errno));
+ if (accept(lso, NULL, 0) != -1 && errno != EWOULDBLOCK)
+ errx(-1, "not ok 11 - accept #1 %s", strerror(errno));
+ if (write(so, "0", 1) != 1)
+ errx(-1, "not ok 11 - write %s", strerror(errno));
+ /*
+ * XXXGL: ugly, but we need to make sure that our write reaches
+ * remote side of the socket.
+ */
+ usleep(10000);
+ if (accept(lso, NULL, 0) < 1)
+ errx(-1, "not ok 11 - accept #2 %s", strerror(errno));
+ if (close(so) != 0)
+ errx(-1, "not ok 11 - close(): %s", strerror(errno));
+ printf("ok 11 - accept\n");
+
+ /*
+ * Step 12: reset connection before accept filter allows it.
+ * In this case the connection must make it to the listen
+ * queue, but with ECONNABORTED code.
+ */
+ so = socket(PF_INET, SOCK_STREAM, 0);
+ if (so == -1)
+ errx(-1, "not ok 12 - socket: %s", strerror(errno));
+ if (connect(so, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ errx(-1, "not ok 12 - connect %s", strerror(errno));
+ linger.l_onoff = 1;
+ linger.l_linger = 0;
+ ret = setsockopt(so, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
+ if (ret != 0)
+ errx(-1, "not ok 12 - setsockopt(SO_LINGER) failed with %d "
+ "(%s)", errno, strerror(errno));
+ if (close(so) != 0)
+ errx(-1, "not ok 12 - close(): %s", strerror(errno));
+ if (accept(lso, NULL, 0) != -1 && errno != ECONNABORTED)
+ errx(-1, "not ok 12 - accept #3 %s", strerror(errno));
+ printf("ok 12 - accept\n");
+
+#if 1
+ /*
+ * XXXGL: this doesn't belong to the test itself, but is known
+ * to examine rarely examined paths in the kernel. Intentionally
+ * leave a socket on the incomplete queue, before the program
+ * exits.
+ */
+ so = socket(PF_INET, SOCK_STREAM, 0);
+ if (so == -1)
+ errx(-1, "not ok 13 - socket: %s", strerror(errno));
+ if (connect(so, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ errx(-1, "not ok 13 - connect %s", strerror(errno));
+#endif
+
+ /*
+ * Step 12: Remove accept filter. After removing the accept filter
+ * getsockopt() should fail with EINVAL.
+ */
+ ret = setsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0);
+ if (ret != 0)
+ errx(-1, "not ok 13 - setsockopt() after listen() "
+ "failed with %d (%s)", errno, strerror(errno));
+ bzero(&afa, sizeof(afa));
+ len = sizeof(afa);
+ ret = getsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, &len);
+ if (ret == 0)
+ errx(-1, "not ok 13 - getsockopt() after removing "
+ "the accept filter returns valid accept filter %s",
+ afa.af_name);
+ if (errno != EINVAL)
+ errx(-1, "not ok 13 - getsockopt() after removing the accept"
+ "filter failed with %d (%s)", errno, strerror(errno));
+ if (close(lso) != 0)
+ errx(-1, "not ok 13 - close() of listening socket: %s",
+ strerror(errno));
+ printf("ok 13 - setsockopt\n");
+
+ return (0);
+}
diff --git a/tools/regression/sockets/accf_data_attach/accf_data_attach.t b/tools/regression/sockets/accf_data_attach/accf_data_attach.t
new file mode 100644
index 000000000000..74eb8c9c581d
--- /dev/null
+++ b/tools/regression/sockets/accf_data_attach/accf_data_attach.t
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+cd `dirname $0`
+
+executable=`basename $0 .t`
+
+make $executable 2>&1 > /dev/null
+
+exec ./$executable
diff --git a/tools/regression/sockets/fstat/Makefile b/tools/regression/sockets/fstat/Makefile
new file mode 100644
index 000000000000..ee36f797b5e1
--- /dev/null
+++ b/tools/regression/sockets/fstat/Makefile
@@ -0,0 +1,7 @@
+#
+#
+
+PROG= fstat
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/fstat/fstat.c b/tools/regression/sockets/fstat/fstat.c
new file mode 100644
index 000000000000..be370e983089
--- /dev/null
+++ b/tools/regression/sockets/fstat/fstat.c
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2008 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <unistd.h>
+
+/*
+ * Basic test to make sure that fstat(2) returns success on various socket
+ * types. In the future we should also validate the fields, confirming
+ * expected results such as the effect of shutdown(2) on permissions, etc.
+ */
+
+static void
+dotest(int domain, int type, int protocol)
+{
+ struct stat sb;
+ int sock;
+
+ sock = socket(domain, type, protocol);
+ if (sock < 0)
+ err(-1, "socket(%d, %d, %d)", domain, type, protocol);
+
+ if (fstat(sock, &sb) < 0)
+ err(-1, "fstat on socket(%d, %d, %d)", domain, type,
+ protocol);
+
+ close(sock);
+}
+
+int
+main(void)
+{
+
+ dotest(PF_INET, SOCK_DGRAM, 0);
+ dotest(PF_INET, SOCK_STREAM, 0);
+ dotest(PF_INET6, SOCK_DGRAM, 0);
+ dotest(PF_INET6, SOCK_STREAM, 0);
+ dotest(PF_LOCAL, SOCK_DGRAM, 0);
+ dotest(PF_LOCAL, SOCK_STREAM, 0);
+
+ return (0);
+}
diff --git a/tools/regression/sockets/kqueue/Makefile b/tools/regression/sockets/kqueue/Makefile
new file mode 100644
index 000000000000..142330b97e6e
--- /dev/null
+++ b/tools/regression/sockets/kqueue/Makefile
@@ -0,0 +1,7 @@
+#
+#
+
+PROG= kqueue
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/kqueue/kqueue.c b/tools/regression/sockets/kqueue/kqueue.c
new file mode 100644
index 000000000000..bc75ce38e7c9
--- /dev/null
+++ b/tools/regression/sockets/kqueue/kqueue.c
@@ -0,0 +1,366 @@
+/*-
+ * Copyright (c) 2004 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static int curtest = 1;
+
+/*-
+ * This test uses UNIX domain socket pairs to perform some basic exercising
+ * of kqueue functionality on sockets. In particular, testing that for read
+ * and write filters, we see the correct detection of whether reads and
+ * writes should actually be able to occur.
+ *
+ * TODO:
+ * - Test read/write filters for listen/accept sockets.
+ * - Handle the XXXRW below regarding datagram sockets.
+ * - Test that watermark/buffer size "data" fields returned by kqueue are
+ * correct.
+ * - Check that kqueue does something sensible when the remote endpoing is
+ * closed.
+ */
+
+#define OK(testname) printf("ok %d - %s\n", curtest, testname); \
+ curtest++;
+
+static void
+fail(int error, const char *func, const char *socktype, const char *rest)
+{
+
+ printf("not ok %d\n", curtest);
+
+ if (socktype == NULL)
+ printf("# %s(): %s\n", func, strerror(error));
+ else if (rest == NULL)
+ printf("# %s(%s): %s\n", func, socktype,
+ strerror(error));
+ else
+ printf("# %s(%s, %s): %s\n", func, socktype, rest,
+ strerror(error));
+ exit(-1);
+}
+
+static void
+fail_assertion(const char *func, const char *socktype, const char *rest,
+ const char *assertion)
+{
+
+ printf("not ok %d - %s\n", curtest, assertion);
+
+ if (socktype == NULL)
+ printf("# %s(): assertion %s failed\n", func,
+ assertion);
+ else if (rest == NULL)
+ printf("# %s(%s): assertion %s failed\n", func,
+ socktype, assertion);
+ else
+ printf("# %s(%s, %s): assertion %s failed\n", func,
+ socktype, rest, assertion);
+ exit(-1);
+}
+
+/*
+ * Test read kevent on a socket pair: check to make sure endpoint 0 isn't
+ * readable when we start, then write to endpoint 1 and confirm that endpoint
+ * 0 is now readable. Drain the write, then check that it's not readable
+ * again. Use non-blocking kqueue operations and socket operations.
+ */
+static void
+test_evfilt_read(int kq, int fd[2], const char *socktype)
+{
+ struct timespec ts;
+ struct kevent ke;
+ ssize_t len;
+ char ch;
+ int i;
+
+ EV_SET(&ke, fd[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
+ if (kevent(kq, &ke, 1, NULL, 0, NULL) == -1)
+ fail(errno, "kevent", socktype, "EVFILT_READ, EV_ADD");
+ OK("EVFILT_READ, EV_ADD");
+
+ /*
+ * Confirm not readable to begin with, no I/O yet.
+ */
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ i = kevent(kq, NULL, 0, &ke, 1, &ts);
+ if (i == -1)
+ fail(errno, "kevent", socktype, "EVFILT_READ");
+ OK("EVFILT_READ");
+ if (i != 0)
+ fail_assertion("kevent", socktype, "EVFILT_READ",
+ "empty socket unreadable");
+ OK("empty socket unreadable");
+
+ /*
+ * Write a byte to one end.
+ */
+ ch = 'a';
+ len = write(fd[1], &ch, sizeof(ch));
+ if (len == -1)
+ fail(errno, "write", socktype, NULL);
+ OK("write one byte");
+ if (len != sizeof(ch))
+ fail_assertion("write", socktype, NULL, "write length");
+ OK("write one byte length");
+
+ /*
+ * Other end should now be readable.
+ */
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ i = kevent(kq, NULL, 0, &ke, 1, &ts);
+ if (i == -1)
+ fail(errno, "kevent", socktype, "EVFILT_READ");
+ OK("EVFILT_READ");
+ if (i != 1)
+ fail_assertion("kevent", socktype, "EVFILT_READ",
+ "non-empty socket unreadable");
+ OK("non-empty socket unreadable");
+
+ /*
+ * Read a byte to clear the readable state.
+ */
+ len = read(fd[0], &ch, sizeof(ch));
+ if (len == -1)
+ fail(errno, "read", socktype, NULL);
+ OK("read one byte");
+ if (len != sizeof(ch))
+ fail_assertion("read", socktype, NULL, "read length");
+ OK("read one byte length");
+
+ /*
+ * Now re-check for readability.
+ */
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ i = kevent(kq, NULL, 0, &ke, 1, &ts);
+ if (i == -1)
+ fail(errno, "kevent", socktype, "EVFILT_READ");
+ OK("EVFILT_READ");
+ if (i != 0)
+ fail_assertion("kevent", socktype, "EVFILT_READ",
+ "empty socket unreadable");
+ OK("empty socket unreadable");
+
+ EV_SET(&ke, fd[0], EVFILT_READ, EV_DELETE, 0, 0, NULL);
+ if (kevent(kq, &ke, 1, NULL, 0, NULL) == -1)
+ fail(errno, "kevent", socktype, "EVFILT_READ, EV_DELETE");
+ OK("EVFILT_READ, EV_DELETE");
+}
+
+static void
+test_evfilt_write(int kq, int fd[2], const char *socktype)
+{
+ struct timespec ts;
+ struct kevent ke;
+ ssize_t len;
+ char ch;
+ int i;
+
+ EV_SET(&ke, fd[0], EVFILT_WRITE, EV_ADD, 0, 0, NULL);
+ if (kevent(kq, &ke, 1, NULL, 0, NULL) == -1)
+ fail(errno, "kevent", socktype, "EVFILT_WRITE, EV_ADD");
+ OK("EVFILE_WRITE, EV_ADD");
+
+ /*
+ * Confirm writable to begin with, no I/O yet.
+ */
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ i = kevent(kq, NULL, 0, &ke, 1, &ts);
+ if (i == -1)
+ fail(errno, "kevent", socktype, "EVFILT_WRITE");
+ OK("EVFILE_WRITE");
+ if (i != 1)
+ fail_assertion("kevent", socktype, "EVFILT_WRITE",
+ "empty socket unwritable");
+ OK("empty socket unwritable");
+
+ /*
+ * Write bytes into the socket until we can't write anymore.
+ */
+ ch = 'a';
+ while ((len = write(fd[0], &ch, sizeof(ch))) == sizeof(ch)) {};
+ if (len == -1 && errno != EAGAIN && errno != ENOBUFS)
+ fail(errno, "write", socktype, NULL);
+ OK("write");
+ if (len != -1 && len != sizeof(ch))
+ fail_assertion("write", socktype, NULL, "write length");
+ OK("write length");
+
+ /*
+ * Check to make sure the socket is no longer writable.
+ */
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ i = kevent(kq, NULL, 0, &ke, 1, &ts);
+ if (i == -1)
+ fail(errno, "kevent", socktype, "EVFILT_WRITE");
+ OK("EVFILT_WRITE");
+ if (i != 0)
+ fail_assertion("kevent", socktype, "EVFILT_WRITE",
+ "full socket writable");
+ OK("full socket writable");
+
+ EV_SET(&ke, fd[0], EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
+ if (kevent(kq, &ke, 1, NULL, 0, NULL) == -1)
+ fail(errno, "kevent", socktype, "EVFILT_WRITE, EV_DELETE");
+ OK("EVFILT_WRITE, EV_DELETE");
+}
+
+/*
+ * Basic registration exercise for kqueue(2). Create several types/brands of
+ * sockets, and confirm that we can register for various events on them.
+ */
+int
+main(void)
+{
+ int kq, sv[2];
+
+ printf("1..49\n");
+
+ kq = kqueue();
+ if (kq == -1)
+ fail(errno, "kqueue", NULL, NULL);
+ OK("kqueue()");
+
+ /*
+ * Create a UNIX domain datagram socket, and attach/test/detach a
+ * read filter on it.
+ */
+ if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) == -1)
+ fail(errno, "socketpair", "PF_UNIX, SOCK_DGRAM", NULL);
+ OK("socketpair() 1");
+
+ if (fcntl(sv[0], F_SETFL, O_NONBLOCK) != 0)
+ fail(errno, "fcntl", "PF_UNIX, SOCK_DGRAM", "O_NONBLOCK");
+ OK("fcntl() 1");
+ if (fcntl(sv[1], F_SETFL, O_NONBLOCK) != 0)
+ fail(errno, "fcntl", "PF_UNIX, SOCK_DGRAM", "O_NONBLOCK");
+ OK("fnctl() 2");
+
+ test_evfilt_read(kq, sv, "PF_UNIX, SOCK_DGRAM");
+
+ if (close(sv[0]) == -1)
+ fail(errno, "close", "PF_UNIX/SOCK_DGRAM", "sv[0]");
+ OK("close() 1");
+ if (close(sv[1]) == -1)
+ fail(errno, "close", "PF_UNIX/SOCK_DGRAM", "sv[1]");
+ OK("close() 2");
+
+#if 0
+ /*
+ * XXXRW: We disable the write test in the case of datagram sockets,
+ * as kqueue can't tell when the remote socket receive buffer is
+ * full, whereas the UNIX domain socket implementation can tell and
+ * returns ENOBUFS.
+ */
+ /*
+ * Create a UNIX domain datagram socket, and attach/test/detach a
+ * write filter on it.
+ */
+ if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) == -1)
+ fail(errno, "socketpair", "PF_UNIX, SOCK_DGRAM", NULL);
+
+ if (fcntl(sv[0], F_SETFL, O_NONBLOCK) != 0)
+ fail(errno, "fcntl", "PF_UNIX, SOCK_DGRAM", "O_NONBLOCK");
+ if (fcntl(sv[1], F_SETFL, O_NONBLOCK) != 0)
+ fail(errno, "fcntl", "PF_UNIX, SOCK_DGRAM", "O_NONBLOCK");
+
+ test_evfilt_write(kq, sv, "PF_UNIX, SOCK_DGRAM");
+
+ if (close(sv[0]) == -1)
+ fail(errno, "close", "PF_UNIX/SOCK_DGRAM", "sv[0]");
+ if (close(sv[1]) == -1)
+ fail(errno, "close", "PF_UNIX/SOCK_DGRAM", "sv[1]");
+#endif
+
+ /*
+ * Create a UNIX domain stream socket, and attach/test/detach a
+ * read filter on it.
+ */
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1)
+ fail(errno, "socketpair", "PF_UNIX, SOCK_STREAM", NULL);
+ OK("socketpair() 2");
+
+ if (fcntl(sv[0], F_SETFL, O_NONBLOCK) != 0)
+ fail(errno, "fcntl", "PF_UNIX, SOCK_STREAM", "O_NONBLOCK");
+ OK("fcntl() 3");
+ if (fcntl(sv[1], F_SETFL, O_NONBLOCK) != 0)
+ fail(errno, "fcntl", "PF_UNIX, SOCK_STREAM", "O_NONBLOCK");
+ OK("fcntl() 4");
+
+ test_evfilt_read(kq, sv, "PF_UNIX, SOCK_STREAM");
+
+ if (close(sv[0]) == -1)
+ fail(errno, "close", "PF_UNIX/SOCK_STREAM", "sv[0]");
+ OK("close() 3");
+ if (close(sv[1]) == -1)
+ fail(errno, "close", "PF_UNIX/SOCK_STREAM", "sv[1]");
+ OK("close() 4");
+
+ /*
+ * Create a UNIX domain stream socket, and attach/test/detach a
+ * write filter on it.
+ */
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1)
+ fail(errno, "socketpair", "PF_UNIX, SOCK_STREAM", NULL);
+ OK("socketpair() 3");
+
+ if (fcntl(sv[0], F_SETFL, O_NONBLOCK) != 0)
+ fail(errno, "fcntl", "PF_UNIX, SOCK_STREAM", "O_NONBLOCK");
+ OK("fcntl() 5");
+ if (fcntl(sv[1], F_SETFL, O_NONBLOCK) != 0)
+ fail(errno, "fcntl", "PF_UNIX, SOCK_STREAM", "O_NONBLOCK");
+ OK("fcntl() 6");
+
+ test_evfilt_write(kq, sv, "PF_UNIX, SOCK_STREAM");
+
+ if (close(sv[0]) == -1)
+ fail(errno, "close", "PF_UNIX/SOCK_STREAM", "sv[0]");
+ OK("close() 5");
+ if (close(sv[1]) == -1)
+ fail(errno, "close", "PF_UNIX/SOCK_STREAM", "sv[1]");
+ OK("close() 6");
+
+ if (close(kq) == -1)
+ fail(errno, "close", "kq", NULL);
+ OK("close() 7");
+
+ return (0);
+}
diff --git a/tools/regression/sockets/kqueue/kqueue.t b/tools/regression/sockets/kqueue/kqueue.t
new file mode 100644
index 000000000000..74eb8c9c581d
--- /dev/null
+++ b/tools/regression/sockets/kqueue/kqueue.t
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+cd `dirname $0`
+
+executable=`basename $0 .t`
+
+make $executable 2>&1 > /dev/null
+
+exec ./$executable
diff --git a/tools/regression/sockets/listen_backlog/Makefile b/tools/regression/sockets/listen_backlog/Makefile
new file mode 100644
index 000000000000..c7414442582e
--- /dev/null
+++ b/tools/regression/sockets/listen_backlog/Makefile
@@ -0,0 +1,4 @@
+PROG= listen_backlog
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/listen_backlog/listen_backlog.c b/tools/regression/sockets/listen_backlog/listen_backlog.c
new file mode 100644
index 000000000000..843c36ad3543
--- /dev/null
+++ b/tools/regression/sockets/listen_backlog/listen_backlog.c
@@ -0,0 +1,381 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * This regression test is intended to validate that the backlog parameter
+ * set by listen() is properly set, can be retrieved using SO_LISTENQLIMIT,
+ * and that it can be updated by later calls to listen(). We also check that
+ * SO_LISTENQLIMIT cannot be set.
+ *
+ * Future things to test:
+ *
+ * - That if we change the value of kern.ipc.soacceptqueue, the limits really
+ * do change.
+ *
+ * - That limits are, approximately, enforced and implemented.
+ *
+ * - All this on multiple socket types -- i.e., PF_LOCAL.
+ *
+ * - That we also test SO_LISTENQLEN and SO_LISTENINCQLEN.
+ */
+
+/*
+ * We retrieve kern.ipc.soacceptqueue before running the tests in order to use a
+ * run-time set value of SOMAXCONN, rather than compile-time set. We assume
+ * that no other process will be simultaneously frobbing it, and these tests
+ * may fail if that assumption is not held.
+ */
+static int somaxconn;
+
+/*
+ * Retrieve the current socket listen queue limit using SO_LISTENQLIMIT.
+ */
+static int
+socket_get_backlog(int sock, int *backlogp, const char *testclass,
+ const char *test, const char *testfunc)
+{
+ socklen_t len;
+ int i;
+
+ len = sizeof(i);
+ if (getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &i, &len) < 0) {
+ warn("%s: %s: %s: socket_get_backlog: getsockopt("
+ "SOL_SOCKET, SO_LISTENQLIMIT)", testclass, test,
+ testfunc);
+ return (-1);
+ }
+
+ if (len != sizeof(i)) {
+ warnx("%s: %s: %s: socket_get_backlog: getsockopt("
+ "SOL_SOCKET, SO_LISTENQLIMIT): returned size %d",
+ testclass, test, testfunc, len);
+ return (-1);
+ }
+
+ *backlogp = i;
+
+ return (0);
+}
+
+/*
+ * Create a socket, check the queue limit on creation, perform a listen(),
+ * and make sure that the limit was set as expected by listen().
+ */
+static int
+socket_listen(int domain, int type, int protocol, int backlog,
+ int create_backlog_assertion, int listen_backlog_assertion, int *sockp,
+ const char *domainstring, const char *typestring, const char *testclass,
+ const char *test)
+{
+ int backlog_retrieved, sock;
+
+ sock = socket(domain, type, protocol);
+ if (sock < 0) {
+ warn("%s: %s: socket_listen: socket(%s, %s)", testclass,
+ test, domainstring, typestring);
+ close(sock);
+ return (-1);
+ }
+
+ if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
+ "socket_listen") < 0) {
+ close(sock);
+ return (-1);
+ }
+
+ if (backlog_retrieved != create_backlog_assertion) {
+ warnx("%s: %s: socket_listen: create backlog is %d not %d",
+ testclass, test, backlog_retrieved,
+ create_backlog_assertion);
+ close(sock);
+ return (-1);
+ }
+
+ if (listen(sock, backlog) < 0) {
+ warn("%s: %s: socket_listen: listen(, %d)", testclass, test,
+ backlog);
+ close(sock);
+ return (-1);
+ }
+
+ if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
+ "socket_listen") < 0) {
+ close(sock);
+ return (-1);
+ }
+
+ if (backlog_retrieved != listen_backlog_assertion) {
+ warnx("%s: %s: socket_listen: listen backlog is %d not %d",
+ testclass, test, backlog_retrieved,
+ listen_backlog_assertion);
+ close(sock);
+ return (-1);
+ }
+
+ *sockp = sock;
+ return (0);
+}
+
+/*
+ * This test creates sockets and tests default states before and after
+ * listen(). Specifically, we expect a queue limit of 0 before listen, and
+ * then various settings for after listen(). If the passed backlog was
+ * either < 0 or > somaxconn, it should be set to somaxconn; otherwise, the
+ * passed queue depth.
+ */
+static void
+test_defaults(void)
+{
+ int sock;
+
+ /*
+ * First pass. Confirm the default is 0. Listen with a backlog of
+ * 0 and confirm it gets set that way.
+ */
+ if (socket_listen(PF_INET, SOCK_STREAM, 0, 0, 0, 0, &sock, "PF_INET",
+ "SOCK_STREAM", "test_defaults", "default_0_listen_0") < 0)
+ exit(-1);
+ close(sock);
+
+ /*
+ * Second pass. Listen with a backlog of -1 and make sure it is set
+ * to somaxconn.
+ */
+ if (socket_listen(PF_INET, SOCK_STREAM, 0, -1, 0, somaxconn, &sock,
+ "PF_INET", "SOCK_STREAM", "test_defaults", "default_0_listen_-1")
+ < 0)
+ exit(-1);
+ close(sock);
+
+ /*
+ * Third pass. Listen with a backlog of 1 and make sure it is set to
+ * 1.
+ */
+ if (socket_listen(PF_INET, SOCK_STREAM, 0, 1, 0, 1, &sock, "PF_INET",
+ "SOCK_STREAM", "test_defaults", "default_0_listen_1") < 0)
+ exit(-1);
+ close(sock);
+
+ /*
+ * Fourth pass. Listen with a backlog of somaxconn and make sure it
+ * is set to somaxconn.
+ */
+ if (socket_listen(PF_INET, SOCK_STREAM, 0, somaxconn, 0, somaxconn,
+ &sock, "PF_INET", "SOCK_STREAM", "test_defaults",
+ "default_0_listen_somaxconn") < 0)
+ exit(-1);
+ close(sock);
+
+ /*
+ * Fifth pass. Listen with a backlog of somaxconn+1 and make sure it
+ * is set to somaxconn.
+ */
+ if (socket_listen(PF_INET, SOCK_STREAM, 0, somaxconn+1, 0, somaxconn,
+ &sock, "PF_INET", "SOCK_STREAM", "test_defaults",
+ "default_0_listen_somaxconn+1") < 0)
+ exit(-1);
+ close(sock);
+}
+
+/*
+ * Create a socket, set the initial listen() state, then update the queue
+ * depth using listen(). Check that the backlog is as expected after both
+ * the first and second listen().
+ */
+static int
+socket_listen_update(int domain __unused, int type __unused,
+ int protocol __unused, int backlog,
+ int update_backlog, int listen_backlog_assertion,
+ int update_backlog_assertion, int *sockp, const char *domainstring,
+ const char *typestring, const char *testclass, const char *test)
+{
+ int backlog_retrieved, sock;
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ warn("%s: %s: socket_listen_update: socket(%s, %s)",
+ testclass, test, domainstring, typestring);
+ return (-1);
+ }
+
+ if (listen(sock, backlog) < 0) {
+ warn("%s: %s: socket_listen_update: initial listen(, %d)",
+ testclass, test, backlog);
+ close(sock);
+ return (-1);
+ }
+
+ if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
+ "socket_listen_update") < 0) {
+ close(sock);
+ return (-1);
+ }
+
+ if (backlog_retrieved != listen_backlog_assertion) {
+ warnx("%s: %s: socket_listen_update: initial backlog is %d "
+ "not %d", testclass, test, backlog_retrieved,
+ listen_backlog_assertion);
+ close(sock);
+ return (-1);
+ }
+
+ if (listen(sock, update_backlog) < 0) {
+ warn("%s: %s: socket_listen_update: update listen(, %d)",
+ testclass, test, update_backlog);
+ close(sock);
+ return (-1);
+ }
+
+ if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
+ "socket_listen_update") < 0) {
+ close(sock);
+ return (-1);
+ }
+
+ if (backlog_retrieved != update_backlog_assertion) {
+ warnx("%s: %s: socket_listen_update: updated backlog is %d "
+ "not %d", testclass, test, backlog_retrieved,
+ update_backlog_assertion);
+ close(sock);
+ return (-1);
+ }
+
+ *sockp = sock;
+ return (0);
+}
+
+/*
+ * This test tests using listen() to update the queue depth after a socket
+ * has already been marked as listening. We test several cases: setting the
+ * socket < 0, 0, 1, somaxconn, and somaxconn + 1.
+ */
+static void
+test_listen_update(void)
+{
+ int sock;
+
+ /*
+ * Set to 5, update to -1, which should give somaxconn.
+ */
+ if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, -1, 5, somaxconn,
+ &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
+ "update_5,-1") < 0)
+ exit(-1);
+ close(sock);
+
+ /*
+ * Set to 5, update to 0, which should give 0.
+ */
+ if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, 0, 5, 0, &sock,
+ "PF_INET", "SOCK_STREAM", "test_listen_update", "update_5,0")
+ < 0)
+ exit(-1);
+ close(sock);
+
+ /*
+ * Set to 5, update to 1, which should give 1.
+ */
+ if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, 1, 5, 1, &sock,
+ "PF_INET", "SOCK_STREAM", "test_listen_update", "update_5,1")
+ < 0)
+ exit(-1);
+ close(sock);
+
+ /*
+ * Set to 5, update to somaxconn, which should give somaxconn.
+ */
+ if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, somaxconn, 5,
+ somaxconn, &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
+ "update_5,somaxconn") < 0)
+ exit(-1);
+ close(sock);
+
+ /*
+ * Set to 5, update to somaxconn+1, which should give somaxconn.
+ */
+ if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, somaxconn+1, 5,
+ somaxconn, &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
+ "update_5,somaxconn+1") < 0)
+ exit(-1);
+ close(sock);
+}
+
+/*
+ * SO_LISTENQLIMIT is a read-only socket option, so make sure we get an error
+ * if we try to write it.
+ */
+static void
+test_set_qlimit(void)
+{
+ int i, ret, sock;
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ err(-1, "test_set_qlimit: socket(PF_INET, SOCK_STREAM)");
+
+ i = 0;
+ ret = setsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &i, sizeof(i));
+ if (ret < 0 && errno != ENOPROTOOPT) {
+ warn("test_set_qlimit: setsockopt(SOL_SOCKET, "
+ "SO_LISTENQLIMIT, 0): unexpected error");
+ close(sock);
+ }
+
+ if (ret == 0) {
+ warnx("test_set_qlimit: setsockopt(SOL_SOCKET, "
+ "SO_LISTENQLIMIT, 0) succeeded");
+ close(sock);
+ exit(-1);
+ }
+ close(sock);
+}
+
+int
+main(void)
+{
+ size_t len;
+
+ len = sizeof(somaxconn);
+ if (sysctlbyname("kern.ipc.soacceptqueue", &somaxconn, &len, NULL, 0)
+ < 0)
+ err(-1, "sysctlbyname(kern.ipc.soacceptqueue)");
+
+ test_defaults();
+ test_listen_update();
+ test_set_qlimit();
+
+ return (0);
+}
diff --git a/tools/regression/sockets/listen_kqueue/Makefile b/tools/regression/sockets/listen_kqueue/Makefile
new file mode 100644
index 000000000000..e67350d9ff14
--- /dev/null
+++ b/tools/regression/sockets/listen_kqueue/Makefile
@@ -0,0 +1,4 @@
+PROG= listen_kqueue
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/listen_kqueue/listen_kqueue.c b/tools/regression/sockets/listen_kqueue/listen_kqueue.c
new file mode 100644
index 000000000000..29de5f7745a0
--- /dev/null
+++ b/tools/regression/sockets/listen_kqueue/listen_kqueue.c
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 2017 Hartmut Brandt <harti@FreeBSD.org>
+ * Copyright (c) 2017 Gleb Smirnoff <glebius@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/event.h>
+#include <sys/filio.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * This regression test is against a scenario when a socket is first added
+ * to a kqueue, and only then is put into listening state.
+ * This weird scenario was made a valid one in r313043, and shouldn't be
+ * broken later.
+ */
+
+int
+main()
+{
+ struct sockaddr_in addr;
+ struct kevent ev[2];
+ socklen_t socklen;
+ int kq, sock, opt, pid, nev, fd;
+
+ if ((kq = kqueue()) == -1)
+ err(1, "kqueue");
+
+ if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
+ err(1, "socket");
+
+ EV_SET(&ev[0], sock, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, NULL);
+ EV_SET(&ev[1], sock, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, NULL);
+
+ opt = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)) == -1)
+ err(1, "setsockopt");
+
+ if (kevent(kq, ev, 2, NULL, 0, NULL) == -1)
+ err(1, "kevent");
+
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
+ err(1, "setsockopt");
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(10000);
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1)
+ err(1, "bind");
+ if (listen(sock, 0x80) == -1)
+ err(1, "listen");
+
+ if (ioctl(sock, FIONBIO, &opt) == -1)
+ err(1, "ioctl(FIONBIO)");
+
+ if (kevent(kq, ev, 2, NULL, 0, NULL) == -1)
+ err(1, "kevent");
+
+ pid = fork();
+ if (pid == -1)
+ err(1, "fork");
+ if (pid == 0) {
+ if (close(sock) == -1)
+ err(1, "close");
+ if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
+ err(1, "socket");
+ if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1)
+ err(1, "connect");
+ } else {
+ nev = kevent(kq, NULL, 0, ev, 2, NULL);
+ if (nev < 1)
+ err(1, "kevent");
+ for (int i = 0; i < nev; ++i) {
+ if (ev[i].ident == (uintptr_t )sock) {
+ fd = accept(ev[i].ident,
+ (struct sockaddr *)&addr, &socklen);
+ if (fd == -1)
+ err(1, "accept");
+ printf("OK\n");
+ }
+ }
+ }
+}
diff --git a/tools/regression/sockets/listenclose/Makefile b/tools/regression/sockets/listenclose/Makefile
new file mode 100644
index 000000000000..a2a09a02a3bf
--- /dev/null
+++ b/tools/regression/sockets/listenclose/Makefile
@@ -0,0 +1,7 @@
+#
+#
+
+PROG= listenclose
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/listenclose/listenclose.c b/tools/regression/sockets/listenclose/listenclose.c
new file mode 100644
index 000000000000..5aa7090a1676
--- /dev/null
+++ b/tools/regression/sockets/listenclose/listenclose.c
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 2004-2005 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * The listenclose regression test is designed to catch kernel bugs that may
+ * trigger as a result of performing a close on a listen() socket with as-yet
+ * unaccepted connections in its queues. This results in the connections
+ * being aborted, which is a not-often-followed code path. To do this, we
+ * create a local TCP socket, build a non-blocking connection to it, and then
+ * close the accept socket. The connection must be non-blocking or the
+ * program will block and as such connect() will not return as accept() is
+ * never called.
+ */
+
+int
+main(void)
+{
+ int listen_sock, connect_sock;
+ struct sockaddr_in sin;
+ socklen_t len;
+ u_short port;
+ int arg;
+
+ listen_sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (listen_sock == -1)
+ errx(-1,
+ "socket(PF_INET, SOCK_STREAM, 0) for listen socket: %s",
+ strerror(errno));
+
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = 0;
+
+ if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ errx(-1, "bind(%s, %d) for listen socket: %s",
+ inet_ntoa(sin.sin_addr), 0, strerror(errno));
+
+ len = sizeof(sin);
+ if (getsockname(listen_sock, (struct sockaddr *)&sin, &len) < 0)
+ errx(-1, "getsockname() for listen socket: %s",
+ strerror(errno));
+ port = sin.sin_port;
+
+ if (listen(listen_sock, -1) < 0)
+ errx(-1, "listen() for listen socket: %s", strerror(errno));
+
+ connect_sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (connect_sock == -1)
+ errx(-1, "socket(PF_INET, SOCK_STREAM, 0) for connect "
+ "socket: %s", strerror(errno));
+
+ arg = O_NONBLOCK;
+ if (fcntl(connect_sock, F_SETFL, &arg) < 0)
+ errx(-1, "socket(PF_INET, SOCK_STREAM, 0) for connect socket"
+ ": %s", strerror(errno));
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = port;
+
+ if (connect(connect_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ errx(-1, "connect() for connect socket: %s", strerror(errno));
+ close(connect_sock);
+ close(listen_sock);
+
+ return (0);
+}
diff --git a/tools/regression/sockets/pr_atomic/Makefile b/tools/regression/sockets/pr_atomic/Makefile
new file mode 100644
index 000000000000..e9e5a2fa2e55
--- /dev/null
+++ b/tools/regression/sockets/pr_atomic/Makefile
@@ -0,0 +1,4 @@
+PROG= pr_atomic
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/pr_atomic/pr_atomic.c b/tools/regression/sockets/pr_atomic/pr_atomic.c
new file mode 100644
index 000000000000..cb7e24cdc972
--- /dev/null
+++ b/tools/regression/sockets/pr_atomic/pr_atomic.c
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 2006 Bruce M. Simpson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Regression test for uiomove in kernel; specifically for PR kern/38495.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdlib.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+
+static char socket_path[] = "tmp.XXXXXX";
+
+static jmp_buf myjmpbuf;
+
+static void handle_sigalrm(int signo __unused)
+{
+ longjmp(myjmpbuf, 1);
+}
+
+int
+main(void)
+{
+ struct sockaddr_un un;
+ pid_t pid;
+ int s;
+
+ if (mkstemp(socket_path) == -1)
+ err(1, "mkstemp");
+ s = socket(PF_LOCAL, SOCK_DGRAM, 0);
+ if (s == -1)
+ errx(-1, "socket");
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_LOCAL;
+ unlink(socket_path);
+ strcpy(un.sun_path, socket_path);
+ if (bind(s, (struct sockaddr *)&un, sizeof(un)) == -1)
+ errx(-1, "bind");
+ pid = fork();
+ if (pid == -1)
+ errx(-1, "fork");
+ if (pid == 0) {
+ int conn;
+ char buf[] = "AAAAAAAAA";
+
+ close(s);
+ conn = socket(AF_LOCAL, SOCK_DGRAM, 0);
+ if (conn == -1)
+ errx(-1,"socket");
+ if (sendto(conn, buf, sizeof(buf), 0, (struct sockaddr *)&un,
+ sizeof(un)) != sizeof(buf))
+ errx(-1,"sendto");
+ close(conn);
+ _exit(0);
+ }
+
+ sleep(5);
+
+ /* Make sure the data is there when we try to receive it. */
+ if (recvfrom(s, (void *)-1, 1, 0, NULL, NULL) != -1)
+ errx(-1,"recvfrom succeeded when failure expected");
+
+ (void)signal(SIGALRM, handle_sigalrm);
+ if (setjmp(myjmpbuf) == 0) {
+ /*
+ * This recvfrom will panic an unpatched system, and block
+ * a patched one.
+ */
+ alarm(5);
+ (void)recvfrom(s, (void *)-1, 1, 0, NULL, NULL);
+ }
+
+ /* We should reach here via longjmp() and all should be well. */
+
+ return (0);
+}
diff --git a/tools/regression/sockets/reconnect/Makefile b/tools/regression/sockets/reconnect/Makefile
new file mode 100644
index 000000000000..2b3eba0e8db4
--- /dev/null
+++ b/tools/regression/sockets/reconnect/Makefile
@@ -0,0 +1,7 @@
+#
+#
+
+PROG= reconnect
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/reconnect/reconnect.c b/tools/regression/sockets/reconnect/reconnect.c
new file mode 100644
index 000000000000..033d03369637
--- /dev/null
+++ b/tools/regression/sockets/reconnect/reconnect.c
@@ -0,0 +1,131 @@
+/*-
+ * Copyright (c) 2005 Maxim Sobolev
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * The reconnect regression test is designed to catch kernel bug that may
+ * prevent changing association of already associated datagram unix domain
+ * socket when server side of connection has been closed.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+static char uds_name1[] = "reconnect.XXXXXXXX";
+static char uds_name2[] = "reconnect.XXXXXXXX";
+
+#define sstosa(ss) ((struct sockaddr *)(ss))
+
+static void
+prepare_ifsun(struct sockaddr_un *ifsun, const char *path)
+{
+
+ memset(ifsun, '\0', sizeof(*ifsun));
+#if !defined(__linux__) && !defined(__solaris__)
+ ifsun->sun_len = strlen(path);
+#endif
+ ifsun->sun_family = AF_LOCAL;
+ strcpy(ifsun->sun_path, path);
+}
+
+static int
+create_uds_server(const char *path)
+{
+ struct sockaddr_un ifsun;
+ int sock;
+
+ prepare_ifsun(&ifsun, path);
+
+ unlink(ifsun.sun_path);
+
+ sock = socket(PF_LOCAL, SOCK_DGRAM, 0);
+ if (sock == -1)
+ err(1, "can't create socket");
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &sock, sizeof(sock));
+ if (bind(sock, sstosa(&ifsun), sizeof(ifsun)) < 0)
+ err(1, "can't bind to a socket");
+
+ return sock;
+}
+
+static void
+connect_uds_server(int sock, const char *path)
+{
+ struct sockaddr_un ifsun;
+ int e;
+
+ prepare_ifsun(&ifsun, path);
+
+ e = connect(sock, sstosa(&ifsun), sizeof(ifsun));
+ if (e < 0)
+ err(1, "can't connect to a socket");
+}
+
+static void
+cleanup(void)
+{
+
+ unlink(uds_name1);
+ unlink(uds_name2);
+}
+
+int
+main(void)
+{
+ int s_sock1, s_sock2, c_sock;
+
+ atexit(cleanup);
+
+ if (mkstemp(uds_name1) == -1)
+ err(1, "mkstemp");
+ unlink(uds_name1);
+ s_sock1 = create_uds_server(uds_name1);
+
+ if (mkstemp(uds_name2) == -1)
+ err(1, "mkstemp");
+ unlink(uds_name2);
+ s_sock2 = create_uds_server(uds_name2);
+
+ c_sock = socket(PF_LOCAL, SOCK_DGRAM, 0);
+ if (c_sock < 0)
+ err(1, "can't create socket");
+
+ connect_uds_server(c_sock, uds_name1);
+ close(s_sock1);
+ connect_uds_server(c_sock, uds_name2);
+ close(s_sock2);
+
+ exit (0);
+}
diff --git a/tools/regression/sockets/rtsocket/Makefile b/tools/regression/sockets/rtsocket/Makefile
new file mode 100644
index 000000000000..31120210ee2a
--- /dev/null
+++ b/tools/regression/sockets/rtsocket/Makefile
@@ -0,0 +1,4 @@
+PROG= rtsocket
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/rtsocket/rtsocket.c b/tools/regression/sockets/rtsocket/rtsocket.c
new file mode 100644
index 000000000000..68003768223b
--- /dev/null
+++ b/tools/regression/sockets/rtsocket/rtsocket.c
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 2006 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Simple routing socket regression test: create and destroy a raw routing
+ * socket, and make sure that dgram and stream don't work, socketpair, etc.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <net/route.h>
+
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ int sock, socks[2];
+
+ sock = socket(PF_ROUTE, SOCK_STREAM, 0);
+ if (sock >= 0) {
+ close(sock);
+ errx(-1, "socket(PF_ROUTE, SOCK_STREAM, 0) returned %d",
+ sock);
+ }
+
+ if (errno != EPROTOTYPE)
+ err(-1, "socket(PF_ROUTE, SOCK_STREAM, 0)");
+
+ sock = socket(PF_ROUTE, SOCK_DGRAM, 0);
+ if (sock >= 0) {
+ close(sock);
+ errx(-1, "socket(PF_ROUTE, SOCK_DGRAM, 0) returned %d",
+ sock);
+ }
+
+ if (errno != EPROTOTYPE)
+ err(-1, "socket(PF_ROUTE, SOCK_DGRAM, 0)");
+
+ sock = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (sock < 0)
+ err(-1, "socket(PF_ROUTE, SOCK_RAW, 0)");
+ close(sock);
+
+ if (socketpair(PF_ROUTE, SOCK_STREAM, 0, socks) == 0) {
+ close(socks[0]);
+ close(socks[1]);
+ errx(-1,
+ "socketpair(PF_ROUTE, SOCK_STREAM, 0, socks) success");
+ }
+
+ if (errno != EPROTOTYPE)
+ err(-1, "socketpair(PF_ROUTE, SOCK_STREAM, 0, socks)");
+
+ if (socketpair(PF_ROUTE, SOCK_DGRAM, 0, socks) == 0) {
+ close(socks[0]);
+ close(socks[1]);
+ errx(-1,
+ "socketpair(PF_ROUTE, SOCK_DGRAM, 0, socks) success");
+ }
+
+ if (errno != EPROTOTYPE)
+ err(-1, "socketpair(PF_ROUTE, SOCK_DGRAM, 0, socks)");
+
+ if (socketpair(PF_ROUTE, SOCK_RAW, 0, socks) == 0) {
+ close(socks[0]);
+ close(socks[1]);
+ errx(-1,
+ "socketpair(PF_ROUTE, SOCK_STREAM, 0, socks) success");
+ }
+
+ return (0);
+}
diff --git a/tools/regression/sockets/sblock/Makefile b/tools/regression/sockets/sblock/Makefile
new file mode 100644
index 000000000000..724259c8615f
--- /dev/null
+++ b/tools/regression/sockets/sblock/Makefile
@@ -0,0 +1,7 @@
+#
+#
+
+PROG= sblock
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/sblock/sblock.c b/tools/regression/sockets/sblock/sblock.c
new file mode 100644
index 000000000000..e7788d943033
--- /dev/null
+++ b/tools/regression/sockets/sblock/sblock.c
@@ -0,0 +1,205 @@
+/*-
+ * Copyright (c) 2007 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Sockets serialize I/O in each direction in order to avoid interlacing of
+ * I/O by multiple processes or threcvs recving or sending the socket. This
+ * is done using some form of kernel lock (varies by kernel version), called
+ * "sblock" in FreeBSD. However, to avoid unkillable processes waiting on
+ * I/O that may be entirely controlled by a remote network endpoint, that
+ * lock acquisition must be interruptible.
+ *
+ * To test this, set up a local domain stream socket pair and a set of three
+ * processes. Two processes block in recv(), the first on sbwait (wait for
+ * I/O), and the second on the sblock waiting for the first to finish. A
+ * third process is responsible for signalling the second process, then
+ * writing to the socket. Depending on the error returned in the second
+ * process, we can tell whether the sblock wait was interrupted, or if
+ * instead the process only woke up when the write was performed.
+ */
+
+#include <sys/socket.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static int interrupted;
+static void
+signal_handler(int signum __unused)
+{
+
+ interrupted++;
+}
+
+/*
+ * Process that will perform a blocking recv on a UNIX domain socket. This
+ * should return one byte of data.
+ */
+static void
+blocking_recver(int fd)
+{
+ ssize_t len;
+ char ch;
+
+ len = recv(fd, &ch, sizeof(ch), 0);
+ if (len < 0)
+ err(-1, "FAIL: blocking_recver: recv");
+ if (len == 0)
+ errx(-1, "FAIL: blocking_recver: recv: eof");
+ if (len != 1)
+ errx(-1, "FAIL: blocking_recver: recv: %zd bytes", len);
+ if (interrupted)
+ errx(-1, "FAIL: blocking_recver: interrupted wrong pid");
+}
+
+/*
+ * Process that will perform a locking recv on a UNIX domain socket.
+ *
+ * This is where we figure out if the test worked or not. If it has failed,
+ * then recv() will return EOF, as the close() arrives before the signal,
+ * meaning that the wait for the sblock was not interrupted; if it has
+ * succeeded, we get EINTR as the signal interrupts the lock request.
+ */
+static void
+locking_recver(int fd)
+{
+ ssize_t len;
+ char ch;
+
+ if (sleep(1) != 0)
+ err(-1, "FAIL: locking_recver: sleep");
+ len = recv(fd, &ch, sizeof(ch), 0);
+ if (len < 0 && errno != EINTR)
+ err(-1, "FAIL: locking_recver: recv");
+ if (len < 0 && errno == EINTR) {
+ fprintf(stderr, "PASS\n");
+ exit(0);
+ }
+ if (len == 0)
+ errx(-1, "FAIL: locking_recver: recv: eof");
+ if (!interrupted)
+ errx(-1, "FAIL: locking_recver: not interrupted");
+}
+
+static void
+signaller(pid_t locking_recver_pid, int fd)
+{
+ ssize_t len;
+ char ch;
+
+ if (sleep(2) != 0) {
+ warn("signaller sleep(2)");
+ return;
+ }
+ if (kill(locking_recver_pid, SIGHUP) < 0) {
+ warn("signaller kill(%d)", locking_recver_pid);
+ return;
+ }
+ if (sleep(1) != 0) {
+ warn("signaller sleep(1)");
+ return;
+ }
+ len = send(fd, &ch, sizeof(ch), 0);
+ if (len < 0) {
+ warn("signaller send");
+ return;
+ }
+ if (len != sizeof(ch)) {
+ warnx("signaller send ret %zd", len);
+ return;
+ }
+ if (close(fd) < 0) {
+ warn("signaller close");
+ return;
+ }
+ if (sleep(1) != 0) {
+ warn("signaller sleep(1)");
+ return;
+ }
+}
+
+int
+main(void)
+{
+ int error, fds[2], recver_fd, sender_fd;
+ pid_t blocking_recver_pid;
+ pid_t locking_recver_pid;
+ struct sigaction sa;
+
+ if (sigaction(SIGHUP, NULL, &sa) < 0)
+ err(-1, "FAIL: sigaction(SIGHUP, NULL, &sa)");
+
+ sa.sa_handler = signal_handler;
+ if (sa.sa_flags & SA_RESTART)
+ printf("SIGHUP restartable by default (cleared)\n");
+ sa.sa_flags &= ~SA_RESTART;
+
+ if (sigaction(SIGHUP, &sa, NULL) < 0)
+ err(-1, "FAIL: sigaction(SIGHUP, &sa, NULL)");
+
+#if 0
+ if (signal(SIGHUP, signal_handler) == SIG_ERR)
+ err(-1, "FAIL: signal(SIGHUP)");
+#endif
+
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0)
+ err(-1, "FAIL: socketpair(PF_LOCAL, SOGK_STREAM, 0)");
+
+ sender_fd = fds[0];
+ recver_fd = fds[1];
+
+ blocking_recver_pid = fork();
+ if (blocking_recver_pid < 0)
+ err(-1, "FAIL: fork");
+ if (blocking_recver_pid == 0) {
+ close(sender_fd);
+ blocking_recver(recver_fd);
+ exit(0);
+ }
+
+ locking_recver_pid = fork();
+ if (locking_recver_pid < 0) {
+ error = errno;
+ kill(blocking_recver_pid, SIGKILL);
+ errno = error;
+ err(-1, "FAIL: fork");
+ }
+ if (locking_recver_pid == 0) {
+ close(sender_fd);
+ locking_recver(recver_fd);
+ exit(0);
+ }
+
+ signaller(locking_recver_pid, sender_fd);
+
+ kill(blocking_recver_pid, SIGKILL);
+ kill(locking_recver_pid, SIGKILL);
+ exit(0);
+}
diff --git a/tools/regression/sockets/sendfile/Makefile b/tools/regression/sockets/sendfile/Makefile
new file mode 100644
index 000000000000..31ae7b9124bb
--- /dev/null
+++ b/tools/regression/sockets/sendfile/Makefile
@@ -0,0 +1,8 @@
+#
+#
+
+PROG= sendfile
+MAN=
+LIBADD= md
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/sendfile/sendfile.c b/tools/regression/sockets/sendfile/sendfile.c
new file mode 100644
index 000000000000..40af5a67982e
--- /dev/null
+++ b/tools/regression/sockets/sendfile/sendfile.c
@@ -0,0 +1,481 @@
+/*-
+ * Copyright (c) 2006 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <md5.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Simple regression test for sendfile. Creates a file sized at four pages
+ * and then proceeds to send it over a series of sockets, exercising a number
+ * of cases and performing limited validation.
+ */
+
+#define FAIL(msg) {printf("# %s\n", msg); \
+ return (-1);}
+
+#define FAIL_ERR(msg) {printf("# %s: %s\n", msg, strerror(errno)); \
+ return (-1);}
+
+#define TEST_PORT 5678
+#define TEST_MAGIC 0x4440f7bb
+#define TEST_PAGES 4
+#define TEST_SECONDS 30
+
+struct test_header {
+ uint32_t th_magic;
+ uint32_t th_header_length;
+ uint32_t th_offset;
+ uint32_t th_length;
+ char th_md5[33];
+};
+
+struct sendfile_test {
+ uint32_t hdr_length;
+ uint32_t offset;
+ uint32_t length;
+ uint32_t file_size;
+};
+
+static int file_fd;
+static char path[PATH_MAX];
+static int listen_socket;
+static int accept_socket;
+
+static int test_th(struct test_header *th, uint32_t *header_length,
+ uint32_t *offset, uint32_t *length);
+static void signal_alarm(int signum);
+static void setup_alarm(int seconds);
+static void cancel_alarm(void);
+static int receive_test(void);
+static void run_child(void);
+static int new_test_socket(int *connect_socket);
+static void init_th(struct test_header *th, uint32_t header_length,
+ uint32_t offset, uint32_t length);
+static int send_test(int connect_socket, struct sendfile_test);
+static int write_test_file(size_t file_size);
+static void run_parent(void);
+static void cleanup(void);
+
+
+static int
+test_th(struct test_header *th, uint32_t *header_length, uint32_t *offset,
+ uint32_t *length)
+{
+
+ if (th->th_magic != htonl(TEST_MAGIC))
+ FAIL("magic number not found in header")
+ *header_length = ntohl(th->th_header_length);
+ *offset = ntohl(th->th_offset);
+ *length = ntohl(th->th_length);
+ return (0);
+}
+
+static void
+signal_alarm(int signum)
+{
+ (void)signum;
+
+ printf("# test timeout\n");
+
+ if (accept_socket > 0)
+ close(accept_socket);
+ if (listen_socket > 0)
+ close(listen_socket);
+
+ _exit(-1);
+}
+
+static void
+setup_alarm(int seconds)
+{
+ struct itimerval itv;
+ bzero(&itv, sizeof(itv));
+ (void)seconds;
+ itv.it_value.tv_sec = seconds;
+
+ signal(SIGALRM, signal_alarm);
+ setitimer(ITIMER_REAL, &itv, NULL);
+}
+
+static void
+cancel_alarm(void)
+{
+ struct itimerval itv;
+ bzero(&itv, sizeof(itv));
+ setitimer(ITIMER_REAL, &itv, NULL);
+}
+
+static int
+receive_test(void)
+{
+ uint32_t header_length, offset, length, counter;
+ struct test_header th;
+ ssize_t len;
+ char buf[10240];
+ MD5_CTX md5ctx;
+ char *rxmd5;
+
+ len = read(accept_socket, &th, sizeof(th));
+ if (len < 0 || (size_t)len < sizeof(th))
+ FAIL_ERR("read")
+
+ if (test_th(&th, &header_length, &offset, &length) != 0)
+ return (-1);
+
+ MD5Init(&md5ctx);
+
+ counter = 0;
+ while (1) {
+ len = read(accept_socket, buf, sizeof(buf));
+ if (len < 0 || len == 0)
+ break;
+ counter += len;
+ MD5Update(&md5ctx, buf, len);
+ }
+
+ rxmd5 = MD5End(&md5ctx, NULL);
+
+ if ((counter != header_length+length) ||
+ memcmp(th.th_md5, rxmd5, 33) != 0)
+ FAIL("receive length mismatch")
+
+ free(rxmd5);
+ return (0);
+}
+
+static void
+run_child(void)
+{
+ struct sockaddr_in sin;
+ int rc = 0;
+
+ listen_socket = socket(PF_INET, SOCK_STREAM, 0);
+ if (listen_socket < 0) {
+ printf("# socket: %s\n", strerror(errno));
+ rc = -1;
+ }
+
+ if (!rc) {
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(TEST_PORT);
+
+ if (bind(listen_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ printf("# bind: %s\n", strerror(errno));
+ rc = -1;
+ }
+ }
+
+ if (!rc && listen(listen_socket, -1) < 0) {
+ printf("# listen: %s\n", strerror(errno));
+ rc = -1;
+ }
+
+ if (!rc) {
+ accept_socket = accept(listen_socket, NULL, NULL);
+ setup_alarm(TEST_SECONDS);
+ if (receive_test() != 0)
+ rc = -1;
+ }
+
+ cancel_alarm();
+ if (accept_socket > 0)
+ close(accept_socket);
+ if (listen_socket > 0)
+ close(listen_socket);
+
+ _exit(rc);
+}
+
+static int
+new_test_socket(int *connect_socket)
+{
+ struct sockaddr_in sin;
+ int rc = 0;
+
+ *connect_socket = socket(PF_INET, SOCK_STREAM, 0);
+ if (*connect_socket < 0)
+ FAIL_ERR("socket")
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(TEST_PORT);
+
+ if (connect(*connect_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ FAIL_ERR("connect")
+
+ return (rc);
+}
+
+static void
+init_th(struct test_header *th, uint32_t header_length, uint32_t offset,
+ uint32_t length)
+{
+ bzero(th, sizeof(*th));
+ th->th_magic = htonl(TEST_MAGIC);
+ th->th_header_length = htonl(header_length);
+ th->th_offset = htonl(offset);
+ th->th_length = htonl(length);
+
+ MD5FileChunk(path, th->th_md5, offset, length);
+}
+
+static int
+send_test(int connect_socket, struct sendfile_test test)
+{
+ struct test_header th;
+ struct sf_hdtr hdtr, *hdtrp;
+ struct iovec headers;
+ char *header;
+ ssize_t len;
+ int length;
+ off_t off;
+
+ len = lseek(file_fd, 0, SEEK_SET);
+ if (len != 0)
+ FAIL_ERR("lseek")
+
+ struct stat st;
+ if (fstat(file_fd, &st) < 0)
+ FAIL_ERR("fstat")
+ length = st.st_size - test.offset;
+ if (test.length > 0 && test.length < (uint32_t)length)
+ length = test.length;
+
+ init_th(&th, test.hdr_length, test.offset, length);
+
+ len = write(connect_socket, &th, sizeof(th));
+ if (len != sizeof(th))
+ return (-1);
+
+ if (test.hdr_length != 0) {
+ header = malloc(test.hdr_length);
+ if (header == NULL)
+ FAIL_ERR("malloc")
+
+ hdtrp = &hdtr;
+ bzero(&headers, sizeof(headers));
+ headers.iov_base = header;
+ headers.iov_len = test.hdr_length;
+ bzero(&hdtr, sizeof(hdtr));
+ hdtr.headers = &headers;
+ hdtr.hdr_cnt = 1;
+ hdtr.trailers = NULL;
+ hdtr.trl_cnt = 0;
+ } else {
+ hdtrp = NULL;
+ header = NULL;
+ }
+
+ if (sendfile(file_fd, connect_socket, test.offset, test.length,
+ hdtrp, &off, 0) < 0) {
+ if (header != NULL)
+ free(header);
+ FAIL_ERR("sendfile")
+ }
+
+ if (length == 0) {
+ struct stat sb;
+
+ if (fstat(file_fd, &sb) == 0)
+ length = sb.st_size - test.offset;
+ }
+
+ if (header != NULL)
+ free(header);
+
+ if (off != length)
+ FAIL("offset != length")
+
+ return (0);
+}
+
+static int
+write_test_file(size_t file_size)
+{
+ char *page_buffer;
+ ssize_t len;
+ static size_t current_file_size = 0;
+
+ if (file_size == current_file_size)
+ return (0);
+ else if (file_size < current_file_size) {
+ if (ftruncate(file_fd, file_size) != 0)
+ FAIL_ERR("ftruncate");
+ current_file_size = file_size;
+ return (0);
+ }
+
+ page_buffer = malloc(file_size);
+ if (page_buffer == NULL)
+ FAIL_ERR("malloc")
+ bzero(page_buffer, file_size);
+
+ len = write(file_fd, page_buffer, file_size);
+ if (len < 0)
+ FAIL_ERR("write")
+
+ len = lseek(file_fd, 0, SEEK_SET);
+ if (len < 0)
+ FAIL_ERR("lseek")
+ if (len != 0)
+ FAIL("len != 0")
+
+ free(page_buffer);
+ current_file_size = file_size;
+ return (0);
+}
+
+static void
+run_parent(void)
+{
+ int connect_socket;
+ int status;
+ int test_num;
+ int test_count;
+ int pid;
+ size_t desired_file_size = 0;
+
+ const int pagesize = getpagesize();
+
+ struct sendfile_test tests[] = {
+ { .hdr_length = 0, .offset = 0, .length = 1 },
+ { .hdr_length = 0, .offset = 0, .length = pagesize },
+ { .hdr_length = 0, .offset = 1, .length = 1 },
+ { .hdr_length = 0, .offset = 1, .length = pagesize },
+ { .hdr_length = 0, .offset = pagesize, .length = pagesize },
+ { .hdr_length = 0, .offset = 0, .length = 2*pagesize },
+ { .hdr_length = 0, .offset = 0, .length = 0 },
+ { .hdr_length = 0, .offset = pagesize, .length = 0 },
+ { .hdr_length = 0, .offset = 2*pagesize, .length = 0 },
+ { .hdr_length = 0, .offset = TEST_PAGES*pagesize, .length = 0 },
+ { .hdr_length = 0, .offset = 0, .length = pagesize,
+ .file_size = 1 }
+ };
+
+ test_count = sizeof(tests) / sizeof(tests[0]);
+ printf("1..%d\n", test_count);
+
+ for (test_num = 1; test_num <= test_count; test_num++) {
+
+ desired_file_size = tests[test_num - 1].file_size;
+ if (desired_file_size == 0)
+ desired_file_size = TEST_PAGES * pagesize;
+ if (write_test_file(desired_file_size) != 0) {
+ printf("not ok %d\n", test_num);
+ continue;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ printf("not ok %d\n", test_num);
+ continue;
+ }
+
+ if (pid == 0)
+ run_child();
+
+ usleep(250000);
+
+ if (new_test_socket(&connect_socket) != 0) {
+ printf("not ok %d\n", test_num);
+ kill(pid, SIGALRM);
+ close(connect_socket);
+ continue;
+ }
+
+ if (send_test(connect_socket, tests[test_num-1]) != 0) {
+ printf("not ok %d\n", test_num);
+ kill(pid, SIGALRM);
+ close(connect_socket);
+ continue;
+ }
+
+ close(connect_socket);
+ if (waitpid(pid, &status, 0) == pid) {
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+ printf("%s %d\n", "ok", test_num);
+ else
+ printf("%s %d\n", "not ok", test_num);
+ }
+ else {
+ printf("not ok %d\n", test_num);
+ }
+ }
+}
+
+static void
+cleanup(void)
+{
+
+ unlink(path);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ path[0] = '\0';
+
+ if (argc == 1) {
+ snprintf(path, sizeof(path), "sendfile.XXXXXXXXXXXX");
+ file_fd = mkstemp(path);
+ if (file_fd == -1)
+ FAIL_ERR("mkstemp");
+ } else if (argc == 2) {
+ (void)strlcpy(path, argv[1], sizeof(path));
+ file_fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600);
+ if (file_fd == -1)
+ FAIL_ERR("open");
+ } else {
+ FAIL("usage: sendfile [path]");
+ }
+
+ atexit(cleanup);
+
+ run_parent();
+ return (0);
+}
diff --git a/tools/regression/sockets/shutdown/Makefile b/tools/regression/sockets/shutdown/Makefile
new file mode 100644
index 000000000000..87706eb23450
--- /dev/null
+++ b/tools/regression/sockets/shutdown/Makefile
@@ -0,0 +1,7 @@
+#
+#
+
+PROG= shutdown
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/shutdown/shutdown.c b/tools/regression/sockets/shutdown/shutdown.c
new file mode 100644
index 000000000000..95c0120801e2
--- /dev/null
+++ b/tools/regression/sockets/shutdown/shutdown.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (C) 2005 The FreeBSD Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ struct sockaddr_in sock;
+ socklen_t len;
+ int listen_sock, connect_sock;
+ u_short port;
+
+ listen_sock = -1;
+
+ /* Shutdown(2) on an invalid file descriptor has to return EBADF. */
+ if ((shutdown(listen_sock, SHUT_RDWR) != -1) && (errno != EBADF))
+ errx(-1, "shutdown() for invalid file descriptor does not "
+ "return EBADF");
+
+ listen_sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (listen_sock == -1)
+ errx(-1,
+ "socket(PF_INET, SOCK_STREAM, 0) for listen socket: %s",
+ strerror(errno));
+
+ bzero(&sock, sizeof(sock));
+ sock.sin_len = sizeof(sock);
+ sock.sin_family = AF_INET;
+ sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sock.sin_port = 0;
+
+ if (bind(listen_sock, (struct sockaddr *)&sock, sizeof(sock)) < 0)
+ errx(-1, "bind(%s, %d) for listen socket: %s",
+ inet_ntoa(sock.sin_addr), sock.sin_port, strerror(errno));
+
+ len = sizeof(sock);
+ if (getsockname(listen_sock, (struct sockaddr *)&sock, &len) < 0)
+ errx(-1, "getsockname() for listen socket: %s",
+ strerror(errno));
+ port = sock.sin_port;
+
+ if (listen(listen_sock, -1) < 0)
+ errx(-1, "listen() for listen socket: %s", strerror(errno));
+
+ connect_sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (connect_sock == -1)
+ errx(-1, "socket(PF_INET, SOCK_STREAM, 0) for connect "
+ "socket: %s", strerror(errno));
+
+ bzero(&sock, sizeof(sock));
+ sock.sin_len = sizeof(sock);
+ sock.sin_family = AF_INET;
+ sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sock.sin_port = port;
+
+ if (connect(connect_sock, (struct sockaddr *)&sock, sizeof(sock)) < 0)
+ errx(-1, "connect() for connect socket: %s", strerror(errno));
+ /* Try to pass an invalid flags. */
+ if ((shutdown(connect_sock, SHUT_RD - 1) != -1) && (errno != EINVAL))
+ errx(-1, "shutdown(SHUT_RD - 1) does not return EINVAL");
+ if ((shutdown(connect_sock, SHUT_RDWR + 1) != -1) && (errno != EINVAL))
+ errx(-1, "shutdown(SHUT_RDWR + 1) does not return EINVAL");
+
+ if (shutdown(connect_sock, SHUT_RD) < 0)
+ errx(-1, "shutdown(SHUT_RD) for connect socket: %s",
+ strerror(errno));
+ if (shutdown(connect_sock, SHUT_WR) < 0)
+ errx(-1, "shutdown(SHUT_WR) for connect socket: %s",
+ strerror(errno));
+
+ close(connect_sock);
+ close(listen_sock);
+
+ return (0);
+}
diff --git a/tools/regression/sockets/sigpipe/Makefile b/tools/regression/sockets/sigpipe/Makefile
new file mode 100644
index 000000000000..d6cb19b7f88f
--- /dev/null
+++ b/tools/regression/sockets/sigpipe/Makefile
@@ -0,0 +1,7 @@
+#
+#
+
+PROG= sigpipe
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/sigpipe/sigpipe.c b/tools/regression/sockets/sigpipe/sigpipe.c
new file mode 100644
index 000000000000..d3c52356f4b1
--- /dev/null
+++ b/tools/regression/sockets/sigpipe/sigpipe.c
@@ -0,0 +1,320 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * This regression test is intended to verify whether or not SIGPIPE is
+ * properly generated in several simple test cases, as well as testing
+ * whether SO_NOSIGPIPE disables SIGPIPE, if available on the system.
+ * SIGPIPE is generated if a write or send is attempted on a socket that has
+ * been shutdown for write. This test runs several test cases with UNIX
+ * domain sockets and TCP sockets to confirm that either EPIPE or SIGPIPE is
+ * properly returned.
+ *
+ * For the purposes of testing TCP, an unused port number must be specified.
+ */
+static void
+usage(void)
+{
+
+ errx(-1, "usage: sigpipe tcpport");
+}
+
+/*
+ * Signal catcher. Set a global flag that can be tested by the caller.
+ */
+static int signaled;
+static int
+got_signal(void)
+{
+
+ return (signaled);
+}
+
+static void
+signal_handler(int signum __unused)
+{
+
+ signaled = 1;
+}
+
+static void
+signal_setup(const char *testname)
+{
+
+ signaled = 0;
+ if (signal(SIGPIPE, signal_handler) == SIG_ERR)
+ err(-1, "%s: signal(SIGPIPE)", testname);
+}
+
+static void
+test_send(const char *testname, int sock)
+{
+ ssize_t len;
+ char ch;
+
+ ch = 0;
+ len = send(sock, &ch, sizeof(ch), 0);
+ if (len < 0) {
+ if (errno == EPIPE)
+ return;
+ err(-1, "%s: send", testname);
+ }
+ errx(-1, "%s: send: returned %zd", testname, len);
+}
+
+static void
+test_write(const char *testname, int sock)
+{
+ ssize_t len;
+ char ch;
+
+ ch = 0;
+ len = write(sock, &ch, sizeof(ch));
+ if (len < 0) {
+ if (errno == EPIPE)
+ return;
+ err(-1, "%s: write", testname);
+ }
+ errx(-1, "%s: write: returned %zd", testname, len);
+}
+
+static void
+test_send_wantsignal(const char *testname, int sock1, int sock2)
+{
+
+ if (shutdown(sock2, SHUT_WR) < 0)
+ err(-1, "%s: shutdown", testname);
+ signal_setup(testname);
+ test_send(testname, sock2);
+ if (!got_signal())
+ errx(-1, "%s: send: didn't receive SIGPIPE", testname);
+ close(sock1);
+ close(sock2);
+}
+
+#ifdef SO_NOSIGPIPE
+static void
+test_send_dontsignal(const char *testname, int sock1, int sock2)
+{
+ int i;
+
+ i = 1;
+ if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0)
+ err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname);
+ if (shutdown(sock2, SHUT_WR) < 0)
+ err(-1, "%s: shutdown", testname);
+ signal_setup(testname);
+ test_send(testname, sock2);
+ if (got_signal())
+ errx(-1, "%s: send: got SIGPIPE", testname);
+ close(sock1);
+ close(sock2);
+}
+#endif
+
+static void
+test_write_wantsignal(const char *testname, int sock1, int sock2)
+{
+
+ if (shutdown(sock2, SHUT_WR) < 0)
+ err(-1, "%s: shutdown", testname);
+ signal_setup(testname);
+ test_write(testname, sock2);
+ if (!got_signal())
+ errx(-1, "%s: write: didn't receive SIGPIPE", testname);
+ close(sock1);
+ close(sock2);
+}
+
+#ifdef SO_NOSIGPIPE
+static void
+test_write_dontsignal(const char *testname, int sock1, int sock2)
+{
+ int i;
+
+ i = 1;
+ if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0)
+ err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname);
+ if (shutdown(sock2, SHUT_WR) < 0)
+ err(-1, "%s: shutdown", testname);
+ signal_setup(testname);
+ test_write(testname, sock2);
+ if (got_signal())
+ errx(-1, "%s: write: got SIGPIPE", testname);
+ close(sock1);
+ close(sock2);
+}
+#endif
+
+static int listen_sock;
+static void
+tcp_setup(u_short port)
+{
+ struct sockaddr_in sin;
+
+ listen_sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (listen_sock < 0)
+ err(-1, "tcp_setup: listen");
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(port);
+
+ if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ err(-1, "tcp_setup: bind");
+
+ if (listen(listen_sock, -1) < 0)
+ err(-1, "tcp_setup: listen");
+}
+
+static void
+tcp_teardown(void)
+{
+
+ close(listen_sock);
+}
+
+static void
+tcp_pair(u_short port, int sock[2])
+{
+ int accept_sock, connect_sock;
+ struct sockaddr_in sin;
+ socklen_t len;
+
+ connect_sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (connect_sock < 0)
+ err(-1, "tcp_pair: socket");
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(port);
+
+ if (connect(connect_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ err(-1, "tcp_pair: connect");
+
+ sleep(1); /* Time for TCP to settle. */
+
+ len = sizeof(sin);
+ accept_sock = accept(listen_sock, (struct sockaddr *)&sin, &len);
+ if (accept_sock < 0)
+ err(-1, "tcp_pair: accept");
+
+ sleep(1); /* Time for TCP to settle. */
+
+ sock[0] = accept_sock;
+ sock[1] = connect_sock;
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *dummy;
+ int sock[2];
+ long port;
+
+ if (argc != 2)
+ usage();
+
+ port = strtol(argv[1], &dummy, 10);
+ if (port < 0 || port > 65535 || *dummy != '\0')
+ usage();
+
+#ifndef SO_NOSIGPIPE
+ warnx("sigpipe: SO_NOSIGPIPE not defined, skipping some tests");
+#endif
+
+ /*
+ * UNIX domain socketpair().
+ */
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0)
+ err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)");
+ test_send_wantsignal("test_send_wantsignal(PF_LOCAL)", sock[0],
+ sock[1]);
+
+#ifdef SO_NOSIGPIPE
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0)
+ err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)");
+ test_send_dontsignal("test_send_dontsignal(PF_LOCAL)", sock[0],
+ sock[1]);
+#endif
+
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0)
+ err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)");
+ test_write_wantsignal("test_write_wantsignal(PF_LOCAL)", sock[0],
+ sock[1]);
+
+#ifdef SO_NOSIGPIPE
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0)
+ err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)");
+ test_write_dontsignal("test_write_dontsignal(PF_LOCAL)", sock[0],
+ sock[1]);
+#endif
+
+ /*
+ * TCP.
+ */
+ tcp_setup(port);
+ tcp_pair(port, sock);
+ test_send_wantsignal("test_send_wantsignal(PF_INET)", sock[0],
+ sock[1]);
+
+#ifdef SO_NOSIGPIPE
+ tcp_pair(port, sock);
+ test_send_dontsignal("test_send_dontsignal(PF_INET)", sock[0],
+ sock[1]);
+#endif
+
+ tcp_pair(port, sock);
+ test_write_wantsignal("test_write_wantsignal(PF_INET)", sock[0],
+ sock[1]);
+
+#ifdef SO_NOSIGPIPE
+ tcp_pair(port, sock);
+ test_write_dontsignal("test_write_dontsignal(PF_INET)", sock[0],
+ sock[1]);
+#endif
+ tcp_teardown();
+
+ fprintf(stderr, "PASS\n");
+ return (0);
+}
diff --git a/tools/regression/sockets/so_setfib/Makefile b/tools/regression/sockets/so_setfib/Makefile
new file mode 100644
index 000000000000..3670514e6c42
--- /dev/null
+++ b/tools/regression/sockets/so_setfib/Makefile
@@ -0,0 +1,13 @@
+.include <src.opts.mk>
+
+PROG= so_setfib
+MAN=
+
+.if ${MK_INET} != "no"
+CFLAGS+= -DINET
+.endif
+.if ${MK_INET6} != "no"
+CFLAGS+= -DINET6
+.endif
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/so_setfib/so_setfib.c b/tools/regression/sockets/so_setfib/so_setfib.c
new file mode 100644
index 000000000000..9d3110a92499
--- /dev/null
+++ b/tools/regression/sockets/so_setfib/so_setfib.c
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 2012 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * This software was developed by Bjoern Zeeb under contract to
+ * Cisco Systems, Inc..
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Regression test on SO_SETFIB setsockopt(2).
+ *
+ * Check that the expected domain(9) families all handle the socket option
+ * correctly and do proper bounds checks.
+ *
+ * Test plan:
+ * 1. Get system wide number of FIBs from sysctl and convert to index (-= 1).
+ * 2. For each protocol family (INET, INET6, ROUTE and LOCAL) open socketes of
+ * type (STREAM, DGRAM and RAW) as supported.
+ * 3. Do a sequence of -2, -1, 0, .. n, n+1, n+2 SO_SETFIB sockopt calls,
+ * expecting the first two and last two to fail (valid 0 ... n).
+ * 4. Try 3 random numbers. Calculate result based on valid range.
+ * 5. Repeat for next domain family and type from (2) on.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static struct t_dom {
+ int domain;
+ const char *name;
+} t_dom[] = {
+#ifdef INET6
+ { .domain = PF_INET6, .name = "PF_INET6" },
+#endif
+#ifdef INET
+ { .domain = PF_INET, .name = "PF_INET" },
+#endif
+ { .domain = PF_ROUTE, .name = "PF_ROUTE" },
+ { .domain = PF_LOCAL, .name = "PF_LOCAL" },
+};
+
+static struct t_type {
+ int type;
+ const char *name;
+} t_type[] = {
+ { .type = SOCK_STREAM, .name = "SOCK_STREAM" },
+ { .type = SOCK_DGRAM, .name = "SOCK_DGRAM" },
+ { .type = SOCK_RAW, .name = "SOCK_RAW" },
+};
+
+/*
+ * Number of FIBs as read from net.fibs sysctl - 1. Initialize to clear out of
+ * bounds value to not accidentally run on a limited range.
+ */
+static int rt_numfibs = -42;
+
+/* Number of test case. */
+static int testno = 1;
+
+
+/*
+ * Try the setsockopt with given FIB number i on socket s.
+ * Handle result given on error and valid range and errno.
+ */
+static void
+so_setfib(int s, int i, u_int dom, u_int type)
+{
+ int error;
+
+ error = setsockopt(s, SOL_SOCKET, SO_SETFIB, &i, sizeof(i));
+ /* For out of bounds we expect an error. */
+ if (error == -1 && (i < 0 || i > rt_numfibs))
+ printf("ok %d %s_%s_%d\n", testno, t_dom[dom].name,
+ t_type[type].name, i);
+ else if (error != -1 && (i < 0 || i > rt_numfibs))
+ printf("not ok %d %s_%s_%d # setsockopt(%d, SOL_SOCKET, "
+ "SO_SETFIB, %d, ..) unexpectedly succeeded\n", testno,
+ t_dom[dom].name, t_type[type].name, i, s, i);
+ else if (error == 0)
+ printf("ok %d %s_%s_%d\n", testno, t_dom[dom].name,
+ t_type[type].name, i);
+ else if (errno != EINVAL)
+ printf("not ok %d %s_%s_%d # setsockopt(%d, SOL_SOCKET, "
+ "SO_SETFIB, %d, ..) unexpected error: %s\n", testno,
+ t_dom[dom].name, t_type[type].name, i, s, i,
+ strerror(errno));
+ else
+ printf("not ok %d %s_%s_%d\n", testno, t_dom[dom].name,
+ t_type[type].name, i);
+
+ /* Test run done, next please. */
+ testno++;
+}
+
+/*
+ * Main test. Open socket given domain family and type. For each FIB, out of
+ * bounds FIB numbers and 3 random FIB numbers set the socket option.
+ */
+static void
+t(u_int dom, u_int type)
+{
+ int i, s;
+
+ /* PF_ROUTE only supports RAW socket types, while PF_LOCAL does not. */
+ if (t_dom[dom].domain == PF_ROUTE && t_type[type].type != SOCK_RAW)
+ return;
+ if (t_dom[dom].domain == PF_LOCAL && t_type[type].type == SOCK_RAW)
+ return;
+
+ /* Open socket for given combination. */
+ s = socket(t_dom[dom].domain, t_type[type].type, 0);
+ if (s == -1) {
+ printf("not ok %d %s_%s # socket(): %s\n", testno,
+ t_dom[dom].name, t_type[type].name, strerror(errno));
+ testno++;
+ return;
+ }
+
+ /* Test FIBs -2, -1, 0, .. n, n + 1, n + 2. */
+ for (i = -2; i <= (rt_numfibs + 2); i++)
+ so_setfib(s, i, dom, type);
+
+ /* Test 3 random FIB numbers. */
+ for (i = 0; i < 3; i++)
+ so_setfib(s, (int)random(), dom, type);
+
+ /* Close socket. */
+ close(s);
+}
+
+/*
+ * Returns 0 if no program error, 1 on sysctlbyname error.
+ * Test results are communicated by printf("[not ]ok <n> ..").
+ */
+int
+main(int argc __unused, char *argv[] __unused)
+{
+ u_int i, j;
+ size_t s;
+
+ if (geteuid() != 0) {
+ printf("1..0 # SKIP: must be root\n");
+ return (0);
+ }
+
+ /* Initialize randomness. */
+ srandomdev();
+
+ /* Get number of FIBs supported by kernel. */
+ s = sizeof(rt_numfibs);
+ if (sysctlbyname("net.fibs", &rt_numfibs, &s, NULL, 0) == -1)
+ err(1, "sysctlbyname(net.fibs, ..)");
+
+ printf("1..%lu\n",
+ (nitems(t_dom) - 1) * nitems(t_type) * (2 + rt_numfibs + 2 + 3));
+
+ /* Adjust from number to index. */
+ rt_numfibs -= 1;
+
+ /* Run tests. */
+ for (i = 0; i < sizeof(t_dom) / sizeof(struct t_dom); i++)
+ for (j = 0; j < sizeof(t_type) / sizeof(struct t_type); j++)
+ t(i, j);
+
+ return (0);
+}
+
+/* end */
diff --git a/tools/regression/sockets/udp_pingpong/Makefile b/tools/regression/sockets/udp_pingpong/Makefile
new file mode 100644
index 000000000000..72090d725e75
--- /dev/null
+++ b/tools/regression/sockets/udp_pingpong/Makefile
@@ -0,0 +1,4 @@
+PROG= udp_pingpong
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/udp_pingpong/udp_pingpong.c b/tools/regression/sockets/udp_pingpong/udp_pingpong.c
new file mode 100644
index 000000000000..e852f7a7af87
--- /dev/null
+++ b/tools/regression/sockets/udp_pingpong/udp_pingpong.c
@@ -0,0 +1,621 @@
+/*-
+ * Copyright (c) 2017 Maksym Sobolyev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * The test that setups two processes A and B and make A sending
+ * B UDP packet(s) and B send it back. The time of sending is recorded
+ * in the payload and time of the arrival is either determined by
+ * reading clock after recv() completes or using kernel-supplied
+ * via recvmsg(). End-to-end time t(A->B->A) is then calculated
+ * and compared against time for both t(A->B) + t(B->A) to make
+ * sure it makes sense.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+
+#define NPKTS 1000
+#define PKT_SIZE 128
+/* Timeout to receive pong on the side A, 100ms */
+#define SRECV_TIMEOUT (1 * 100)
+/*
+ * Timeout to receive ping on the side B. 4x as large as on the side A,
+ * so that in the case of packet loss the side A will have a chance to
+ * realize that and send few more before B bails out.
+ */
+#define RRECV_TIMEOUT (SRECV_TIMEOUT * 4)
+#define MIN_NRECV ((NPKTS * 99) / 100) /* 99% */
+
+//#define SIMULATE_PLOSS
+
+struct trip_ts {
+ struct timespec sent;
+ struct timespec recvd;
+};
+
+struct test_pkt {
+ int pnum;
+ struct trip_ts tss[2];
+ int lost;
+ unsigned char data[PKT_SIZE];
+};
+
+struct test_ctx {
+ const char *name;
+ int fds[2];
+ struct pollfd pfds[2];
+ union {
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ } sin[2];
+ struct test_pkt test_pkts[NPKTS];
+ int nsent;
+ int nrecvd;
+ clockid_t clock;
+ int use_recvmsg;
+ int ts_type;
+};
+
+struct rtt {
+ struct timespec a2b;
+ struct timespec b2a;
+ struct timespec e2e;
+ struct timespec a2b_b2a;
+};
+
+#define SEC(x) ((x)->tv_sec)
+#define NSEC(x) ((x)->tv_nsec)
+#define NSEC_MAX 1000000000L
+#define NSEC_IN_USEC 1000L
+
+#define timeval2timespec(tv, ts) \
+ do { \
+ SEC(ts) = (tv)->tv_sec; \
+ NSEC(ts) = (tv)->tv_usec * NSEC_IN_USEC; \
+ } while (0);
+
+static const struct timespec zero_ts;
+/* 0.01s, should be more than enough for the loopback communication */
+static const struct timespec max_ts = {.tv_nsec = (NSEC_MAX / 100)};
+
+enum ts_types {TT_TIMESTAMP = -2, TT_BINTIME = -1,
+ TT_REALTIME_MICRO = SO_TS_REALTIME_MICRO, TT_TS_BINTIME = SO_TS_BINTIME,
+ TT_REALTIME = SO_TS_REALTIME, TT_MONOTONIC = SO_TS_MONOTONIC};
+
+static clockid_t
+get_clock_type(struct test_ctx *tcp)
+{
+ switch (tcp->ts_type) {
+ case TT_TIMESTAMP:
+ case TT_BINTIME:
+ case TT_REALTIME_MICRO:
+ case TT_TS_BINTIME:
+ case TT_REALTIME:
+ return (CLOCK_REALTIME);
+
+ case TT_MONOTONIC:
+ return (CLOCK_MONOTONIC);
+ }
+ abort();
+}
+
+static int
+get_scm_type(struct test_ctx *tcp)
+{
+ switch (tcp->ts_type) {
+ case TT_TIMESTAMP:
+ case TT_REALTIME_MICRO:
+ return (SCM_TIMESTAMP);
+
+ case TT_BINTIME:
+ case TT_TS_BINTIME:
+ return (SCM_BINTIME);
+
+ case TT_REALTIME:
+ return (SCM_REALTIME);
+
+ case TT_MONOTONIC:
+ return (SCM_MONOTONIC);
+ }
+ abort();
+}
+
+static size_t
+get_scm_size(struct test_ctx *tcp)
+{
+ switch (tcp->ts_type) {
+ case TT_TIMESTAMP:
+ case TT_REALTIME_MICRO:
+ return (sizeof(struct timeval));
+
+ case TT_BINTIME:
+ case TT_TS_BINTIME:
+ return (sizeof(struct bintime));
+
+ case TT_REALTIME:
+ case TT_MONOTONIC:
+ return (sizeof(struct timespec));
+ }
+ abort();
+}
+
+static void
+setup_ts_sockopt(struct test_ctx *tcp, int fd)
+{
+ int rval, oname1, oname2, sval1, sval2;
+
+ oname1 = SO_TIMESTAMP;
+ oname2 = -1;
+ sval2 = -1;
+
+ switch (tcp->ts_type) {
+ case TT_REALTIME_MICRO:
+ case TT_TS_BINTIME:
+ case TT_REALTIME:
+ case TT_MONOTONIC:
+ oname2 = SO_TS_CLOCK;
+ sval2 = tcp->ts_type;
+ break;
+
+ case TT_TIMESTAMP:
+ break;
+
+ case TT_BINTIME:
+ oname1 = SO_BINTIME;
+ break;
+
+ default:
+ abort();
+ }
+
+ sval1 = 1;
+ rval = setsockopt(fd, SOL_SOCKET, oname1, &sval1,
+ sizeof(sval1));
+ if (rval != 0) {
+ err(1, "%s: setup_udp: setsockopt(%d, %d, 1)", tcp->name,
+ fd, oname1);
+ }
+ if (oname2 == -1)
+ return;
+ rval = setsockopt(fd, SOL_SOCKET, oname2, &sval2,
+ sizeof(sval2));
+ if (rval != 0) {
+ err(1, "%s: setup_udp: setsockopt(%d, %d, %d)",
+ tcp->name, fd, oname2, sval2);
+ }
+}
+
+
+static void
+setup_udp(struct test_ctx *tcp)
+{
+ int i;
+ socklen_t sin_len, af_len;
+
+ af_len = sizeof(tcp->sin[0].v4);
+ for (i = 0; i < 2; i++) {
+ tcp->sin[i].v4.sin_len = af_len;
+ tcp->sin[i].v4.sin_family = AF_INET;
+ tcp->sin[i].v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ tcp->fds[i] = socket(PF_INET, SOCK_DGRAM, 0);
+ if (tcp->fds[i] < 0)
+ err(1, "%s: setup_udp: socket", tcp->name);
+ if (bind(tcp->fds[i], (struct sockaddr *)&tcp->sin[i], af_len) < 0)
+ err(1, "%s: setup_udp: bind(%s, %d)", tcp->name,
+ inet_ntoa(tcp->sin[i].v4.sin_addr), 0);
+ sin_len = af_len;
+ if (getsockname(tcp->fds[i], (struct sockaddr *)&tcp->sin[i], &sin_len) < 0)
+ err(1, "%s: setup_udp: getsockname(%d)", tcp->name, tcp->fds[i]);
+ if (tcp->use_recvmsg != 0) {
+ setup_ts_sockopt(tcp, tcp->fds[i]);
+ }
+
+ tcp->pfds[i].fd = tcp->fds[i];
+ tcp->pfds[i].events = POLLIN;
+ }
+
+ if (connect(tcp->fds[0], (struct sockaddr *)&tcp->sin[1], af_len) < 0)
+ err(1, "%s: setup_udp: connect(%s, %d)", tcp->name,
+ inet_ntoa(tcp->sin[1].v4.sin_addr), ntohs(tcp->sin[1].v4.sin_port));
+ if (connect(tcp->fds[1], (struct sockaddr *)&tcp->sin[0], af_len) < 0)
+ err(1, "%s: setup_udp: connect(%s, %d)", tcp->name,
+ inet_ntoa(tcp->sin[0].v4.sin_addr), ntohs(tcp->sin[0].v4.sin_port));
+}
+
+static char *
+inet_ntoa6(const void *sin6_addr)
+{
+ static char straddr[INET6_ADDRSTRLEN];
+
+ inet_ntop(AF_INET6, sin6_addr, straddr, sizeof(straddr));
+ return (straddr);
+}
+
+static void
+setup_udp6(struct test_ctx *tcp)
+{
+ int i;
+ socklen_t sin_len, af_len;
+
+ af_len = sizeof(tcp->sin[0].v6);
+ for (i = 0; i < 2; i++) {
+ tcp->sin[i].v6.sin6_len = af_len;
+ tcp->sin[i].v6.sin6_family = AF_INET6;
+ tcp->sin[i].v6.sin6_addr = in6addr_loopback;
+ tcp->fds[i] = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (tcp->fds[i] < 0)
+ err(1, "%s: setup_udp: socket", tcp->name);
+ if (bind(tcp->fds[i], (struct sockaddr *)&tcp->sin[i], af_len) < 0)
+ err(1, "%s: setup_udp: bind(%s, %d)", tcp->name,
+ inet_ntoa6(&tcp->sin[i].v6.sin6_addr), 0);
+ sin_len = af_len;
+ if (getsockname(tcp->fds[i], (struct sockaddr *)&tcp->sin[i], &sin_len) < 0)
+ err(1, "%s: setup_udp: getsockname(%d)", tcp->name, tcp->fds[i]);
+ if (tcp->use_recvmsg != 0) {
+ setup_ts_sockopt(tcp, tcp->fds[i]);
+ }
+
+ tcp->pfds[i].fd = tcp->fds[i];
+ tcp->pfds[i].events = POLLIN;
+ }
+
+ if (connect(tcp->fds[0], (struct sockaddr *)&tcp->sin[1], af_len) < 0)
+ err(1, "%s: setup_udp: connect(%s, %d)", tcp->name,
+ inet_ntoa6(&tcp->sin[1].v6.sin6_addr),
+ ntohs(tcp->sin[1].v6.sin6_port));
+ if (connect(tcp->fds[1], (struct sockaddr *)&tcp->sin[0], af_len) < 0)
+ err(1, "%s: setup_udp: connect(%s, %d)", tcp->name,
+ inet_ntoa6(&tcp->sin[0].v6.sin6_addr),
+ ntohs(tcp->sin[0].v6.sin6_port));
+}
+
+static void
+teardown_udp(struct test_ctx *tcp)
+{
+
+ close(tcp->fds[0]);
+ close(tcp->fds[1]);
+}
+
+static void
+send_pkt(struct test_ctx *tcp, int pnum, int fdidx, const char *face)
+{
+ ssize_t r;
+ size_t slen;
+
+ slen = sizeof(tcp->test_pkts[pnum]);
+ clock_gettime(get_clock_type(tcp), &tcp->test_pkts[pnum].tss[fdidx].sent);
+ r = send(tcp->fds[fdidx], &tcp->test_pkts[pnum], slen, 0);
+ if (r < 0) {
+ err(1, "%s: %s: send(%d)", tcp->name, face, tcp->fds[fdidx]);
+ }
+ if (r < (ssize_t)slen) {
+ errx(1, "%s: %s: send(%d): short send", tcp->name, face,
+ tcp->fds[fdidx]);
+ }
+ tcp->nsent += 1;
+}
+
+#define PDATA(tcp, i) ((tcp)->test_pkts[(i)].data)
+
+static void
+hdr_extract_ts(struct test_ctx *tcp, struct msghdr *mhp, struct timespec *tp)
+{
+ int scm_type;
+ size_t scm_size;
+ union {
+ struct timespec ts;
+ struct bintime bt;
+ struct timeval tv;
+ } tdata;
+ struct cmsghdr *cmsg;
+
+ scm_type = get_scm_type(tcp);
+ scm_size = get_scm_size(tcp);
+ for (cmsg = CMSG_FIRSTHDR(mhp); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(mhp, cmsg)) {
+ if ((cmsg->cmsg_level == SOL_SOCKET) &&
+ (cmsg->cmsg_type == scm_type)) {
+ memcpy(&tdata, CMSG_DATA(cmsg), scm_size);
+ break;
+ }
+ }
+ if (cmsg == NULL) {
+ abort();
+ }
+ switch (tcp->ts_type) {
+ case TT_REALTIME:
+ case TT_MONOTONIC:
+ *tp = tdata.ts;
+ break;
+
+ case TT_TIMESTAMP:
+ case TT_REALTIME_MICRO:
+ timeval2timespec(&tdata.tv, tp);
+ break;
+
+ case TT_BINTIME:
+ case TT_TS_BINTIME:
+ bintime2timespec(&tdata.bt, tp);
+ break;
+
+ default:
+ abort();
+ }
+}
+
+static void
+recv_pkt_recvmsg(struct test_ctx *tcp, int fdidx, const char *face, void *buf,
+ size_t rlen, struct timespec *tp)
+{
+ /* We use a union to make sure hdr is aligned */
+ union {
+ struct cmsghdr hdr;
+ unsigned char buf[CMSG_SPACE(1024)];
+ } cmsgbuf;
+ struct msghdr msg;
+ struct iovec iov;
+ ssize_t rval;
+
+ memset(&msg, '\0', sizeof(msg));
+ iov.iov_base = buf;
+ iov.iov_len = rlen;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ rval = recvmsg(tcp->fds[fdidx], &msg, 0);
+ if (rval < 0) {
+ err(1, "%s: %s: recvmsg(%d)", tcp->name, face, tcp->fds[fdidx]);
+ }
+ if (rval < (ssize_t)rlen) {
+ errx(1, "%s: %s: recvmsg(%d): short recv", tcp->name, face,
+ tcp->fds[fdidx]);
+ }
+
+ hdr_extract_ts(tcp, &msg, tp);
+}
+
+static void
+recv_pkt_recv(struct test_ctx *tcp, int fdidx, const char *face, void *buf,
+ size_t rlen, struct timespec *tp)
+{
+ ssize_t rval;
+
+ rval = recv(tcp->fds[fdidx], buf, rlen, 0);
+ clock_gettime(get_clock_type(tcp), tp);
+ if (rval < 0) {
+ err(1, "%s: %s: recv(%d)", tcp->name, face, tcp->fds[fdidx]);
+ }
+ if (rval < (ssize_t)rlen) {
+ errx(1, "%s: %s: recv(%d): short recv", tcp->name, face,
+ tcp->fds[fdidx]);
+ }
+}
+
+static int
+recv_pkt(struct test_ctx *tcp, int fdidx, const char *face, int tout)
+{
+ int pr;
+ struct test_pkt recv_buf;
+ size_t rlen;
+
+ pr = poll(&tcp->pfds[fdidx], 1, tout);
+ if (pr < 0) {
+ err(1, "%s: %s: poll(%d)", tcp->name, face, tcp->fds[fdidx]);
+ }
+ if (pr == 0) {
+ return (-1);
+ }
+ if(tcp->pfds[fdidx].revents != POLLIN) {
+ errx(1, "%s: %s: poll(%d): unexpected result", tcp->name, face,
+ tcp->fds[fdidx]);
+ }
+ rlen = sizeof(recv_buf);
+ if (tcp->use_recvmsg == 0) {
+ recv_pkt_recv(tcp, fdidx, face, &recv_buf, rlen,
+ &recv_buf.tss[fdidx].recvd);
+ } else {
+ recv_pkt_recvmsg(tcp, fdidx, face, &recv_buf, rlen,
+ &recv_buf.tss[fdidx].recvd);
+ }
+ if (recv_buf.pnum < 0 || recv_buf.pnum >= NPKTS ||
+ memcmp(recv_buf.data, PDATA(tcp, recv_buf.pnum), PKT_SIZE) != 0) {
+ errx(1, "%s: %s: recv(%d): corrupted data, packet %d", tcp->name,
+ face, tcp->fds[fdidx], recv_buf.pnum);
+ }
+ tcp->nrecvd += 1;
+ memcpy(tcp->test_pkts[recv_buf.pnum].tss, recv_buf.tss,
+ sizeof(recv_buf.tss));
+ tcp->test_pkts[recv_buf.pnum].lost = 0;
+ return (recv_buf.pnum);
+}
+
+static void
+test_server(struct test_ctx *tcp)
+{
+ int i, j;
+
+ for (i = 0; i < NPKTS; i++) {
+ send_pkt(tcp, i, 0, __FUNCTION__);
+ j = recv_pkt(tcp, 0, __FUNCTION__, SRECV_TIMEOUT);
+ if (j < 0) {
+ warnx("packet %d is lost", i);
+ /* timeout */
+ continue;
+ }
+ }
+}
+
+static void
+test_client(struct test_ctx *tcp)
+{
+ int i, j;
+
+ for (i = 0; i < NPKTS; i++) {
+ j = recv_pkt(tcp, 1, __FUNCTION__, RRECV_TIMEOUT);
+ if (j < 0) {
+ /* timeout */
+ return;
+ }
+#if defined(SIMULATE_PLOSS)
+ if ((i % 99) == 0) {
+ warnx("dropping packet %d", i);
+ continue;
+ }
+#endif
+ send_pkt(tcp, j, 1, __FUNCTION__);
+ }
+}
+
+static void
+calc_rtt(struct test_pkt *tpp, struct rtt *rttp)
+{
+
+ timespecsub(&tpp->tss[1].recvd, &tpp->tss[0].sent, &rttp->a2b);
+ timespecsub(&tpp->tss[0].recvd, &tpp->tss[1].sent, &rttp->b2a);
+ timespecadd(&rttp->a2b, &rttp->b2a, &rttp->a2b_b2a);
+ timespecsub(&tpp->tss[0].recvd, &tpp->tss[0].sent, &rttp->e2e);
+}
+
+static void
+test_run(int ts_type, int use_ipv6, int use_recvmsg, const char *name)
+{
+ struct test_ctx test_ctx;
+ pid_t pid, cpid;
+ int i, j, status;
+
+ printf("Testing %s via %s: ", name, (use_ipv6 == 0) ? "IPv4" : "IPv6");
+ fflush(stdout);
+ bzero(&test_ctx, sizeof(test_ctx));
+ test_ctx.name = name;
+ test_ctx.use_recvmsg = use_recvmsg;
+ test_ctx.ts_type = ts_type;
+ if (use_ipv6 == 0) {
+ setup_udp(&test_ctx);
+ } else {
+ setup_udp6(&test_ctx);
+ }
+ for (i = 0; i < NPKTS; i++) {
+ test_ctx.test_pkts[i].pnum = i;
+ test_ctx.test_pkts[i].lost = 1;
+ for (j = 0; j < PKT_SIZE; j++) {
+ test_ctx.test_pkts[i].data[j] = (unsigned char)random();
+ }
+ }
+ cpid = fork();
+ if (cpid < 0) {
+ err(1, "%s: fork()", test_ctx.name);
+ }
+ if (cpid == 0) {
+ test_client(&test_ctx);
+ exit(0);
+ }
+ test_server(&test_ctx);
+ pid = waitpid(cpid, &status, 0);
+ if (pid == (pid_t)-1) {
+ err(1, "%s: waitpid(%d)", test_ctx.name, cpid);
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+ errx(1, "client exit status is %d",
+ WEXITSTATUS(status));
+ }
+ } else {
+ if (WIFSIGNALED(status))
+ errx(1, "abnormal termination of client, signal %d%s",
+ WTERMSIG(status), WCOREDUMP(status) ?
+ " (core file generated)" : "");
+ else
+ errx(1, "termination of client, unknown status");
+ }
+ if (test_ctx.nrecvd < MIN_NRECV) {
+ errx(1, "packet loss is too high %d received out of %d, min %d",
+ test_ctx.nrecvd, test_ctx.nsent, MIN_NRECV);
+ }
+ for (i = 0; i < NPKTS; i++) {
+ struct rtt rtt;
+ if (test_ctx.test_pkts[i].lost != 0) {
+ continue;
+ }
+ calc_rtt(&test_ctx.test_pkts[i], &rtt);
+ if (!timespeccmp(&rtt.e2e, &rtt.a2b_b2a, >))
+ errx(1, "end-to-end trip time is too small");
+ if (!timespeccmp(&rtt.e2e, &max_ts, <))
+ errx(1, "end-to-end trip time is too large");
+ if (!timespeccmp(&rtt.a2b, &zero_ts, >))
+ errx(1, "A2B trip time is not positive");
+ if (!timespeccmp(&rtt.b2a, &zero_ts, >))
+ errx(1, "B2A trip time is not positive");
+ }
+ teardown_udp(&test_ctx);
+}
+
+int
+main(void)
+{
+ int i;
+
+ srandomdev();
+
+ for (i = 0; i < 2; i++) {
+ test_run(0, i, 0, "send()/recv()");
+ printf("OK\n");
+ test_run(TT_TIMESTAMP, i, 1,
+ "send()/recvmsg(), setsockopt(SO_TIMESTAMP, 1)");
+ printf("OK\n");
+ test_run(TT_BINTIME, i, 1,
+ "send()/recvmsg(), setsockopt(SO_BINTIME, 1)");
+ printf("OK\n");
+ test_run(TT_REALTIME_MICRO, i, 1,
+ "send()/recvmsg(), setsockopt(SO_TS_CLOCK, SO_TS_REALTIME_MICRO)");
+ printf("OK\n");
+ test_run(TT_TS_BINTIME, i, 1,
+ "send()/recvmsg(), setsockopt(SO_TS_CLOCK, SO_TS_BINTIME)");
+ printf("OK\n");
+ test_run(TT_REALTIME, i, 1,
+ "send()/recvmsg(), setsockopt(SO_TS_CLOCK, SO_TS_REALTIME)");
+ printf("OK\n");
+ test_run(TT_MONOTONIC, i, 1,
+ "send()/recvmsg(), setsockopt(SO_TS_CLOCK, SO_TS_MONOTONIC)");
+ printf("OK\n");
+ }
+ exit(0);
+}
diff --git a/tools/regression/sockets/unix_bindconnect/Makefile b/tools/regression/sockets/unix_bindconnect/Makefile
new file mode 100644
index 000000000000..0e5c32e67b92
--- /dev/null
+++ b/tools/regression/sockets/unix_bindconnect/Makefile
@@ -0,0 +1,4 @@
+PROG= unix_bindconnect
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/unix_bindconnect/unix_bindconnect.c b/tools/regression/sockets/unix_bindconnect/unix_bindconnect.c
new file mode 100644
index 000000000000..11487c70dc9c
--- /dev/null
+++ b/tools/regression/sockets/unix_bindconnect/unix_bindconnect.c
@@ -0,0 +1,316 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Simple regression test to exercise some error cases relating to the use of
+ * bind() and connect() on UNIX domain sockets. In particular, make sure
+ * that when two sockets rendezvous using the file system name space, they
+ * get the expected success/failure cases.
+ *
+ * TODO:
+ * - Check that the resulting file mode/owner are right.
+ * - Do the same tests with UNIX domain sockets.
+ * - Check the results of getsockaddr() and getpeeraddr().
+ */
+
+#define SOCK_NAME_ONE "socket.1"
+#define SOCK_NAME_TWO "socket.2"
+
+#define UNWIND_MAX 1024
+
+static int unwind_len;
+static struct unwind {
+ char u_path[PATH_MAX];
+} unwind_list[UNWIND_MAX];
+
+static void
+push_path(const char *path)
+{
+
+ if (unwind_len >= UNWIND_MAX)
+ err(-1, "push_path: one path too many (%s)", path);
+
+ strlcpy(unwind_list[unwind_len].u_path, path, PATH_MAX);
+ unwind_len++;
+}
+
+static void
+unwind(void)
+{
+ int i;
+
+ for (i = unwind_len - 1; i >= 0; i--) {
+ unlink(unwind_list[i].u_path);
+ rmdir(unwind_list[i].u_path);
+ }
+}
+
+static int
+bind_test(const char *directory_path)
+{
+ char socket_path[PATH_MAX];
+ struct sockaddr_un sun;
+ int sock1, sock2;
+
+ sock1 = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock1 < 0) {
+ warn("bind_test: socket(PF_UNIX, SOCK_STREAM, 0)");
+ return (-1);
+ }
+
+ if (snprintf(socket_path, sizeof(socket_path), "%s/%s",
+ directory_path, SOCK_NAME_ONE) >= PATH_MAX) {
+ warn("bind_test: snprintf(socket_path)");
+ close(sock1);
+ return (-1);
+ }
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_len = sizeof(sun);
+ sun.sun_family = AF_UNIX;
+ if (snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path)
+ >= (int)sizeof(sun.sun_path)) {
+ warn("bind_test: snprintf(sun.sun_path)");
+ close(sock1);
+ return (-1);
+ }
+
+ if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
+ warn("bind_test: bind(sun) #1");
+ close(sock1);
+ return (-1);
+ }
+
+ push_path(socket_path);
+
+ /*
+ * Once a STREAM UNIX domain socket has been bound, it can't be
+ * rebound. Expected error is EINVAL.
+ */
+ if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
+ warnx("bind_test: bind(sun) #2 succeeded");
+ close(sock1);
+ return (-1);
+ }
+ if (errno != EINVAL) {
+ warn("bind_test: bind(sun) #2");
+ close(sock1);
+ return (-1);
+ }
+
+ sock2 = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock2 < 0) {
+ warn("bind_test: socket(PF_UNIX, SOCK_STREAM, 0)");
+ close(sock1);
+ return (-1);
+ }
+
+ /*
+ * Since a socket is already bound to the pathname, it can't be bound
+ * to a second socket. Expected error is EADDRINUSE.
+ */
+ if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
+ warnx("bind_test: bind(sun) #3 succeeded");
+ close(sock1);
+ close(sock2);
+ return (-1);
+ }
+ if (errno != EADDRINUSE) {
+ warn("bind_test: bind(sun) #2");
+ close(sock1);
+ close(sock2);
+ return (-1);
+ }
+
+ close(sock1);
+
+ /*
+ * The socket bound to the pathname has been closed, but the pathname
+ * can't be reused without first being unlinked. Expected error is
+ * EADDRINUSE.
+ */
+ if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
+ warnx("bind_test: bind(sun) #4 succeeded");
+ close(sock2);
+ return (-1);
+ }
+ if (errno != EADDRINUSE) {
+ warn("bind_test: bind(sun) #4");
+ close(sock2);
+ return (-1);
+ }
+
+ unlink(socket_path);
+
+ /*
+ * The pathname is now free, so the socket should be able to bind to
+ * it.
+ */
+ if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
+ warn("bind_test: bind(sun) #5");
+ close(sock2);
+ return (-1);
+ }
+
+ close(sock2);
+ return (0);
+}
+
+static int
+connect_test(const char *directory_path)
+{
+ char socket_path[PATH_MAX];
+ struct sockaddr_un sun;
+ int sock1, sock2;
+
+ sock1 = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock1 < 0) {
+ warn("connect_test: socket(PF_UNIX, SOCK_STREAM, 0)");
+ return (-1);
+ }
+
+ if (snprintf(socket_path, sizeof(socket_path), "%s/%s",
+ directory_path, SOCK_NAME_TWO) >= PATH_MAX) {
+ warn("connect_test: snprintf(socket_path)");
+ close(sock1);
+ return (-1);
+ }
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_len = sizeof(sun);
+ sun.sun_family = AF_UNIX;
+ if (snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path)
+ >= (int)sizeof(sun.sun_path)) {
+ warn("connect_test: snprintf(sun.sun_path)");
+ close(sock1);
+ return (-1);
+ }
+
+ /*
+ * Try connecting to a path that doesn't yet exist. Should fail with
+ * ENOENT.
+ */
+ if (connect(sock1, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
+ warnx("connect_test: connect(sun) #1 succeeded");
+ close(sock1);
+ return (-1);
+ }
+ if (errno != ENOENT) {
+ warn("connect_test: connect(sun) #1");
+ close(sock1);
+ return (-1);
+ }
+
+ if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
+ warn("connect_test: bind(sun) #1");
+ close(sock1);
+ return (-1);
+ }
+
+ if (listen(sock1, 3) < 0) {
+ warn("connect_test: listen(sock1)");
+ close(sock1);
+ return (-1);
+ }
+
+ push_path(socket_path);
+
+ sock2 = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock2 < 0) {
+ warn("socket(PF_UNIX, SOCK_STREAM, 0)");
+ close(sock1);
+ return (-1);
+ }
+
+ /*
+ * Do a simple connect and make sure that works.
+ */
+ if (connect(sock2, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
+ warn("connect(sun) #2");
+ close(sock1);
+ return (-1);
+ }
+
+ close(sock2);
+
+ close(sock1);
+
+ sock2 = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock2 < 0) {
+ warn("socket(PF_UNIX, SOCK_STREAM, 0)");
+ return (-1);
+ }
+
+ /*
+ * Confirm that once the listen socket is closed, we get a
+ * connection refused (ECONNREFUSED) when attempting to connect to
+ * the pathname.
+ */
+ if (connect(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
+ warnx("connect(sun) #3 succeeded");
+ close(sock2);
+ return (-1);
+ }
+ if (errno != ECONNREFUSED) {
+ warn("connect(sun) #3");
+ close(sock2);
+ return (-1);
+ }
+
+ close(sock2);
+ unlink(socket_path);
+ return (0);
+}
+int
+main(void)
+{
+ char directory_path[PATH_MAX];
+ int error;
+
+ strlcpy(directory_path, "/tmp/unix_bind.XXXXXXX", PATH_MAX);
+ if (mkdtemp(directory_path) == NULL)
+ err(-1, "mkdtemp");
+ push_path(directory_path);
+
+ error = bind_test(directory_path);
+
+ if (error == 0)
+ error = connect_test(directory_path);
+
+ unwind();
+ return (error);
+}
diff --git a/tools/regression/sockets/unix_close_race/Makefile b/tools/regression/sockets/unix_close_race/Makefile
new file mode 100644
index 000000000000..ca151dc7af48
--- /dev/null
+++ b/tools/regression/sockets/unix_close_race/Makefile
@@ -0,0 +1,4 @@
+PROG= unix_close_race
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/unix_close_race/unix_close_race.c b/tools/regression/sockets/unix_close_race/unix_close_race.c
new file mode 100644
index 000000000000..fd7675576ae6
--- /dev/null
+++ b/tools/regression/sockets/unix_close_race/unix_close_race.c
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 2010 Mikolaj Golub
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This regression test attempts to trigger a race that occurs when both
+ * endpoints of a connected UNIX domain socket are closed at once. The two
+ * close paths may run concurrently leading to a call to sodisconnect() on an
+ * already-closed socket in kernel. Before it was fixed, this might lead to
+ * ENOTCONN being returned improperly from close().
+ *
+ * This race is fairly timing-dependent, so it effectively requires SMP, and
+ * may not even trigger then.
+ */
+
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+static char socket_path[] = "tmp.XXXXXXXX";
+
+#define USLEEP 100
+#define LOOPS 100000
+
+int
+main(void)
+{
+ struct sockaddr_un servaddr;
+ int listenfd, connfd, pid;
+ u_int counter, ncpus;
+ size_t len;
+
+ len = sizeof(ncpus);
+ if (sysctlbyname("kern.smp.cpus", &ncpus, &len, NULL, 0) < 0)
+ err(1, "kern.smp.cpus");
+ if (len != sizeof(ncpus))
+ errx(1, "kern.smp.cpus: invalid length");
+ if (ncpus < 2)
+ warnx("SMP not present, test may be unable to trigger race");
+
+ if (mkstemp(socket_path) == -1)
+ err(1, "mkstemp failed");
+ unlink(socket_path);
+
+ /*
+ * Create a UNIX domain socket that the child will repeatedly
+ * accept() from, and that the parent will repeatedly connect() to.
+ */
+ if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ err(1, "parent: socket error");
+ (void)unlink(socket_path);
+ bzero(&servaddr, sizeof(servaddr));
+ servaddr.sun_family = AF_LOCAL;
+ strcpy(servaddr.sun_path, socket_path);
+ if (bind(listenfd, (struct sockaddr *) &servaddr,
+ sizeof(servaddr)) < 0)
+ err(1, "parent: bind error");
+ if (listen(listenfd, 1024) < 0)
+ err(1, "parent: listen error");
+
+ pid = fork();
+ if (pid == -1)
+ err(1, "fork()");
+ if (pid != 0) {
+ /*
+ * In the parent, repeatedly connect and disconnect from the
+ * socket, attempting to induce the race.
+ */
+ close(listenfd);
+ sleep(1);
+ bzero(&servaddr, sizeof(servaddr));
+ servaddr.sun_family = AF_LOCAL;
+ strcpy(servaddr.sun_path, socket_path);
+ for (counter = 0; counter < LOOPS; counter++) {
+ if ((connfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ (void)kill(pid, SIGTERM);
+ err(1, "parent: socket error");
+ }
+ if (connect(connfd, (struct sockaddr *)&servaddr,
+ sizeof(servaddr)) < 0) {
+ (void)kill(pid, SIGTERM);
+ err(1, "parent: connect error");
+ }
+ if (close(connfd) < 0) {
+ (void)kill(pid, SIGTERM);
+ err(1, "parent: close error");
+ }
+ usleep(USLEEP);
+ }
+ (void)kill(pid, SIGTERM);
+ } else {
+ /*
+ * In the child, loop accepting and closing. We may pick up
+ * the race here so report errors from close().
+ */
+ for ( ; ; ) {
+ if ((connfd = accept(listenfd,
+ (struct sockaddr *)NULL, NULL)) < 0)
+ err(1, "child: accept error");
+ if (close(connfd) < 0)
+ err(1, "child: close error");
+ }
+ }
+ printf("OK\n");
+ exit(0);
+}
diff --git a/tools/regression/sockets/unix_cmsg/Makefile b/tools/regression/sockets/unix_cmsg/Makefile
new file mode 100644
index 000000000000..24ca702e0a94
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/Makefile
@@ -0,0 +1,37 @@
+PROG= unix_cmsg
+SRCS= ${AUTOSRCS} unix_cmsg.c uc_common.h uc_common.c \
+ t_generic.h t_generic.c t_peercred.h t_peercred.c \
+ t_cmsgcred.h t_cmsgcred.c t_sockcred.h t_sockcred.c \
+ t_cmsgcred_sockcred.h t_cmsgcred_sockcred.c t_cmsg_len.h t_cmsg_len.c \
+ uc_check_time.h uc_check_time.c
+CLEANFILES+= ${AUTOSRCS}
+MAN=
+CFLAGS+= -I${.CURDIR} -I${.OBJDIR}
+WARNS?= 3
+
+REXP_bintime= 's|%%TTYPE%%|bintime|g ; s|%%DTYPE%%|bintime|g ; \
+ s|%%SCM_TTYPE%%|SCM_BINTIME|g ; \
+ s|%%MAJ_MEMB%%|sec|g ; s|%%MIN_MEMB%%|frac|g'
+REXP_timeval= 's|%%TTYPE%%|timeval|g ; s|%%DTYPE%%|timeval|g ; \
+ s|%%SCM_TTYPE%%|SCM_TIMESTAMP|g ; \
+ s|%%MAJ_MEMB%%|tv_sec|g ; s|%%MIN_MEMB%%|tv_usec|g'
+REXP_timespec_real= 's|%%TTYPE%%|timespec_real|g ; s|%%DTYPE%%|timespec|g ; \
+ s|%%SCM_TTYPE%%|SCM_REALTIME|g ; \
+ s|%%MAJ_MEMB%%|tv_sec|g ; s|%%MIN_MEMB%%|tv_nsec|g'
+REXP_timespec_mono= 's|%%TTYPE%%|timespec_mono|g ; s|%%DTYPE%%|timespec|g ; \
+ s|%%SCM_TTYPE%%|SCM_MONOTONIC|g ; \
+ s|%%MAJ_MEMB%%|tv_sec|g ; s|%%MIN_MEMB%%|tv_nsec|g'
+
+.for ttype in bintime timeval timespec_real timespec_mono
+AUTOSRCS+= t_${ttype}.h t_${ttype}.c
+
+t_${ttype}.o: t_${ttype}.c t_${ttype}.h
+
+t_${ttype}.c: t_xxxtime.c.in
+ sed ${REXP_${ttype}} < ${.ALLSRC} > ${.TARGET}
+
+t_${ttype}.h: t_xxxtime.h.in
+ sed ${REXP_${ttype}} < ${.ALLSRC} > ${.TARGET}
+.endfor
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/unix_cmsg/README b/tools/regression/sockets/unix_cmsg/README
new file mode 100644
index 000000000000..ca9fa3c1b3b3
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/README
@@ -0,0 +1,159 @@
+
+About unix_cmsg
+===============
+
+This program is a collection of regression tests for ancillary data
+(control information) for PF_LOCAL sockets (local domain or Unix domain
+sockets). There are tests for stream and datagram sockets.
+
+Usually each test does following steps: creates Server, forks Client,
+Client sends something to Server, Server verifies whether everything is
+correct in received message(s).
+
+It is better to change the owner of unix_cmsg to some safe user
+(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
+check credentials can give correct results for wrong implementation.
+
+It is better to run this program by a user that belongs to more
+than 16 groups.
+
+Available options
+=================
+
+usage: unix_cmsg [-dh] [-n num] [-s size] [-t type] [-z value] [testno]
+
+ Options are:
+ -d Output debugging information
+ -h Output the help message and exit
+ -n num Number of messages to send
+ -s size Specify size of data for IPC
+ -t type Specify socket type (stream, dgram) for tests
+ -z value Do not send data in a message (bit 0x1), do not send
+ data array associated with a cmsghdr structure (bit 0x2)
+ testno Run one test by its number (require the -t option)
+
+Description
+===========
+
+If Client sends something to Server, then it sends 5 messages by default.
+Number of messages can be changed in the -n command line option. Number
+of messages will be given as N in the following descriptions.
+
+If Client sends something to Server, then it sends some data (few bytes)
+in each message by default. The size of this data can be changed by the -s
+command line option. The "-s 0" command line option means, that Client will
+send zero bytes represented by { NULL, 0 } value of struct iovec{}, referenced
+by the msg_iov field from struct msghdr{}. The "-z 1" or "-z 3" command line
+option means, that Client will send zero bytes represented by the NULL value
+in the msg_iov field from struct msghdr{}.
+
+If Client sends some ancillary data object, then this ancillary data object
+always has associated data array by default. The "-z 2" or "-z 3" option
+means, that Client will not send associated data array if possible.
+
+For SOCK_STREAM sockets:
+-----------------------
+
+ 1: Sending, receiving cmsgcred
+
+ Client connects to Server and sends N messages with SCM_CREDS ancillary
+ data object. Server should receive N messages, each message should
+ have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
+
+ 2: Receiving sockcred (listening socket)
+
+ Server creates a listening stream socket and sets the LOCAL_CREDS
+ socket option for it. Client connects to Server two times, each time
+ it sends N messages. Server accepts two connections and receives N
+ messages from each connection. The first message from each connection
+ should have SCM_CREDS ancillary data object followed by struct sockcred{},
+ next messages from the same connection should not have ancillary data.
+
+ 3: Receiving sockcred (accepted socket)
+
+ Client connects to Server. Server accepts connection and sets the
+ LOCAL_CREDS socket option for just accepted socket. Client sends N
+ messages to Server. Server should receive N messages, the first
+ message should have SCM_CREDS ancillary data object followed by
+ struct sockcred{}, next messages should not have ancillary data.
+
+ 4: Sending cmsgcred, receiving sockcred
+
+ Server creates a listening stream socket and sets the LOCAL_CREDS
+ socket option for it. Client connects to Server and sends N messages
+ with SCM_CREDS ancillary data object. Server should receive N messages,
+ the first message should have SCM_CREDS ancillary data object followed
+ by struct sockcred{}, each of next messages should have SCM_CREDS
+ ancillary data object followed by struct cmsgcred{}.
+
+ 5: Sending, receiving timeval
+
+ Client connects to Server and sends message with SCM_TIMESTAMP ancillary
+ data object. Server should receive one message with SCM_TIMESTAMP
+ ancillary data object followed by struct timeval{}.
+
+ 6: Sending, receiving bintime
+
+ Client connects to Server and sends message with SCM_BINTIME ancillary
+ data object. Server should receive one message with SCM_BINTIME
+ ancillary data object followed by struct bintime{}.
+
+ 7: Checking cmsghdr.cmsg_len
+
+ Client connects to Server and tries to send several messages with
+ SCM_CREDS ancillary data object that has wrong cmsg_len field in its
+ struct cmsghdr{}. All these attempts should fail, since cmsg_len
+ in all requests is less than CMSG_LEN(0).
+
+ 8: Check LOCAL_PEERCRED socket option
+
+ This test does not use ancillary data, but can be implemented here.
+ Client connects to Server. Both Client and Server verify that
+ credentials of the peer are correct using LOCAL_PEERCRED socket option.
+
+For SOCK_DGRAM sockets:
+----------------------
+
+ 1: Sending, receiving cmsgcred
+
+ Client connects to Server and sends N messages with SCM_CREDS ancillary
+ data object. Server should receive N messages, each message should
+ have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
+
+ 2: Receiving sockcred
+
+ Server creates datagram socket and sets the LOCAL_CREDS socket option
+ for it. Client sends N messages to Server. Server should receive N
+ messages, each message should have SCM_CREDS ancillary data object
+ followed by struct sockcred{}.
+
+ 3: Sending cmsgcred, receiving sockcred
+
+ Server creates datagram socket and sets the LOCAL_CREDS socket option
+ for it. Client sends N messages with SCM_CREDS ancillary data object
+ to Server. Server should receive N messages, the first message should
+ have SCM_CREDS ancillary data object followed by struct sockcred{},
+ each of next messages should have SCM_CREDS ancillary data object
+ followed by struct cmsgcred{}.
+
+ 4: Sending, receiving timeval
+
+ Client sends one message with SCM_TIMESTAMP ancillary data object
+ to Server. Server should receive one message with SCM_TIMESTAMP
+ ancillary data object followed by struct timeval{}.
+
+ 5: Sending, receiving bintime
+
+ Client sends one message with SCM_BINTIME ancillary data object
+ to Server. Server should receive one message with SCM_BINTIME
+ ancillary data object followed by struct bintime{}.
+
+ 6: Checking cmsghdr.cmsg_len
+
+ Client tries to send Server several messages with SCM_CREDS ancillary
+ data object that has wrong cmsg_len field in its struct cmsghdr{}.
+ All these attempts should fail, since cmsg_len in all requests is less
+ than CMSG_LEN(0).
+
+- Andrey Simonenko
+andreysimonenko@users.sourceforge.net
diff --git a/tools/regression/sockets/unix_cmsg/t_cmsg_len.c b/tools/regression/sockets/unix_cmsg/t_cmsg_len.c
new file mode 100644
index 000000000000..d9e4b6fb99ae
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_cmsg_len.c
@@ -0,0 +1,135 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "uc_common.h"
+#include "t_generic.h"
+#include "t_cmsg_len.h"
+
+#ifndef __LP64__
+static int
+t_cmsg_len_client(int fd)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ struct cmsghdr *cmsghdr;
+ void *cmsg_data;
+ size_t size, cmsg_size;
+ socklen_t socklen;
+ int rv;
+
+ if (uc_sync_recv() < 0)
+ return (-2);
+
+ rv = -2;
+
+ cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ uc_logmsg("malloc");
+ goto done;
+ }
+ uc_msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
+ SCM_CREDS, sizeof(struct cmsgcred));
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
+
+ if (uc_socket_connect(fd) < 0)
+ goto done;
+
+ size = msghdr.msg_iov != NULL ? msghdr.msg_iov->iov_len : 0;
+ rv = -1;
+ for (socklen = 0; socklen < CMSG_LEN(0); ++socklen) {
+ cmsghdr->cmsg_len = socklen;
+ uc_dbgmsg("send: data size %zu", size);
+ uc_dbgmsg("send: msghdr.msg_controllen %u",
+ (u_int)msghdr.msg_controllen);
+ uc_dbgmsg("send: cmsghdr.cmsg_len %u",
+ (u_int)cmsghdr->cmsg_len);
+ if (sendmsg(fd, &msghdr, 0) < 0) {
+ uc_dbgmsg("sendmsg(2) failed: %s; retrying",
+ strerror(errno));
+ continue;
+ }
+ uc_logmsgx("sent message with cmsghdr.cmsg_len %u < %u",
+ (u_int)cmsghdr->cmsg_len, (u_int)CMSG_LEN(0));
+ break;
+ }
+ if (socklen == CMSG_LEN(0))
+ rv = 0;
+
+ if (uc_sync_send() < 0) {
+ rv = -2;
+ goto done;
+ }
+done:
+ free(cmsg_data);
+ return (rv);
+}
+
+static int
+t_cmsg_len_server(int fd1)
+{
+ int fd2, rv;
+
+ if (uc_sync_send() < 0)
+ return (-2);
+
+ rv = -2;
+
+ if (uc_cfg.sock_type == SOCK_STREAM) {
+ fd2 = uc_socket_accept(fd1);
+ if (fd2 < 0)
+ goto done;
+ } else
+ fd2 = fd1;
+
+ if (uc_sync_recv() < 0)
+ goto done;
+
+ rv = 0;
+done:
+ if (uc_cfg.sock_type == SOCK_STREAM && fd2 >= 0)
+ if (uc_socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
+}
+
+int
+t_cmsg_len(void)
+{
+ return (t_generic(t_cmsg_len_client, t_cmsg_len_server));
+}
+#endif
diff --git a/tools/regression/sockets/unix_cmsg/t_cmsg_len.h b/tools/regression/sockets/unix_cmsg/t_cmsg_len.h
new file mode 100644
index 000000000000..d526dc306cc9
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_cmsg_len.h
@@ -0,0 +1,30 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * Copyright (c) 2016 Maksym Sobolyev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __LP64__
+int t_cmsg_len(void);
+#endif
diff --git a/tools/regression/sockets/unix_cmsg/t_cmsgcred.c b/tools/regression/sockets/unix_cmsg/t_cmsgcred.c
new file mode 100644
index 000000000000..a1f6dc43e1db
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_cmsgcred.c
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "uc_common.h"
+#include "t_generic.h"
+#include "t_cmsgcred.h"
+
+int
+t_cmsgcred_client(int fd)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ void *cmsg_data;
+ size_t cmsg_size;
+ int rv;
+
+ if (uc_sync_recv() < 0)
+ return (-2);
+
+ rv = -2;
+
+ cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ uc_logmsg("malloc");
+ goto done;
+ }
+ uc_msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
+ SCM_CREDS, sizeof(struct cmsgcred));
+
+ if (uc_socket_connect(fd) < 0)
+ goto done;
+
+ if (uc_message_sendn(fd, &msghdr) < 0)
+ goto done;
+
+ rv = 0;
+done:
+ free(cmsg_data);
+ return (rv);
+}
+
+static int
+t_cmsgcred_server(int fd1)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ struct cmsghdr *cmsghdr;
+ void *cmsg_data;
+ size_t cmsg_size;
+ u_int i;
+ int fd2, rv;
+
+ if (uc_sync_send() < 0)
+ return (-2);
+
+ fd2 = -1;
+ rv = -2;
+
+ cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ uc_logmsg("malloc");
+ goto done;
+ }
+
+ if (uc_cfg.sock_type == SOCK_STREAM) {
+ fd2 = uc_socket_accept(fd1);
+ if (fd2 < 0)
+ goto done;
+ } else
+ fd2 = fd1;
+
+ rv = -1;
+ for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) {
+ uc_dbgmsg("message #%u", i);
+
+ uc_msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
+ if (uc_message_recv(fd2, &msghdr) < 0) {
+ rv = -2;
+ break;
+ }
+
+ if (uc_check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
+ break;
+
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ if (uc_check_scm_creds_cmsgcred(cmsghdr) < 0)
+ break;
+ }
+ if (i > uc_cfg.ipc_msg.msg_num)
+ rv = 0;
+done:
+ free(cmsg_data);
+ if (uc_cfg.sock_type == SOCK_STREAM && fd2 >= 0)
+ if (uc_socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
+}
+
+int
+t_cmsgcred(void)
+{
+ return (t_generic(t_cmsgcred_client, t_cmsgcred_server));
+}
diff --git a/tools/regression/sockets/unix_cmsg/t_cmsgcred.h b/tools/regression/sockets/unix_cmsg/t_cmsgcred.h
new file mode 100644
index 000000000000..2a459260da41
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_cmsgcred.h
@@ -0,0 +1,28 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+int t_cmsgcred_client(int fd);
+int t_cmsgcred(void);
diff --git a/tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.c b/tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.c
new file mode 100644
index 000000000000..af4232470812
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "uc_common.h"
+#include "t_generic.h"
+#include "t_cmsgcred.h"
+#include "t_cmsgcred_sockcred.h"
+
+static int
+t_cmsgcred_sockcred_server(int fd1)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ struct cmsghdr *cmsghdr;
+ void *cmsg_data, *cmsg1_data, *cmsg2_data;
+ size_t cmsg_size, cmsg1_size, cmsg2_size;
+ u_int i;
+ int fd2, rv, val;
+
+ fd2 = -1;
+ rv = -2;
+
+ cmsg1_size = CMSG_SPACE(SOCKCREDSIZE(uc_cfg.proc_cred.gid_num));
+ cmsg2_size = CMSG_SPACE(sizeof(struct cmsgcred));
+ cmsg1_data = malloc(cmsg1_size);
+ cmsg2_data = malloc(cmsg2_size);
+ if (cmsg1_data == NULL || cmsg2_data == NULL) {
+ uc_logmsg("malloc");
+ goto done;
+ }
+
+ uc_dbgmsg("setting LOCAL_CREDS");
+ val = 1;
+ if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
+ uc_logmsg("setsockopt(LOCAL_CREDS)");
+ goto done;
+ }
+
+ if (uc_sync_send() < 0)
+ goto done;
+
+ if (uc_cfg.sock_type == SOCK_STREAM) {
+ fd2 = uc_socket_accept(fd1);
+ if (fd2 < 0)
+ goto done;
+ } else
+ fd2 = fd1;
+
+ cmsg_data = cmsg1_data;
+ cmsg_size = cmsg1_size;
+ rv = -1;
+ for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) {
+ uc_dbgmsg("message #%u", i);
+
+ uc_msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
+ if (uc_message_recv(fd2, &msghdr) < 0) {
+ rv = -2;
+ break;
+ }
+
+ if (uc_check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
+ break;
+
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ if (i == 1 || uc_cfg.sock_type == SOCK_DGRAM) {
+ if (uc_check_scm_creds_sockcred(cmsghdr) < 0)
+ break;
+ } else {
+ if (uc_check_scm_creds_cmsgcred(cmsghdr) < 0)
+ break;
+ }
+
+ cmsg_data = cmsg2_data;
+ cmsg_size = cmsg2_size;
+ }
+ if (i > uc_cfg.ipc_msg.msg_num)
+ rv = 0;
+done:
+ free(cmsg1_data);
+ free(cmsg2_data);
+ if (uc_cfg.sock_type == SOCK_STREAM && fd2 >= 0)
+ if (uc_socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
+}
+
+int
+t_cmsgcred_sockcred(void)
+{
+ return (t_generic(t_cmsgcred_client, t_cmsgcred_sockcred_server));
+}
diff --git a/tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.h b/tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.h
new file mode 100644
index 000000000000..ed0fecac4b2f
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.h
@@ -0,0 +1,28 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * Copyright (c) 2016 Maksym Sobolyev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+int t_cmsgcred_sockcred(void);
diff --git a/tools/regression/sockets/unix_cmsg/t_generic.c b/tools/regression/sockets/unix_cmsg/t_generic.c
new file mode 100644
index 000000000000..8b4e251e92fe
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_generic.c
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "uc_common.h"
+#include "t_generic.h"
+
+int
+t_generic(int (*client_func)(int), int (*server_func)(int))
+{
+ int fd, rv, rv_client;
+
+ switch (uc_client_fork()) {
+ case 0:
+ fd = uc_socket_create();
+ if (fd < 0)
+ rv = -2;
+ else {
+ rv = client_func(fd);
+ if (uc_socket_close(fd) < 0)
+ rv = -2;
+ }
+ uc_client_exit(rv);
+ break;
+ case 1:
+ fd = uc_socket_create();
+ if (fd < 0)
+ rv = -2;
+ else {
+ rv = server_func(fd);
+ rv_client = uc_client_wait();
+ if (rv == 0 || (rv == -2 && rv_client != 0))
+ rv = rv_client;
+ if (uc_socket_close(fd) < 0)
+ rv = -2;
+ }
+ break;
+ default:
+ rv = -2;
+ }
+ return (rv);
+}
diff --git a/tools/regression/sockets/unix_cmsg/t_generic.h b/tools/regression/sockets/unix_cmsg/t_generic.h
new file mode 100644
index 000000000000..25bfc1540d3e
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_generic.h
@@ -0,0 +1,28 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * Copyright (c) 2016 Maksym Sobolyev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+int t_generic(int (*client_func)(int), int (*server_func)(int));
diff --git a/tools/regression/sockets/unix_cmsg/t_peercred.c b/tools/regression/sockets/unix_cmsg/t_peercred.c
new file mode 100644
index 000000000000..cbea69eaea97
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_peercred.c
@@ -0,0 +1,150 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ucred.h>
+#include <sys/un.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "uc_common.h"
+#include "t_generic.h"
+#include "t_peercred.h"
+
+static int
+check_xucred(const struct xucred *xucred, socklen_t len)
+{
+ int rc;
+
+ if (len != sizeof(*xucred)) {
+ uc_logmsgx("option value size %zu != %zu",
+ (size_t)len, sizeof(*xucred));
+ return (-1);
+ }
+
+ uc_dbgmsg("xucred.cr_version %u", xucred->cr_version);
+ uc_dbgmsg("xucred.cr_uid %lu", (u_long)xucred->cr_uid);
+ uc_dbgmsg("xucred.cr_ngroups %d", xucred->cr_ngroups);
+
+ rc = 0;
+
+ if (xucred->cr_version != XUCRED_VERSION) {
+ uc_logmsgx("xucred.cr_version %u != %d",
+ xucred->cr_version, XUCRED_VERSION);
+ rc = -1;
+ }
+ if (xucred->cr_uid != uc_cfg.proc_cred.euid) {
+ uc_logmsgx("xucred.cr_uid %lu != %lu (EUID)",
+ (u_long)xucred->cr_uid, (u_long)uc_cfg.proc_cred.euid);
+ rc = -1;
+ }
+ if (xucred->cr_ngroups == 0) {
+ uc_logmsgx("xucred.cr_ngroups == 0");
+ rc = -1;
+ }
+ if (xucred->cr_ngroups < 0) {
+ uc_logmsgx("xucred.cr_ngroups < 0");
+ rc = -1;
+ }
+ if (xucred->cr_ngroups > XU_NGROUPS) {
+ uc_logmsgx("xucred.cr_ngroups %hu > %u (max)",
+ xucred->cr_ngroups, XU_NGROUPS);
+ rc = -1;
+ }
+ if (xucred->cr_groups[0] != uc_cfg.proc_cred.egid) {
+ uc_logmsgx("xucred.cr_groups[0] %lu != %lu (EGID)",
+ (u_long)xucred->cr_groups[0], (u_long)uc_cfg.proc_cred.egid);
+ rc = -1;
+ }
+ if (uc_check_groups("xucred.cr_groups", xucred->cr_groups,
+ "xucred.cr_ngroups", xucred->cr_ngroups, false) < 0)
+ rc = -1;
+ return (rc);
+}
+
+static int
+t_peercred_client(int fd)
+{
+ struct xucred xucred;
+ socklen_t len;
+
+ if (uc_sync_recv() < 0)
+ return (-1);
+
+ if (uc_socket_connect(fd) < 0)
+ return (-1);
+
+ len = sizeof(xucred);
+ if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
+ uc_logmsg("getsockopt(LOCAL_PEERCRED)");
+ return (-1);
+ }
+
+ if (check_xucred(&xucred, len) < 0)
+ return (-1);
+
+ return (0);
+}
+
+static int
+t_peercred_server(int fd1)
+{
+ struct xucred xucred;
+ socklen_t len;
+ int fd2, rv;
+
+ if (uc_sync_send() < 0)
+ return (-2);
+
+ fd2 = uc_socket_accept(fd1);
+ if (fd2 < 0)
+ return (-2);
+
+ len = sizeof(xucred);
+ if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
+ uc_logmsg("getsockopt(LOCAL_PEERCRED)");
+ rv = -2;
+ goto done;
+ }
+
+ if (check_xucred(&xucred, len) < 0) {
+ rv = -1;
+ goto done;
+ }
+
+ rv = 0;
+done:
+ if (uc_socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
+}
+
+int
+t_peercred(void)
+{
+ return (t_generic(t_peercred_client, t_peercred_server));
+}
diff --git a/tools/regression/sockets/unix_cmsg/t_peercred.h b/tools/regression/sockets/unix_cmsg/t_peercred.h
new file mode 100644
index 000000000000..7a2ac010487b
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_peercred.h
@@ -0,0 +1,27 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+int t_peercred(void);
diff --git a/tools/regression/sockets/unix_cmsg/t_sockcred.c b/tools/regression/sockets/unix_cmsg/t_sockcred.c
new file mode 100644
index 000000000000..d5171946f988
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_sockcred.c
@@ -0,0 +1,212 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "uc_common.h"
+#include "t_generic.h"
+#include "t_sockcred.h"
+
+static int
+t_sockcred_client(int type, int fd)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ int rv;
+
+ if (uc_sync_recv() < 0)
+ return (-2);
+
+ rv = -2;
+
+ uc_msghdr_init_client(&msghdr, iov, NULL, 0, 0, 0);
+
+ if (uc_socket_connect(fd) < 0)
+ goto done;
+
+ if (type == 2)
+ if (uc_sync_recv() < 0)
+ goto done;
+
+ if (uc_message_sendn(fd, &msghdr) < 0)
+ goto done;
+
+ rv = 0;
+done:
+ return (rv);
+}
+
+static int
+t_sockcred_server(int type, int fd1)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ struct cmsghdr *cmsghdr;
+ void *cmsg_data;
+ size_t cmsg_size;
+ u_int i;
+ int fd2, rv, val;
+
+ fd2 = -1;
+ rv = -2;
+
+ cmsg_size = CMSG_SPACE(SOCKCREDSIZE(uc_cfg.proc_cred.gid_num));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ uc_logmsg("malloc");
+ goto done;
+ }
+
+ if (type == 1) {
+ uc_dbgmsg("setting LOCAL_CREDS");
+ val = 1;
+ if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
+ uc_logmsg("setsockopt(LOCAL_CREDS)");
+ goto done;
+ }
+ }
+
+ if (uc_sync_send() < 0)
+ goto done;
+
+ if (uc_cfg.sock_type == SOCK_STREAM) {
+ fd2 = uc_socket_accept(fd1);
+ if (fd2 < 0)
+ goto done;
+ } else
+ fd2 = fd1;
+
+ if (type == 2) {
+ uc_dbgmsg("setting LOCAL_CREDS");
+ val = 1;
+ if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
+ uc_logmsg("setsockopt(LOCAL_CREDS)");
+ goto done;
+ }
+ if (uc_sync_send() < 0)
+ goto done;
+ }
+
+ rv = -1;
+ for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) {
+ uc_dbgmsg("message #%u", i);
+
+ uc_msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
+ if (uc_message_recv(fd2, &msghdr) < 0) {
+ rv = -2;
+ break;
+ }
+
+ if (i > 1 && uc_cfg.sock_type == SOCK_STREAM) {
+ if (uc_check_msghdr(&msghdr, 0) < 0)
+ break;
+ } else {
+ if (uc_check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
+ break;
+
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ if (uc_check_scm_creds_sockcred(cmsghdr) < 0)
+ break;
+ }
+ }
+ if (i > uc_cfg.ipc_msg.msg_num)
+ rv = 0;
+done:
+ free(cmsg_data);
+ if (uc_cfg.sock_type == SOCK_STREAM && fd2 >= 0)
+ if (uc_socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
+}
+
+int
+t_sockcred_1(void)
+{
+ u_int i;
+ int fd, rv, rv_client;
+
+ switch (uc_client_fork()) {
+ case 0:
+ for (i = 1; i <= 2; ++i) {
+ uc_dbgmsg("client #%u", i);
+ fd = uc_socket_create();
+ if (fd < 0)
+ rv = -2;
+ else {
+ rv = t_sockcred_client(1, fd);
+ if (uc_socket_close(fd) < 0)
+ rv = -2;
+ }
+ if (rv != 0)
+ break;
+ }
+ uc_client_exit(rv);
+ break;
+ case 1:
+ fd = uc_socket_create();
+ if (fd < 0)
+ rv = -2;
+ else {
+ rv = t_sockcred_server(1, fd);
+ if (rv == 0)
+ rv = t_sockcred_server(3, fd);
+ rv_client = uc_client_wait();
+ if (rv == 0 || (rv == -2 && rv_client != 0))
+ rv = rv_client;
+ if (uc_socket_close(fd) < 0)
+ rv = -2;
+ }
+ break;
+ default:
+ rv = -2;
+ }
+
+ return (rv);
+}
+
+static int
+t_sockcred_2_client(int fd)
+{
+ return (t_sockcred_client(2, fd));
+}
+
+static int
+t_sockcred_2_server(int fd)
+{
+ return (t_sockcred_server(2, fd));
+}
+
+int
+t_sockcred_2(void)
+{
+ return (t_generic(t_sockcred_2_client, t_sockcred_2_server));
+}
diff --git a/tools/regression/sockets/unix_cmsg/t_sockcred.h b/tools/regression/sockets/unix_cmsg/t_sockcred.h
new file mode 100644
index 000000000000..1f4bf6ab57e9
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_sockcred.h
@@ -0,0 +1,29 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * Copyright (c) 2016 Maksym Sobolyev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+int t_sockcred_1(void);
+int t_sockcred_2(void);
diff --git a/tools/regression/sockets/unix_cmsg/t_xxxtime.c.in b/tools/regression/sockets/unix_cmsg/t_xxxtime.c.in
new file mode 100644
index 000000000000..332bab621fbf
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_xxxtime.c.in
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "t_%%TTYPE%%.h"
+#include "t_generic.h"
+#include "uc_common.h"
+#include "uc_check_time.h"
+
+#if defined(%%SCM_TTYPE%%)
+static int
+check_scm_%%TTYPE%%(struct cmsghdr *cmsghdr)
+{
+ const struct %%DTYPE%% *bt;
+
+ if (uc_check_cmsghdr(cmsghdr, %%SCM_TTYPE%%, sizeof(struct %%DTYPE%%)) < 0)
+ return (-1);
+
+ bt = (struct %%DTYPE%% *)CMSG_DATA(cmsghdr);
+
+ if (uc_check_%%TTYPE%%(bt) < 0)
+ return (-1);
+
+ uc_dbgmsg("%%DTYPE%%.%%MAJ_MEMB%% %"PRIdMAX", %%DTYPE%%.%%MIN_MEMB%% %"PRIuMAX,
+ (intmax_t)bt->%%MAJ_MEMB%%, (uintmax_t)bt->%%MIN_MEMB%%);
+
+ return (0);
+}
+
+static int
+t_%%TTYPE%%_client(int fd)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ void *cmsg_data;
+ size_t cmsg_size;
+ int rv;
+
+ if (uc_sync_recv() < 0)
+ return (-2);
+
+ rv = -2;
+
+ cmsg_size = CMSG_SPACE(sizeof(struct %%DTYPE%%));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ uc_logmsg("malloc");
+ goto done;
+ }
+ uc_msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
+ %%SCM_TTYPE%%, sizeof(struct %%DTYPE%%));
+
+ if (uc_socket_connect(fd) < 0)
+ goto done;
+
+ if (uc_message_sendn(fd, &msghdr) < 0)
+ goto done;
+
+ rv = 0;
+done:
+ free(cmsg_data);
+ return (rv);
+}
+
+static int
+t_%%TTYPE%%_server(int fd1)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ struct cmsghdr *cmsghdr;
+ void *cmsg_data;
+ size_t cmsg_size;
+ u_int i;
+ int fd2, rv;
+
+ if (uc_sync_send() < 0)
+ return (-2);
+
+ fd2 = -1;
+ rv = -2;
+
+ cmsg_size = CMSG_SPACE(sizeof(struct %%DTYPE%%));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ uc_logmsg("malloc");
+ goto done;
+ }
+
+ if (uc_cfg.sock_type == SOCK_STREAM) {
+ fd2 = uc_socket_accept(fd1);
+ if (fd2 < 0)
+ goto done;
+ } else
+ fd2 = fd1;
+
+ rv = -1;
+ for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) {
+ uc_dbgmsg("message #%u", i);
+
+ uc_msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
+ if (uc_message_recv(fd2, &msghdr) < 0) {
+ rv = -2;
+ break;
+ }
+
+ if (uc_check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
+ break;
+
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ if (check_scm_%%TTYPE%%(cmsghdr) < 0)
+ break;
+ }
+ if (i > uc_cfg.ipc_msg.msg_num)
+ rv = 0;
+done:
+ free(cmsg_data);
+ if (uc_cfg.sock_type == SOCK_STREAM && fd2 >= 0)
+ if (uc_socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
+}
+
+int
+t_%%TTYPE%%(void)
+{
+ return (t_generic(t_%%TTYPE%%_client, t_%%TTYPE%%_server));
+}
+#endif /* %%SCM_TTYPE%% */
diff --git a/tools/regression/sockets/unix_cmsg/t_xxxtime.h.in b/tools/regression/sockets/unix_cmsg/t_xxxtime.h.in
new file mode 100644
index 000000000000..e304ffb7c373
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/t_xxxtime.h.in
@@ -0,0 +1,30 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * Copyright (c) 2016 Maksym Sobolyev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(%%SCM_TTYPE%%)
+int t_%%TTYPE%%(void);
+#endif
diff --git a/tools/regression/sockets/unix_cmsg/uc_check_time.c b/tools/regression/sockets/unix_cmsg/uc_check_time.c
new file mode 100644
index 000000000000..abc20e84ade9
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/uc_check_time.c
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 2016 Maksym Sobolyev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "uc_check_time.h"
+
+static const struct timeval max_diff_tv = {.tv_sec = 1, .tv_usec = 0};
+static const struct timespec max_diff_ts = {.tv_sec = 1, .tv_nsec = 0};
+
+int
+uc_check_bintime(const struct bintime *mt)
+{
+ struct timespec bt;
+
+ bintime2timespec(mt, &bt);
+ return (uc_check_timespec_real(&bt));
+}
+
+int
+uc_check_timeval(const struct timeval *bt)
+{
+ struct timeval ct, dt;
+
+ if (gettimeofday(&ct, NULL) < 0)
+ return (-1);
+ timersub(&ct, bt, &dt);
+ if (!timercmp(&dt, &max_diff_tv, <))
+ return (-1);
+
+ return (0);
+}
+
+int
+uc_check_timespec_real(const struct timespec *bt)
+{
+ struct timespec ct;
+
+ if (clock_gettime(CLOCK_REALTIME, &ct) < 0)
+ return (-1);
+ timespecsub(&ct, bt, &ct);
+ if (!timespeccmp(&ct, &max_diff_ts, <))
+ return (-1);
+
+ return (0);
+}
+
+int
+uc_check_timespec_mono(const struct timespec *bt)
+{
+ struct timespec ct;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ct) < 0)
+ return (-1);
+ timespecsub(&ct, bt, &ct);
+ if (!timespeccmp(&ct, &max_diff_ts, <))
+ return (-1);
+
+ return (0);
+}
diff --git a/tools/regression/sockets/unix_cmsg/uc_check_time.h b/tools/regression/sockets/unix_cmsg/uc_check_time.h
new file mode 100644
index 000000000000..2190d244e33e
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/uc_check_time.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2016 Maksym Sobolyev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+struct bintime;
+struct timeval;
+struct timespec;
+
+int uc_check_bintime(const struct bintime *bt);
+int uc_check_timeval(const struct timeval *bt);
+int uc_check_timespec_real(const struct timespec *bt);
+int uc_check_timespec_mono(const struct timespec *bt);
diff --git a/tools/regression/sockets/unix_cmsg/uc_common.c b/tools/regression/sockets/unix_cmsg/uc_common.c
new file mode 100644
index 000000000000..bcb18a808d99
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/uc_common.c
@@ -0,0 +1,742 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <err.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "uc_common.h"
+
+#ifndef LISTENQ
+# define LISTENQ 1
+#endif
+
+#ifndef TIMEOUT
+# define TIMEOUT 2
+#endif
+
+#define SYNC_SERVER 0
+#define SYNC_CLIENT 1
+#define SYNC_RECV 0
+#define SYNC_SEND 1
+
+#define LOGMSG_SIZE 128
+
+void
+uc_output(const char *format, ...)
+{
+ char buf[LOGMSG_SIZE];
+ va_list ap;
+
+ va_start(ap, format);
+ if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
+ err(EXIT_FAILURE, "output: vsnprintf failed");
+ write(STDOUT_FILENO, buf, strlen(buf));
+ va_end(ap);
+}
+
+void
+uc_logmsg(const char *format, ...)
+{
+ char buf[LOGMSG_SIZE];
+ va_list ap;
+ int errno_save;
+
+ errno_save = errno;
+ va_start(ap, format);
+ if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
+ err(EXIT_FAILURE, "logmsg: vsnprintf failed");
+ if (errno_save == 0)
+ uc_output("%s: %s\n", uc_cfg.proc_name, buf);
+ else
+ uc_output("%s: %s: %s\n", uc_cfg.proc_name, buf,
+ strerror(errno_save));
+ va_end(ap);
+ errno = errno_save;
+}
+
+void
+uc_vlogmsgx(const char *format, va_list ap)
+{
+ char buf[LOGMSG_SIZE];
+
+ if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
+ err(EXIT_FAILURE, "uc_logmsgx: vsnprintf failed");
+ uc_output("%s: %s\n", uc_cfg.proc_name, buf);
+}
+
+void
+uc_logmsgx(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ uc_vlogmsgx(format, ap);
+ va_end(ap);
+}
+
+void
+uc_dbgmsg(const char *format, ...)
+{
+ va_list ap;
+
+ if (uc_cfg.debug) {
+ va_start(ap, format);
+ uc_vlogmsgx(format, ap);
+ va_end(ap);
+ }
+}
+
+int
+uc_socket_create(void)
+{
+ struct timeval tv;
+ int fd;
+
+ fd = socket(PF_LOCAL, uc_cfg.sock_type, 0);
+ if (fd < 0) {
+ uc_logmsg("socket_create: socket(PF_LOCAL, %s, 0)", uc_cfg.sock_type_str);
+ return (-1);
+ }
+ if (uc_cfg.server_flag)
+ uc_cfg.serv_sock_fd = fd;
+
+ tv.tv_sec = TIMEOUT;
+ tv.tv_usec = 0;
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
+ setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
+ uc_logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
+ goto failed;
+ }
+
+ if (uc_cfg.server_flag) {
+ if (bind(fd, (struct sockaddr *)&uc_cfg.serv_addr_sun,
+ uc_cfg.serv_addr_sun.sun_len) < 0) {
+ uc_logmsg("socket_create: bind(%s)",
+ uc_cfg.serv_addr_sun.sun_path);
+ goto failed;
+ }
+ if (uc_cfg.sock_type == SOCK_STREAM) {
+ int val;
+
+ if (listen(fd, LISTENQ) < 0) {
+ uc_logmsg("socket_create: listen");
+ goto failed;
+ }
+ val = fcntl(fd, F_GETFL, 0);
+ if (val < 0) {
+ uc_logmsg("socket_create: fcntl(F_GETFL)");
+ goto failed;
+ }
+ if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
+ uc_logmsg("socket_create: fcntl(F_SETFL)");
+ goto failed;
+ }
+ }
+ }
+
+ return (fd);
+
+failed:
+ if (close(fd) < 0)
+ uc_logmsg("socket_create: close");
+ if (uc_cfg.server_flag)
+ if (unlink(uc_cfg.serv_addr_sun.sun_path) < 0)
+ uc_logmsg("socket_close: unlink(%s)",
+ uc_cfg.serv_addr_sun.sun_path);
+ return (-1);
+}
+
+int
+uc_socket_close(int fd)
+{
+ int rv;
+
+ rv = 0;
+ if (close(fd) < 0) {
+ uc_logmsg("socket_close: close");
+ rv = -1;
+ }
+ if (uc_cfg.server_flag && fd == uc_cfg.serv_sock_fd)
+ if (unlink(uc_cfg.serv_addr_sun.sun_path) < 0) {
+ uc_logmsg("socket_close: unlink(%s)",
+ uc_cfg.serv_addr_sun.sun_path);
+ rv = -1;
+ }
+ return (rv);
+}
+
+int
+uc_socket_connect(int fd)
+{
+ uc_dbgmsg("connect");
+
+ if (connect(fd, (struct sockaddr *)&uc_cfg.serv_addr_sun,
+ uc_cfg.serv_addr_sun.sun_len) < 0) {
+ uc_logmsg("socket_connect: connect(%s)", uc_cfg.serv_addr_sun.sun_path);
+ return (-1);
+ }
+ return (0);
+}
+
+int
+uc_sync_recv(void)
+{
+ ssize_t ssize;
+ int fd;
+ char buf;
+
+ uc_dbgmsg("sync: wait");
+
+ fd = uc_cfg.sync_fd[uc_cfg.server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV];
+
+ ssize = read(fd, &buf, 1);
+ if (ssize < 0) {
+ uc_logmsg("sync_recv: read");
+ return (-1);
+ }
+ if (ssize < 1) {
+ uc_logmsgx("sync_recv: read %zd of 1 byte", ssize);
+ return (-1);
+ }
+
+ uc_dbgmsg("sync: received");
+
+ return (0);
+}
+
+int
+uc_sync_send(void)
+{
+ ssize_t ssize;
+ int fd;
+
+ uc_dbgmsg("sync: send");
+
+ fd = uc_cfg.sync_fd[uc_cfg.server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND];
+
+ ssize = write(fd, "", 1);
+ if (ssize < 0) {
+ uc_logmsg("uc_sync_send: write");
+ return (-1);
+ }
+ if (ssize < 1) {
+ uc_logmsgx("uc_sync_send: sent %zd of 1 byte", ssize);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+uc_message_send(int fd, const struct msghdr *msghdr)
+{
+ const struct cmsghdr *cmsghdr;
+ size_t size;
+ ssize_t ssize;
+
+ size = msghdr->msg_iov != 0 ? msghdr->msg_iov->iov_len : 0;
+ uc_dbgmsg("send: data size %zu", size);
+ uc_dbgmsg("send: msghdr.msg_controllen %u",
+ (u_int)msghdr->msg_controllen);
+ cmsghdr = CMSG_FIRSTHDR(msghdr);
+ if (cmsghdr != NULL)
+ uc_dbgmsg("send: cmsghdr.cmsg_len %u",
+ (u_int)cmsghdr->cmsg_len);
+
+ ssize = sendmsg(fd, msghdr, 0);
+ if (ssize < 0) {
+ uc_logmsg("message_send: sendmsg");
+ return (-1);
+ }
+ if ((size_t)ssize != size) {
+ uc_logmsgx("message_send: sendmsg: sent %zd of %zu bytes",
+ ssize, size);
+ return (-1);
+ }
+
+ if (!uc_cfg.send_data_flag)
+ if (uc_sync_send() < 0)
+ return (-1);
+
+ return (0);
+}
+
+int
+uc_message_sendn(int fd, struct msghdr *msghdr)
+{
+ u_int i;
+
+ for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) {
+ uc_dbgmsg("message #%u", i);
+ if (uc_message_send(fd, msghdr) < 0)
+ return (-1);
+ }
+ return (0);
+}
+
+int
+uc_message_recv(int fd, struct msghdr *msghdr)
+{
+ const struct cmsghdr *cmsghdr;
+ size_t size;
+ ssize_t ssize;
+
+ if (!uc_cfg.send_data_flag)
+ if (uc_sync_recv() < 0)
+ return (-1);
+
+ size = msghdr->msg_iov != NULL ? msghdr->msg_iov->iov_len : 0;
+ ssize = recvmsg(fd, msghdr, MSG_WAITALL);
+ if (ssize < 0) {
+ uc_logmsg("message_recv: recvmsg");
+ return (-1);
+ }
+ if ((size_t)ssize != size) {
+ uc_logmsgx("message_recv: recvmsg: received %zd of %zu bytes",
+ ssize, size);
+ return (-1);
+ }
+
+ uc_dbgmsg("recv: data size %zd", ssize);
+ uc_dbgmsg("recv: msghdr.msg_controllen %u",
+ (u_int)msghdr->msg_controllen);
+ cmsghdr = CMSG_FIRSTHDR(msghdr);
+ if (cmsghdr != NULL)
+ uc_dbgmsg("recv: cmsghdr.cmsg_len %u",
+ (u_int)cmsghdr->cmsg_len);
+
+ if (memcmp(uc_cfg.ipc_msg.buf_recv, uc_cfg.ipc_msg.buf_send, size) != 0) {
+ uc_logmsgx("message_recv: received message has wrong content");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+uc_socket_accept(int listenfd)
+{
+ fd_set rset;
+ struct timeval tv;
+ int fd, rv, val;
+
+ uc_dbgmsg("accept");
+
+ FD_ZERO(&rset);
+ FD_SET(listenfd, &rset);
+ tv.tv_sec = TIMEOUT;
+ tv.tv_usec = 0;
+ rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
+ if (rv < 0) {
+ uc_logmsg("socket_accept: select");
+ return (-1);
+ }
+ if (rv == 0) {
+ uc_logmsgx("socket_accept: select timeout");
+ return (-1);
+ }
+
+ fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
+ if (fd < 0) {
+ uc_logmsg("socket_accept: accept");
+ return (-1);
+ }
+
+ val = fcntl(fd, F_GETFL, 0);
+ if (val < 0) {
+ uc_logmsg("socket_accept: fcntl(F_GETFL)");
+ goto failed;
+ }
+ if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
+ uc_logmsg("socket_accept: fcntl(F_SETFL)");
+ goto failed;
+ }
+
+ return (fd);
+
+failed:
+ if (close(fd) < 0)
+ uc_logmsg("socket_accept: close");
+ return (-1);
+}
+
+int
+uc_check_msghdr(const struct msghdr *msghdr, size_t size)
+{
+ if (msghdr->msg_flags & MSG_TRUNC) {
+ uc_logmsgx("msghdr.msg_flags has MSG_TRUNC");
+ return (-1);
+ }
+ if (msghdr->msg_flags & MSG_CTRUNC) {
+ uc_logmsgx("msghdr.msg_flags has MSG_CTRUNC");
+ return (-1);
+ }
+ if (msghdr->msg_controllen < size) {
+ uc_logmsgx("msghdr.msg_controllen %u < %zu",
+ (u_int)msghdr->msg_controllen, size);
+ return (-1);
+ }
+ if (msghdr->msg_controllen > 0 && size == 0) {
+ uc_logmsgx("msghdr.msg_controllen %u > 0",
+ (u_int)msghdr->msg_controllen);
+ return (-1);
+ }
+ return (0);
+}
+
+int
+uc_check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size)
+{
+ if (cmsghdr == NULL) {
+ uc_logmsgx("cmsghdr is NULL");
+ return (-1);
+ }
+ if (cmsghdr->cmsg_level != SOL_SOCKET) {
+ uc_logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET",
+ cmsghdr->cmsg_level);
+ return (-1);
+ }
+ if (cmsghdr->cmsg_type != type) {
+ uc_logmsgx("cmsghdr.cmsg_type %d != %d",
+ cmsghdr->cmsg_type, type);
+ return (-1);
+ }
+ if (cmsghdr->cmsg_len != CMSG_LEN(size)) {
+ uc_logmsgx("cmsghdr.cmsg_len %u != %zu",
+ (u_int)cmsghdr->cmsg_len, CMSG_LEN(size));
+ return (-1);
+ }
+ return (0);
+}
+
+static void
+uc_msghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data)
+{
+ msghdr->msg_name = NULL;
+ msghdr->msg_namelen = 0;
+ if (uc_cfg.send_data_flag) {
+ iov->iov_base = uc_cfg.server_flag ?
+ uc_cfg.ipc_msg.buf_recv : uc_cfg.ipc_msg.buf_send;
+ iov->iov_len = uc_cfg.ipc_msg.buf_size;
+ msghdr->msg_iov = iov;
+ msghdr->msg_iovlen = 1;
+ } else {
+ msghdr->msg_iov = NULL;
+ msghdr->msg_iovlen = 0;
+ }
+ msghdr->msg_control = cmsg_data;
+ msghdr->msg_flags = 0;
+}
+
+void
+uc_msghdr_init_server(struct msghdr *msghdr, struct iovec *iov,
+ void *cmsg_data, size_t cmsg_size)
+{
+ uc_msghdr_init_generic(msghdr, iov, cmsg_data);
+ msghdr->msg_controllen = cmsg_size;
+ uc_dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ?
+ msghdr->msg_iov->iov_len : (size_t)0);
+ uc_dbgmsg("init: msghdr.msg_controllen %u",
+ (u_int)msghdr->msg_controllen);
+}
+
+void
+uc_msghdr_init_client(struct msghdr *msghdr, struct iovec *iov,
+ void *cmsg_data, size_t cmsg_size, int type, size_t arr_size)
+{
+ struct cmsghdr *cmsghdr;
+
+ uc_msghdr_init_generic(msghdr, iov, cmsg_data);
+ if (cmsg_data != NULL) {
+ if (uc_cfg.send_array_flag)
+ uc_dbgmsg("sending an array");
+ else
+ uc_dbgmsg("sending a scalar");
+ msghdr->msg_controllen = uc_cfg.send_array_flag ?
+ cmsg_size : CMSG_SPACE(0);
+ cmsghdr = CMSG_FIRSTHDR(msghdr);
+ cmsghdr->cmsg_level = SOL_SOCKET;
+ cmsghdr->cmsg_type = type;
+ cmsghdr->cmsg_len = CMSG_LEN(uc_cfg.send_array_flag ? arr_size : 0);
+ } else
+ msghdr->msg_controllen = 0;
+}
+
+int
+uc_client_fork(void)
+{
+ int fd1, fd2;
+
+ if (pipe(uc_cfg.sync_fd[SYNC_SERVER]) < 0 ||
+ pipe(uc_cfg.sync_fd[SYNC_CLIENT]) < 0) {
+ uc_logmsg("client_fork: pipe");
+ return (-1);
+ }
+ uc_cfg.client_pid = fork();
+ if (uc_cfg.client_pid == (pid_t)-1) {
+ uc_logmsg("client_fork: fork");
+ return (-1);
+ }
+ if (uc_cfg.client_pid == 0) {
+ uc_cfg.proc_name = "CLIENT";
+ uc_cfg.server_flag = false;
+ fd1 = uc_cfg.sync_fd[SYNC_SERVER][SYNC_RECV];
+ fd2 = uc_cfg.sync_fd[SYNC_CLIENT][SYNC_SEND];
+ } else {
+ fd1 = uc_cfg.sync_fd[SYNC_SERVER][SYNC_SEND];
+ fd2 = uc_cfg.sync_fd[SYNC_CLIENT][SYNC_RECV];
+ }
+ if (close(fd1) < 0 || close(fd2) < 0) {
+ uc_logmsg("client_fork: close");
+ return (-1);
+ }
+ return (uc_cfg.client_pid != 0);
+}
+
+void
+uc_client_exit(int rv)
+{
+ if (close(uc_cfg.sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
+ close(uc_cfg.sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
+ uc_logmsg("client_exit: close");
+ rv = -1;
+ }
+ rv = rv == 0 ? EXIT_SUCCESS : -rv;
+ uc_dbgmsg("exit: code %d", rv);
+ _exit(rv);
+}
+
+int
+uc_client_wait(void)
+{
+ int status;
+ pid_t pid;
+
+ uc_dbgmsg("waiting for client");
+
+ if (close(uc_cfg.sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
+ close(uc_cfg.sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
+ uc_logmsg("client_wait: close");
+ return (-1);
+ }
+
+ pid = waitpid(uc_cfg.client_pid, &status, 0);
+ if (pid == (pid_t)-1) {
+ uc_logmsg("client_wait: waitpid");
+ return (-1);
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+ uc_logmsgx("client exit status is %d",
+ WEXITSTATUS(status));
+ return (-WEXITSTATUS(status));
+ }
+ } else {
+ if (WIFSIGNALED(status))
+ uc_logmsgx("abnormal termination of client, signal %d%s",
+ WTERMSIG(status), WCOREDUMP(status) ?
+ " (core file generated)" : "");
+ else
+ uc_logmsgx("termination of client, unknown status");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+uc_check_groups(const char *gid_arr_str, const gid_t *gid_arr,
+ const char *gid_num_str, int gid_num, bool all_gids)
+{
+ int i;
+
+ for (i = 0; i < gid_num; ++i)
+ uc_dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]);
+
+ if (all_gids) {
+ if (gid_num != uc_cfg.proc_cred.gid_num) {
+ uc_logmsgx("%s %d != %d", gid_num_str, gid_num,
+ uc_cfg.proc_cred.gid_num);
+ return (-1);
+ }
+ } else {
+ if (gid_num > uc_cfg.proc_cred.gid_num) {
+ uc_logmsgx("%s %d > %d", gid_num_str, gid_num,
+ uc_cfg.proc_cred.gid_num);
+ return (-1);
+ }
+ }
+ if (memcmp(gid_arr, uc_cfg.proc_cred.gid_arr,
+ gid_num * sizeof(*gid_arr)) != 0) {
+ uc_logmsgx("%s content is wrong", gid_arr_str);
+ for (i = 0; i < gid_num; ++i)
+ if (gid_arr[i] != uc_cfg.proc_cred.gid_arr[i]) {
+ uc_logmsgx("%s[%d] %lu != %lu",
+ gid_arr_str, i, (u_long)gid_arr[i],
+ (u_long)uc_cfg.proc_cred.gid_arr[i]);
+ break;
+ }
+ return (-1);
+ }
+ return (0);
+}
+
+int
+uc_check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr)
+{
+ const struct cmsgcred *cmcred;
+ int rc;
+
+ if (uc_check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(struct cmsgcred)) < 0)
+ return (-1);
+
+ cmcred = (struct cmsgcred *)CMSG_DATA(cmsghdr);
+
+ uc_dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmcred->cmcred_pid);
+ uc_dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmcred->cmcred_uid);
+ uc_dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmcred->cmcred_euid);
+ uc_dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmcred->cmcred_gid);
+ uc_dbgmsg("cmsgcred.cmcred_ngroups %d", cmcred->cmcred_ngroups);
+
+ rc = 0;
+
+ if (cmcred->cmcred_pid != uc_cfg.client_pid) {
+ uc_logmsgx("cmsgcred.cmcred_pid %ld != %ld",
+ (long)cmcred->cmcred_pid, (long)uc_cfg.client_pid);
+ rc = -1;
+ }
+ if (cmcred->cmcred_uid != uc_cfg.proc_cred.uid) {
+ uc_logmsgx("cmsgcred.cmcred_uid %lu != %lu",
+ (u_long)cmcred->cmcred_uid, (u_long)uc_cfg.proc_cred.uid);
+ rc = -1;
+ }
+ if (cmcred->cmcred_euid != uc_cfg.proc_cred.euid) {
+ uc_logmsgx("cmsgcred.cmcred_euid %lu != %lu",
+ (u_long)cmcred->cmcred_euid, (u_long)uc_cfg.proc_cred.euid);
+ rc = -1;
+ }
+ if (cmcred->cmcred_gid != uc_cfg.proc_cred.gid) {
+ uc_logmsgx("cmsgcred.cmcred_gid %lu != %lu",
+ (u_long)cmcred->cmcred_gid, (u_long)uc_cfg.proc_cred.gid);
+ rc = -1;
+ }
+ if (cmcred->cmcred_ngroups == 0) {
+ uc_logmsgx("cmsgcred.cmcred_ngroups == 0");
+ rc = -1;
+ }
+ if (cmcred->cmcred_ngroups < 0) {
+ uc_logmsgx("cmsgcred.cmcred_ngroups %d < 0",
+ cmcred->cmcred_ngroups);
+ rc = -1;
+ }
+ if (cmcred->cmcred_ngroups > CMGROUP_MAX) {
+ uc_logmsgx("cmsgcred.cmcred_ngroups %d > %d",
+ cmcred->cmcred_ngroups, CMGROUP_MAX);
+ rc = -1;
+ }
+ if (cmcred->cmcred_groups[0] != uc_cfg.proc_cred.egid) {
+ uc_logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)",
+ (u_long)cmcred->cmcred_groups[0], (u_long)uc_cfg.proc_cred.egid);
+ rc = -1;
+ }
+ if (uc_check_groups("cmsgcred.cmcred_groups", cmcred->cmcred_groups,
+ "cmsgcred.cmcred_ngroups", cmcred->cmcred_ngroups, false) < 0)
+ rc = -1;
+ return (rc);
+}
+
+int
+uc_check_scm_creds_sockcred(struct cmsghdr *cmsghdr)
+{
+ const struct sockcred *sc;
+ int rc;
+
+ if (uc_check_cmsghdr(cmsghdr, SCM_CREDS,
+ SOCKCREDSIZE(uc_cfg.proc_cred.gid_num)) < 0)
+ return (-1);
+
+ sc = (struct sockcred *)CMSG_DATA(cmsghdr);
+
+ rc = 0;
+
+ uc_dbgmsg("sockcred.sc_uid %lu", (u_long)sc->sc_uid);
+ uc_dbgmsg("sockcred.sc_euid %lu", (u_long)sc->sc_euid);
+ uc_dbgmsg("sockcred.sc_gid %lu", (u_long)sc->sc_gid);
+ uc_dbgmsg("sockcred.sc_egid %lu", (u_long)sc->sc_egid);
+ uc_dbgmsg("sockcred.sc_ngroups %d", sc->sc_ngroups);
+
+ if (sc->sc_uid != uc_cfg.proc_cred.uid) {
+ uc_logmsgx("sockcred.sc_uid %lu != %lu",
+ (u_long)sc->sc_uid, (u_long)uc_cfg.proc_cred.uid);
+ rc = -1;
+ }
+ if (sc->sc_euid != uc_cfg.proc_cred.euid) {
+ uc_logmsgx("sockcred.sc_euid %lu != %lu",
+ (u_long)sc->sc_euid, (u_long)uc_cfg.proc_cred.euid);
+ rc = -1;
+ }
+ if (sc->sc_gid != uc_cfg.proc_cred.gid) {
+ uc_logmsgx("sockcred.sc_gid %lu != %lu",
+ (u_long)sc->sc_gid, (u_long)uc_cfg.proc_cred.gid);
+ rc = -1;
+ }
+ if (sc->sc_egid != uc_cfg.proc_cred.egid) {
+ uc_logmsgx("sockcred.sc_egid %lu != %lu",
+ (u_long)sc->sc_egid, (u_long)uc_cfg.proc_cred.egid);
+ rc = -1;
+ }
+ if (sc->sc_ngroups == 0) {
+ uc_logmsgx("sockcred.sc_ngroups == 0");
+ rc = -1;
+ }
+ if (sc->sc_ngroups < 0) {
+ uc_logmsgx("sockcred.sc_ngroups %d < 0",
+ sc->sc_ngroups);
+ rc = -1;
+ }
+ if (sc->sc_ngroups != uc_cfg.proc_cred.gid_num) {
+ uc_logmsgx("sockcred.sc_ngroups %d != %u",
+ sc->sc_ngroups, uc_cfg.proc_cred.gid_num);
+ rc = -1;
+ }
+ if (uc_check_groups("sockcred.sc_groups", sc->sc_groups,
+ "sockcred.sc_ngroups", sc->sc_ngroups, true) < 0)
+ rc = -1;
+ return (rc);
+}
diff --git a/tools/regression/sockets/unix_cmsg/uc_common.h b/tools/regression/sockets/unix_cmsg/uc_common.h
new file mode 100644
index 000000000000..626ae659119e
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/uc_common.h
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * Copyright (c) 2016 Maksym Sobolyev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+struct uc_cfg {
+ int sock_type;
+ const char *sock_type_str;
+ bool debug;
+ const char *proc_name;
+ int sync_fd[2][2];
+ int serv_sock_fd;
+ bool server_flag;
+ bool send_data_flag;
+ struct sockaddr_un serv_addr_sun;
+ bool send_array_flag;
+ pid_t client_pid;
+ struct {
+ char *buf_send;
+ char *buf_recv;
+ size_t buf_size;
+ u_int msg_num;
+ } ipc_msg;
+ struct {
+ uid_t uid;
+ uid_t euid;
+ gid_t gid;
+ gid_t egid;
+ gid_t *gid_arr;
+ int gid_num;
+ } proc_cred;
+};
+
+extern struct uc_cfg uc_cfg;
+
+int uc_check_msghdr(const struct msghdr *msghdr, size_t size);
+int uc_check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size);
+void uc_output(const char *format, ...) __printflike(1, 2);
+void uc_logmsgx(const char *format, ...) __printflike(1, 2);
+void uc_dbgmsg(const char *format, ...) __printflike(1, 2);
+void uc_logmsg(const char *format, ...) __printflike(1, 2);
+void uc_vlogmsgx(const char *format, va_list ap);
+int uc_message_recv(int fd, struct msghdr *msghdr);
+int uc_message_send(int fd, const struct msghdr *msghdr);
+int uc_message_sendn(int fd, struct msghdr *msghdr);
+void uc_msghdr_init_server(struct msghdr *msghdr, struct iovec *iov,
+ void *cmsg_data, size_t cmsg_size);
+void uc_msghdr_init_client(struct msghdr *msghdr, struct iovec *iov,
+ void *cmsg_data, size_t cmsg_size, int type, size_t arr_size);
+int uc_socket_create(void);
+int uc_socket_accept(int listenfd);
+int uc_socket_close(int fd);
+int uc_socket_connect(int fd);
+int uc_sync_recv(void);
+int uc_sync_send(void);
+int uc_client_fork(void);
+void uc_client_exit(int rv);
+int uc_client_wait(void);
+int uc_check_groups(const char *gid_arr_str, const gid_t *gid_arr,
+ const char *gid_num_str, int gid_num, bool all_gids);
+int uc_check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr);
+int uc_check_scm_creds_sockcred(struct cmsghdr *cmsghdr);
diff --git a/tools/regression/sockets/unix_cmsg/unix_cmsg.c b/tools/regression/sockets/unix_cmsg/unix_cmsg.c
new file mode 100644
index 000000000000..bd3753140b46
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/unix_cmsg.c
@@ -0,0 +1,449 @@
+/*-
+ * Copyright (c) 2005 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/limits.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <err.h>
+#include <inttypes.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "uc_common.h"
+#include "t_cmsgcred.h"
+#include "t_bintime.h"
+#include "t_generic.h"
+#include "t_peercred.h"
+#include "t_timeval.h"
+#include "t_sockcred.h"
+#include "t_cmsgcred_sockcred.h"
+#include "t_cmsg_len.h"
+#include "t_timespec_real.h"
+#include "t_timespec_mono.h"
+
+/*
+ * There are tables with tests descriptions and pointers to test
+ * functions. Each t_*() function returns 0 if its test passed,
+ * -1 if its test failed, -2 if some system error occurred.
+ * If a test function returns -2, then a program exits.
+ *
+ * If a test function forks a client process, then it waits for its
+ * termination. If a return code of a client process is not equal
+ * to zero, or if a client process was terminated by a signal, then
+ * a test function returns -1 or -2 depending on exit status of
+ * a client process.
+ *
+ * Each function which can block, is run under TIMEOUT. If timeout
+ * occurs, then a test function returns -2 or a client process exits
+ * with a non-zero return code.
+ */
+
+struct test_func {
+ int (*func)(void);
+ const char *desc;
+};
+
+static const struct test_func test_stream_tbl[] = {
+ {
+ .func = NULL,
+ .desc = "All tests"
+ },
+ {
+ .func = t_cmsgcred,
+ .desc = "Sending, receiving cmsgcred"
+ },
+ {
+ .func = t_sockcred_1,
+ .desc = "Receiving sockcred (listening socket)"
+ },
+ {
+ .func = t_sockcred_2,
+ .desc = "Receiving sockcred (accepted socket)"
+ },
+ {
+ .func = t_cmsgcred_sockcred,
+ .desc = "Sending cmsgcred, receiving sockcred"
+ },
+ {
+ .func = t_timeval,
+ .desc = "Sending, receiving timeval"
+ },
+ {
+ .func = t_bintime,
+ .desc = "Sending, receiving bintime"
+ },
+/*
+ * The testcase fails on 64-bit architectures (amd64), but passes on 32-bit
+ * architectures (i386); see bug 206543
+ */
+#ifndef __LP64__
+ {
+ .func = t_cmsg_len,
+ .desc = "Check cmsghdr.cmsg_len"
+ },
+#endif
+ {
+ .func = t_peercred,
+ .desc = "Check LOCAL_PEERCRED socket option"
+ },
+#if defined(SCM_REALTIME)
+ {
+ .func = t_timespec_real,
+ .desc = "Sending, receiving realtime"
+ },
+#endif
+#if defined(SCM_MONOTONIC)
+ {
+ .func = t_timespec_mono,
+ .desc = "Sending, receiving monotonic time (uptime)"
+ }
+#endif
+};
+
+#define TEST_STREAM_TBL_SIZE \
+ (sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
+
+static const struct test_func test_dgram_tbl[] = {
+ {
+ .func = NULL,
+ .desc = "All tests"
+ },
+ {
+ .func = t_cmsgcred,
+ .desc = "Sending, receiving cmsgcred"
+ },
+ {
+ .func = t_sockcred_2,
+ .desc = "Receiving sockcred"
+ },
+ {
+ .func = t_cmsgcred_sockcred,
+ .desc = "Sending cmsgcred, receiving sockcred"
+ },
+ {
+ .func = t_timeval,
+ .desc = "Sending, receiving timeval"
+ },
+ {
+ .func = t_bintime,
+ .desc = "Sending, receiving bintime"
+ },
+#ifndef __LP64__
+ {
+ .func = t_cmsg_len,
+ .desc = "Check cmsghdr.cmsg_len"
+ },
+#endif
+#if defined(SCM_REALTIME)
+ {
+ .func = t_timespec_real,
+ .desc = "Sending, receiving realtime"
+ },
+#endif
+#if defined(SCM_MONOTONIC)
+ {
+ .func = t_timespec_mono,
+ .desc = "Sending, receiving monotonic time (uptime)"
+ }
+#endif
+};
+
+#define TEST_DGRAM_TBL_SIZE \
+ (sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
+
+static bool failed_flag = false;
+
+struct uc_cfg uc_cfg;
+
+static char work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX";
+
+#define IPC_MSG_NUM_DEF 5
+#define IPC_MSG_NUM_MAX 10
+#define IPC_MSG_SIZE_DEF 7
+#define IPC_MSG_SIZE_MAX 128
+
+static void
+usage(bool verbose)
+{
+ u_int i;
+
+ printf("usage: %s [-dh] [-n num] [-s size] [-t type] "
+ "[-z value] [testno]\n", getprogname());
+ if (!verbose)
+ return;
+ printf("\n Options are:\n\
+ -d Output debugging information\n\
+ -h Output the help message and exit\n\
+ -n num Number of messages to send\n\
+ -s size Specify size of data for IPC\n\
+ -t type Specify socket type (stream, dgram) for tests\n\
+ -z value Do not send data in a message (bit 0x1), do not send\n\
+ data array associated with a cmsghdr structure (bit 0x2)\n\
+ testno Run one test by its number (require the -t option)\n\n");
+ printf(" Available tests for stream sockets:\n");
+ for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
+ printf(" %u: %s\n", i, test_stream_tbl[i].desc);
+ printf("\n Available tests for datagram sockets:\n");
+ for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
+ printf(" %u: %s\n", i, test_dgram_tbl[i].desc);
+}
+
+static int
+run_tests(int type, u_int testno1)
+{
+ const struct test_func *tf;
+ u_int i, testno2, failed_num;
+
+ uc_cfg.sock_type = type;
+ if (type == SOCK_STREAM) {
+ uc_cfg.sock_type_str = "SOCK_STREAM";
+ tf = test_stream_tbl;
+ i = TEST_STREAM_TBL_SIZE - 1;
+ } else {
+ uc_cfg.sock_type_str = "SOCK_DGRAM";
+ tf = test_dgram_tbl;
+ i = TEST_DGRAM_TBL_SIZE - 1;
+ }
+ if (testno1 == 0) {
+ testno1 = 1;
+ testno2 = i;
+ } else
+ testno2 = testno1;
+
+ uc_output("Running tests for %s sockets:\n", uc_cfg.sock_type_str);
+ failed_num = 0;
+ for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
+ uc_output(" %u: %s\n", i, tf->desc);
+ switch (tf->func()) {
+ case -1:
+ ++failed_num;
+ break;
+ case -2:
+ uc_logmsgx("some system error or timeout occurred");
+ return (-1);
+ }
+ }
+
+ if (failed_num != 0)
+ failed_flag = true;
+
+ if (testno1 != testno2) {
+ if (failed_num == 0)
+ uc_output("-- all tests passed!\n");
+ else
+ uc_output("-- %u test%s failed!\n",
+ failed_num, failed_num == 1 ? "" : "s");
+ } else {
+ if (failed_num == 0)
+ uc_output("-- test passed!\n");
+ else
+ uc_output("-- test failed!\n");
+ }
+
+ return (0);
+}
+
+static int
+init(void)
+{
+ struct sigaction sigact;
+ size_t idx;
+ int rv;
+
+ uc_cfg.proc_name = "SERVER";
+
+ sigact.sa_handler = SIG_IGN;
+ sigact.sa_flags = 0;
+ sigemptyset(&sigact.sa_mask);
+ if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
+ uc_logmsg("init: sigaction");
+ return (-1);
+ }
+
+ if (uc_cfg.ipc_msg.buf_size == 0)
+ uc_cfg.ipc_msg.buf_send = uc_cfg.ipc_msg.buf_recv = NULL;
+ else {
+ uc_cfg.ipc_msg.buf_send = malloc(uc_cfg.ipc_msg.buf_size);
+ uc_cfg.ipc_msg.buf_recv = malloc(uc_cfg.ipc_msg.buf_size);
+ if (uc_cfg.ipc_msg.buf_send == NULL || uc_cfg.ipc_msg.buf_recv == NULL) {
+ uc_logmsg("init: malloc");
+ return (-1);
+ }
+ for (idx = 0; idx < uc_cfg.ipc_msg.buf_size; ++idx)
+ uc_cfg.ipc_msg.buf_send[idx] = (char)idx;
+ }
+
+ uc_cfg.proc_cred.uid = getuid();
+ uc_cfg.proc_cred.euid = geteuid();
+ uc_cfg.proc_cred.gid = getgid();
+ uc_cfg.proc_cred.egid = getegid();
+ uc_cfg.proc_cred.gid_num = getgroups(0, (gid_t *)NULL);
+ if (uc_cfg.proc_cred.gid_num < 0) {
+ uc_logmsg("init: getgroups");
+ return (-1);
+ }
+ uc_cfg.proc_cred.gid_arr = malloc(uc_cfg.proc_cred.gid_num *
+ sizeof(*uc_cfg.proc_cred.gid_arr));
+ if (uc_cfg.proc_cred.gid_arr == NULL) {
+ uc_logmsg("init: malloc");
+ return (-1);
+ }
+ if (getgroups(uc_cfg.proc_cred.gid_num, uc_cfg.proc_cred.gid_arr) < 0) {
+ uc_logmsg("init: getgroups");
+ return (-1);
+ }
+
+ memset(&uc_cfg.serv_addr_sun, 0, sizeof(uc_cfg.serv_addr_sun));
+ rv = snprintf(uc_cfg.serv_addr_sun.sun_path, sizeof(uc_cfg.serv_addr_sun.sun_path),
+ "%s/%s", work_dir, uc_cfg.proc_name);
+ if (rv < 0) {
+ uc_logmsg("init: snprintf");
+ return (-1);
+ }
+ if ((size_t)rv >= sizeof(uc_cfg.serv_addr_sun.sun_path)) {
+ uc_logmsgx("init: not enough space for socket pathname");
+ return (-1);
+ }
+ uc_cfg.serv_addr_sun.sun_family = PF_LOCAL;
+ uc_cfg.serv_addr_sun.sun_len = SUN_LEN(&uc_cfg.serv_addr_sun);
+
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *errstr;
+ u_int testno, zvalue;
+ int opt, rv;
+ bool dgram_flag, stream_flag;
+
+ memset(&uc_cfg, '\0', sizeof(uc_cfg));
+ uc_cfg.debug = false;
+ uc_cfg.server_flag = true;
+ uc_cfg.send_data_flag = true;
+ uc_cfg.send_array_flag = true;
+ uc_cfg.ipc_msg.buf_size = IPC_MSG_SIZE_DEF;
+ uc_cfg.ipc_msg.msg_num = IPC_MSG_NUM_DEF;
+ dgram_flag = stream_flag = false;
+ while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)
+ switch (opt) {
+ case 'd':
+ uc_cfg.debug = true;
+ break;
+ case 'h':
+ usage(true);
+ return (EXIT_SUCCESS);
+ case 'n':
+ uc_cfg.ipc_msg.msg_num = strtonum(optarg, 1,
+ IPC_MSG_NUM_MAX, &errstr);
+ if (errstr != NULL)
+ errx(EXIT_FAILURE, "option -n: number is %s",
+ errstr);
+ break;
+ case 's':
+ uc_cfg.ipc_msg.buf_size = strtonum(optarg, 0,
+ IPC_MSG_SIZE_MAX, &errstr);
+ if (errstr != NULL)
+ errx(EXIT_FAILURE, "option -s: number is %s",
+ errstr);
+ break;
+ case 't':
+ if (strcmp(optarg, "stream") == 0)
+ stream_flag = true;
+ else if (strcmp(optarg, "dgram") == 0)
+ dgram_flag = true;
+ else
+ errx(EXIT_FAILURE, "option -t: "
+ "wrong socket type");
+ break;
+ case 'z':
+ zvalue = strtonum(optarg, 0, 3, &errstr);
+ if (errstr != NULL)
+ errx(EXIT_FAILURE, "option -z: number is %s",
+ errstr);
+ if (zvalue & 0x1)
+ uc_cfg.send_data_flag = false;
+ if (zvalue & 0x2)
+ uc_cfg.send_array_flag = false;
+ break;
+ default:
+ usage(false);
+ return (EXIT_FAILURE);
+ }
+
+ if (optind < argc) {
+ if (optind + 1 != argc)
+ errx(EXIT_FAILURE, "too many arguments");
+ testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
+ if (errstr != NULL)
+ errx(EXIT_FAILURE, "test number is %s", errstr);
+ if (stream_flag && testno >= TEST_STREAM_TBL_SIZE)
+ errx(EXIT_FAILURE, "given test %u for stream "
+ "sockets does not exist", testno);
+ if (dgram_flag && testno >= TEST_DGRAM_TBL_SIZE)
+ errx(EXIT_FAILURE, "given test %u for datagram "
+ "sockets does not exist", testno);
+ } else
+ testno = 0;
+
+ if (!dgram_flag && !stream_flag) {
+ if (testno != 0)
+ errx(EXIT_FAILURE, "particular test number "
+ "can be used with the -t option only");
+ dgram_flag = stream_flag = true;
+ }
+
+ if (mkdtemp(work_dir) == NULL)
+ err(EXIT_FAILURE, "mkdtemp(%s)", work_dir);
+
+ rv = EXIT_FAILURE;
+ if (init() < 0)
+ goto done;
+
+ if (stream_flag)
+ if (run_tests(SOCK_STREAM, testno) < 0)
+ goto done;
+ if (dgram_flag)
+ if (run_tests(SOCK_DGRAM, testno) < 0)
+ goto done;
+
+ rv = EXIT_SUCCESS;
+done:
+ if (rmdir(work_dir) < 0) {
+ uc_logmsg("rmdir(%s)", work_dir);
+ rv = EXIT_FAILURE;
+ }
+ return (failed_flag ? EXIT_FAILURE : rv);
+}
diff --git a/tools/regression/sockets/unix_cmsg/unix_cmsg.t b/tools/regression/sockets/unix_cmsg/unix_cmsg.t
new file mode 100644
index 000000000000..a267800922b3
--- /dev/null
+++ b/tools/regression/sockets/unix_cmsg/unix_cmsg.t
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+cd `dirname $0`
+cmd="./`basename $0 .t`"
+
+make ${cmd} >/dev/null 2>&1
+
+IFS=
+n=0
+
+run()
+{
+ result=`${cmd} -t $2 $3 ${5%% *} 2>&1`
+ if [ $? -ne 0 ]; then
+ echo -n "not "
+ fi
+ echo "ok $1 - $4 ${5#* }"
+ echo ${result} | grep -E "SERVER|CLIENT" | while read line; do
+ echo "# ${line}"
+ done
+}
+
+echo "1..47"
+
+for t1 in \
+ "1 Sending, receiving cmsgcred" \
+ "4 Sending cmsgcred, receiving sockcred" \
+ "5 Sending, receiving timeval" \
+ "6 Sending, receiving bintime" \
+ "7 Check cmsghdr.cmsg_len"
+do
+ for t2 in \
+ "0 " \
+ "1 (no data)" \
+ "2 (no array)" \
+ "3 (no data, array)"
+ do
+ n=$((n + 1))
+ run ${n} stream "-z ${t2%% *}" STREAM "${t1} ${t2#* }"
+ done
+done
+
+for t1 in \
+ "2 Receiving sockcred (listening socket)" \
+ "3 Receiving sockcred (accepted socket)"
+do
+ for t2 in \
+ "0 " \
+ "1 (no data)"
+ do
+ n=$((n + 1))
+ run ${n} stream "-z ${t2%% *}" STREAM "${t1} ${t2#* }"
+ done
+done
+
+n=$((n + 1))
+run ${n} stream "-z 0" STREAM "8 Check LOCAL_PEERCRED socket option"
+
+for t1 in \
+ "1 Sending, receiving cmsgcred" \
+ "3 Sending cmsgcred, receiving sockcred" \
+ "4 Sending, receiving timeval" \
+ "5 Sending, receiving bintime" \
+ "6 Check cmsghdr.cmsg_len"
+do
+ for t2 in \
+ "0 " \
+ "1 (no data)" \
+ "2 (no array)" \
+ "3 (no data, array)"
+ do
+ n=$((n + 1))
+ run ${n} dgram "-z ${t2%% *}" DGRAM "${t1} ${t2#* }"
+ done
+done
+
+for t1 in \
+ "2 Receiving sockcred"
+do
+ for t2 in \
+ "0 " \
+ "1 (no data)"
+ do
+ n=$((n + 1))
+ run ${n} dgram "-z ${t2%% *}" DGRAM "${t1} ${t2#* }"
+ done
+done
diff --git a/tools/regression/sockets/unix_gc/Makefile b/tools/regression/sockets/unix_gc/Makefile
new file mode 100644
index 000000000000..c9f98fca8710
--- /dev/null
+++ b/tools/regression/sockets/unix_gc/Makefile
@@ -0,0 +1,4 @@
+PROG= unix_gc
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/unix_gc/unix_gc.c b/tools/regression/sockets/unix_gc/unix_gc.c
new file mode 100644
index 000000000000..63b1e45e5284
--- /dev/null
+++ b/tools/regression/sockets/unix_gc/unix_gc.c
@@ -0,0 +1,806 @@
+/*-
+ * Copyright (c) 2007 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * A few regression tests for UNIX domain sockets. Run from single-user mode
+ * as it checks the openfiles sysctl to look for leaks, and we don't want that
+ * changing due to other processes doing stuff.
+ */
+
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static int forcegc = 1;
+static char dpath[PATH_MAX];
+static const char *test;
+
+static int
+getsysctl(const char *name)
+{
+ size_t len;
+ int i;
+
+ len = sizeof(i);
+ if (sysctlbyname(name, &i, &len, NULL, 0) < 0)
+ err(-1, "%s", name);
+ return (i);
+}
+
+static int
+getopenfiles(void)
+{
+
+ return (getsysctl("kern.openfiles"));
+}
+
+static int
+getinflight(void)
+{
+
+ return (getsysctl("net.local.inflight"));
+}
+
+static int
+getdeferred(void)
+{
+
+ return (getsysctl("net.local.deferred"));
+}
+
+static void
+sendfd(int fd, int fdtosend)
+{
+ struct msghdr mh;
+ struct message { struct cmsghdr msg_hdr; int fd; } m;
+ ssize_t len;
+ int after_inflight, before_inflight;
+
+ before_inflight = getinflight();
+
+ bzero(&mh, sizeof(mh));
+ bzero(&m, sizeof(m));
+ mh.msg_control = &m;
+ mh.msg_controllen = sizeof(m);
+ m.msg_hdr.cmsg_len = sizeof(m);
+ m.msg_hdr.cmsg_level = SOL_SOCKET;
+ m.msg_hdr.cmsg_type = SCM_RIGHTS;
+ m.fd = fdtosend;
+ len = sendmsg(fd, &mh, 0);
+ if (len < 0)
+ err(-1, "%s: sendmsg", test);
+ after_inflight = getinflight();
+ if (after_inflight != before_inflight + 1)
+ errx(-1, "%s: sendfd: before %d after %d\n", test,
+ before_inflight, after_inflight);
+}
+
+static void
+close2(int fd1, int fd2)
+{
+
+ close(fd1);
+ close(fd2);
+}
+
+static void
+close3(int fd1, int fd2, int fd3)
+{
+
+ close2(fd1, fd2);
+ close(fd3);
+}
+
+static void
+close4(int fd1, int fd2, int fd3, int fd4)
+{
+
+ close2(fd1, fd2);
+ close2(fd3, fd4);
+}
+
+static void
+close5(int fd1, int fd2, int fd3, int fd4, int fd5)
+{
+
+ close3(fd1, fd2, fd3);
+ close2(fd4, fd5);
+}
+
+static int
+my_socket(int domain, int type, int proto)
+{
+ int sock;
+
+ sock = socket(domain, type, proto);
+ if (sock < 0)
+ err(-1, "%s: socket", test);
+ return (sock);
+}
+
+static void
+my_bind(int sock, struct sockaddr *sa, socklen_t len)
+{
+
+ if (bind(sock, sa, len) < 0)
+ err(-1, "%s: bind", test);
+}
+
+static void
+my_connect(int sock, struct sockaddr *sa, socklen_t len)
+{
+
+ if (connect(sock, sa, len) < 0 && errno != EINPROGRESS)
+ err(-1, "%s: connect", test);
+}
+
+static void
+my_listen(int sock, int backlog)
+{
+
+ if (listen(sock, backlog) < 0)
+ err(-1, "%s: listen", test);
+}
+
+static void
+my_socketpair(int *sv)
+{
+
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0)
+ err(-1, "%s: socketpair", test);
+}
+
+static void
+my_getsockname(int s, struct sockaddr *sa, socklen_t *salen)
+{
+
+ if (getsockname(s, sa, salen) < 0)
+ err(-1, "%s: getsockname", test);
+}
+
+static void
+setnonblock(int s)
+{
+
+ if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
+ err(-1, "%s: fcntl(F_SETFL, O_NONBLOCK)", test);
+}
+
+static void
+alloc3fds(int *s, int *sv)
+{
+
+ if ((*s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+ err(-1, "%s: socket", test);
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0)
+ err(-1, "%s: socketpair", test);
+}
+
+static void
+alloc5fds(int *s, int *sva, int *svb)
+{
+
+ if ((*s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+ err(-1, "%s: socket", test);
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, sva) < 0)
+ err(-1, "%s: socketpair", test);
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, svb) < 0)
+ err(-1, "%s: socketpair", test);
+}
+
+static void
+save_sysctls(int *before_inflight, int *before_openfiles)
+{
+
+ *before_inflight = getinflight();
+ *before_openfiles = getopenfiles();
+}
+
+/*
+ * Try hard to make sure that the GC does in fact run before we test the
+ * condition of things.
+ */
+static void
+trigger_gc(void)
+{
+ int s;
+
+ if (forcegc) {
+ if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+ err(-1, "trigger_gc: socket");
+ close(s);
+ }
+ sleep(1);
+}
+
+static void
+test_sysctls(int before_inflight, int before_openfiles)
+{
+ int after_inflight, after_openfiles;
+
+ trigger_gc();
+ after_inflight = getinflight();
+ if (after_inflight != before_inflight)
+ warnx("%s: before inflight: %d, after inflight: %d",
+ test, before_inflight, after_inflight);
+
+ after_openfiles = getopenfiles();
+ if (after_openfiles != before_openfiles)
+ warnx("%s: before: %d, after: %d", test, before_openfiles,
+ after_openfiles);
+}
+
+static void
+twosome_nothing(void)
+{
+ int inflight, openfiles;
+ int sv[2];
+
+ /*
+ * Create a pair, close in one order.
+ */
+ test = "twosome_nothing1";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+ my_socketpair(sv);
+ close2(sv[0], sv[1]);
+ test_sysctls(inflight, openfiles);
+
+ /*
+ * Create a pair, close in the other order.
+ */
+ test = "twosome_nothing2";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+ my_socketpair(sv);
+ close2(sv[0], sv[1]);
+ test_sysctls(inflight, openfiles);
+}
+
+/*
+ * Using a socket pair, send various endpoints over the pair and close in
+ * various orders.
+ */
+static void
+twosome_drop_work(const char *testname, int sendvia, int tosend, int closefirst)
+{
+ int inflight, openfiles;
+ int sv[2];
+
+ printf("%s\n", testname);
+ test = testname;
+ save_sysctls(&inflight, &openfiles);
+ my_socketpair(sv);
+ sendfd(sv[sendvia], sv[tosend]);
+ if (closefirst == 0)
+ close2(sv[0], sv[1]);
+ else
+ close2(sv[1], sv[0]);
+ test_sysctls(inflight, openfiles);
+}
+
+static void
+twosome_drop(void)
+{
+
+ /*
+ * In various combations, some wastefully symmetric, create socket
+ * pairs and send one or another endpoint over one or another
+ * endpoint, closing the endpoints in various orders.
+ */
+ twosome_drop_work("twosome_drop1", 0, 0, 0);
+ twosome_drop_work("twosome_drop2", 0, 0, 1);
+ twosome_drop_work("twosome_drop3", 0, 1, 0);
+ twosome_drop_work("twosome_drop4", 0, 1, 1);
+ twosome_drop_work("twosome_drop5", 1, 0, 0);
+ twosome_drop_work("twosome_drop6", 1, 0, 1);
+ twosome_drop_work("twosome_drop7", 1, 1, 0);
+ twosome_drop_work("twosome_drop8", 1, 1, 1);
+}
+
+static void
+threesome_nothing(void)
+{
+ int inflight, openfiles;
+ int s, sv[2];
+
+ test = "threesome_nothing";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+ alloc3fds(&s, sv);
+ close3(s, sv[0], sv[1]);
+ test_sysctls(inflight, openfiles);
+}
+
+/*
+ * threesome_drop: create a pair and a spare, send the spare over the pair, and
+ * close in various orders and make sure all the fds went away.
+ */
+static void
+threesome_drop(void)
+{
+ int inflight, openfiles;
+ int s, sv[2];
+
+ /*
+ * threesome_drop1: close sent send receive
+ */
+ test = "threesome_drop1";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+ alloc3fds(&s, sv);
+ sendfd(sv[0], s);
+ close3(s, sv[0], sv[1]);
+ test_sysctls(inflight, openfiles);
+
+ /*
+ * threesome_drop2: close sent receive send
+ */
+ test = "threesome_drop2";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+ alloc3fds(&s, sv);
+ sendfd(sv[0], s);
+ close3(s, sv[1], sv[0]);
+ test_sysctls(inflight, openfiles);
+
+ /*
+ * threesome_drop3: close receive sent send
+ */
+ test = "threesome_drop3";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+ alloc3fds(&s, sv);
+ sendfd(sv[0], s);
+ close3(sv[1], s, sv[0]);
+ test_sysctls(inflight, openfiles);
+
+ /*
+ * threesome_drop4: close receive send sent
+ */
+ test = "threesome_drop4";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+ alloc3fds(&s, sv);
+ sendfd(sv[0], s);
+ close3(sv[1], sv[0], s);
+ test_sysctls(inflight, openfiles);
+
+ /*
+ * threesome_drop5: close send receive sent
+ */
+ test = "threesome_drop5";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+ alloc3fds(&s, sv);
+ sendfd(sv[0], s);
+ close3(sv[0], sv[1], s);
+ test_sysctls(inflight, openfiles);
+
+ /*
+ * threesome_drop6: close send sent receive
+ */
+ test = "threesome_drop6";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+ alloc3fds(&s, sv);
+ close3(sv[0], s, sv[1]);
+ test_sysctls(inflight, openfiles);
+}
+
+/*
+ * Fivesome tests: create two socket pairs and a spare, send the spare over
+ * the first socket pair, then send the first socket pair over the second
+ * socket pair, and GC. Do various closes at various points to exercise
+ * various cases.
+ */
+static void
+fivesome_nothing(void)
+{
+ int inflight, openfiles;
+ int spare, sva[2], svb[2];
+
+ test = "fivesome_nothing";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+ alloc5fds(&spare, sva, svb);
+ close5(spare, sva[0], sva[1], svb[0], svb[1]);
+ test_sysctls(inflight, openfiles);
+}
+
+static void
+fivesome_drop_work(const char *testname, int close_spare_after_send,
+ int close_sva_after_send)
+{
+ int inflight, openfiles;
+ int spare, sva[2], svb[2];
+
+ printf("%s\n", testname);
+ test = testname;
+ save_sysctls(&inflight, &openfiles);
+ alloc5fds(&spare, sva, svb);
+
+ /*
+ * Send spare over sva.
+ */
+ sendfd(sva[0], spare);
+ if (close_spare_after_send)
+ close(spare);
+
+ /*
+ * Send sva over svb.
+ */
+ sendfd(svb[0], sva[0]);
+ sendfd(svb[0], sva[1]);
+ if (close_sva_after_send)
+ close2(sva[0], sva[1]);
+
+ close2(svb[0], svb[1]);
+
+ if (!close_sva_after_send)
+ close2(sva[0], sva[1]);
+ if (!close_spare_after_send)
+ close(spare);
+
+ test_sysctls(inflight, openfiles);
+}
+
+static void
+fivesome_drop(void)
+{
+
+ fivesome_drop_work("fivesome_drop1", 0, 0);
+ fivesome_drop_work("fivesome_drop2", 0, 1);
+ fivesome_drop_work("fivesome_drop3", 1, 0);
+ fivesome_drop_work("fivesome_drop4", 1, 1);
+}
+
+/*
+ * Create a somewhat nasty dual-socket socket intended to upset the garbage
+ * collector if mark-and-sweep is wrong.
+ */
+static void
+complex_cycles(void)
+{
+ int inflight, openfiles;
+ int spare, sva[2], svb[2];
+
+ test = "complex_cycles";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+ alloc5fds(&spare, sva, svb);
+ sendfd(sva[0], svb[0]);
+ sendfd(sva[0], svb[1]);
+ sendfd(svb[0], sva[0]);
+ sendfd(svb[0], sva[1]);
+ sendfd(svb[0], spare);
+ sendfd(sva[0], spare);
+ close5(spare, sva[0], sva[1], svb[0], svb[1]);
+ test_sysctls(inflight, openfiles);
+}
+
+/*
+ * Listen sockets can also be passed over UNIX domain sockets, so test
+ * various cases, including ones where listen sockets have waiting sockets
+ * hanging off them...
+ */
+static void
+listen_nothing(void)
+{
+ struct sockaddr_un sun;
+ struct sockaddr_in sin;
+ int inflight, openfiles;
+ int s;
+
+ test = "listen_nothing_unp";
+ printf("%s\n", test);
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_LOCAL;
+ sun.sun_len = sizeof(sun);
+ snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);
+ save_sysctls(&inflight, &openfiles);
+ s = my_socket(PF_LOCAL, SOCK_STREAM, 0);
+ my_bind(s, (struct sockaddr *)&sun, sizeof(sun));
+ my_listen(s, -1);
+ close(s);
+ (void)unlink(sun.sun_path);
+ test_sysctls(inflight, openfiles);
+
+ test = "listen_nothing_inet";
+ printf("%s\n", test);
+ bzero(&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(0);
+ save_sysctls(&inflight, &openfiles);
+ s = my_socket(PF_INET, SOCK_STREAM, 0);
+ my_bind(s, (struct sockaddr *)&sin, sizeof(sin));
+ my_listen(s, -1);
+ close(s);
+ test_sysctls(inflight, openfiles);
+}
+
+/*
+ * Send a listen UDP socket over a UNIX domain socket.
+ *
+ * Send a listen TCP socket over a UNIX domain socket.
+ *
+ * Do each twice, with closing of the listen socket vs. socketpair in
+ * different orders.
+ */
+static void
+listen_drop(void)
+{
+ struct sockaddr_un sun;
+ struct sockaddr_in sin;
+ int inflight, openfiles;
+ int s, sv[2];
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_LOCAL;
+ sun.sun_len = sizeof(sun);
+
+ /*
+ * Close listen socket first.
+ */
+ test = "listen_drop_unp1";
+ printf("%s\n", test);
+ snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);
+ save_sysctls(&inflight, &openfiles);
+ s = my_socket(PF_LOCAL, SOCK_STREAM, 0);
+ my_bind(s, (struct sockaddr *)&sun, sizeof(sun));
+ my_listen(s, -1);
+ my_socketpair(sv);
+ sendfd(sv[0], s);
+ close3(s, sv[0], sv[1]);
+ test_sysctls(inflight, openfiles);
+
+ /*
+ * Close socketpair first.
+ */
+ test = "listen_drop_unp2";
+ printf("%s\n", test);
+ snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);
+ save_sysctls(&inflight, &openfiles);
+ s = my_socket(PF_LOCAL, SOCK_STREAM, 0);
+ my_bind(s, (struct sockaddr *)&sun, sizeof(sun));
+ my_listen(s, -1);
+ my_socketpair(sv);
+ sendfd(sv[0], s);
+ close3(sv[0], sv[1], s);
+ test_sysctls(inflight, openfiles);
+
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(0);
+
+ /*
+ * Close listen socket first.
+ */
+ test = "listen_drop_inet1";
+ printf("%s\n", test);
+ bzero(&sun, sizeof(sun));
+ save_sysctls(&inflight, &openfiles);
+ s = my_socket(PF_INET, SOCK_STREAM, 0);
+ my_bind(s, (struct sockaddr *)&sin, sizeof(sin));
+ my_listen(s, -1);
+ my_socketpair(sv);
+ sendfd(sv[0], s);
+ close3(s, sv[0], sv[1]);
+ test_sysctls(inflight, openfiles);
+
+ /*
+ * Close socketpair first.
+ */
+ test = "listen_drop_inet2";
+ printf("%s\n", test);
+ bzero(&sun, sizeof(sun));
+ save_sysctls(&inflight, &openfiles);
+ s = my_socket(PF_INET, SOCK_STREAM, 0);
+ my_bind(s, (struct sockaddr *)&sin, sizeof(sin));
+ my_listen(s, -1);
+ my_socketpair(sv);
+ sendfd(sv[0], s);
+ close3(sv[0], sv[1], s);
+ test_sysctls(inflight, openfiles);
+}
+
+/*
+ * Up things a notch with listen sockets: add connections that can be
+ * accepted to the listen queues.
+ */
+static void
+listen_connect_nothing(void)
+{
+ struct sockaddr_in sin;
+ int slisten, sconnect, sv[2];
+ int inflight, openfiles;
+ socklen_t len;
+
+ test = "listen_connect_nothing";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+
+ slisten = my_socket(PF_INET, SOCK_STREAM, 0);
+ my_bind(slisten, (struct sockaddr *)&sin, sizeof(sin));
+ my_listen(slisten, -1);
+
+ my_socketpair(sv);
+
+ len = sizeof(sin);
+ my_getsockname(slisten, (struct sockaddr *)&sin, &len);
+
+ sconnect = my_socket(PF_INET, SOCK_STREAM, 0);
+ setnonblock(sconnect);
+ my_connect(sconnect, (struct sockaddr *)&sin, len);
+
+ sleep(1);
+
+ close4(slisten, sconnect, sv[0], sv[1]);
+
+ test_sysctls(inflight, openfiles);
+}
+
+static void
+listen_connect_drop(void)
+{
+ struct sockaddr_in sin;
+ int slisten, sconnect, sv[2];
+ int inflight, openfiles;
+ socklen_t len;
+
+ test = "listen_connect_drop";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+
+ slisten = my_socket(PF_INET, SOCK_STREAM, 0);
+ my_bind(slisten, (struct sockaddr *)&sin, sizeof(sin));
+ my_listen(slisten, -1);
+
+ my_socketpair(sv);
+
+ len = sizeof(sin);
+ my_getsockname(slisten, (struct sockaddr *)&sin, &len);
+
+ sconnect = my_socket(PF_INET, SOCK_STREAM, 0);
+ setnonblock(sconnect);
+ my_connect(sconnect, (struct sockaddr *)&sin, len);
+
+ sleep(1);
+ sendfd(sv[0], slisten);
+ close3(slisten, sv[0], sv[1]);
+ sleep(1);
+ close(sconnect);
+
+ test_sysctls(inflight, openfiles);
+}
+
+static void
+recursion(void)
+{
+ int fd[2], ff[2];
+ int inflight, openfiles, deferred, deferred1;
+
+ test = "recursion";
+ printf("%s\n", test);
+ save_sysctls(&inflight, &openfiles);
+ deferred = getdeferred();
+
+ my_socketpair(fd);
+
+ for (;;) {
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, ff) == -1) {
+ if (errno == EMFILE || errno == ENFILE)
+ break;
+ err(-1, "socketpair");
+ }
+ sendfd(ff[0], fd[0]);
+ sendfd(ff[0], fd[1]);
+ close2(fd[1], fd[0]);
+ fd[0] = ff[0];
+ fd[1] = ff[1];
+ }
+ close2(fd[0], fd[1]);
+ sleep(1);
+ test_sysctls(inflight, openfiles);
+ deferred1 = getdeferred();
+ if (deferred != deferred1)
+ errx(-1, "recursion: deferred before %d after %d", deferred,
+ deferred1);
+}
+
+#define RMDIR "rm -Rf "
+int
+main(void)
+{
+ char cmd[sizeof(RMDIR) + PATH_MAX];
+ int serrno;
+ pid_t pid;
+
+ strlcpy(dpath, "/tmp/unpgc.XXXXXXXX", sizeof(dpath));
+ if (mkdtemp(dpath) == NULL)
+ err(-1, "mkdtemp");
+
+ /*
+ * Set up a parent process to GC temporary storage when we're done.
+ */
+ pid = fork();
+ if (pid < 0) {
+ serrno = errno;
+ (void)rmdir(dpath);
+ errno = serrno;
+ err(-1, "fork");
+ }
+ if (pid > 0) {
+ signal(SIGINT, SIG_IGN);
+ while (waitpid(pid, NULL, 0) != pid);
+ snprintf(cmd, sizeof(cmd), "%s %s", RMDIR, dpath);
+ (void)system(cmd);
+ exit(0);
+ }
+
+ printf("Start: inflight %d open %d\n", getinflight(),
+ getopenfiles());
+
+ twosome_nothing();
+ twosome_drop();
+
+ threesome_nothing();
+ threesome_drop();
+
+ fivesome_nothing();
+ fivesome_drop();
+
+ complex_cycles();
+
+ listen_nothing();
+ listen_drop();
+
+ listen_connect_nothing();
+ listen_connect_drop();
+
+ recursion();
+
+ printf("Finish: inflight %d open %d\n", getinflight(),
+ getopenfiles());
+ return (0);
+}
diff --git a/tools/regression/sockets/unix_sendtorace/Makefile b/tools/regression/sockets/unix_sendtorace/Makefile
new file mode 100644
index 000000000000..1e7b53d40379
--- /dev/null
+++ b/tools/regression/sockets/unix_sendtorace/Makefile
@@ -0,0 +1,4 @@
+PROG= unix_sendtorace
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/unix_sendtorace/unix_sendtorace.c b/tools/regression/sockets/unix_sendtorace/unix_sendtorace.c
new file mode 100644
index 000000000000..6a6db010314e
--- /dev/null
+++ b/tools/regression/sockets/unix_sendtorace/unix_sendtorace.c
@@ -0,0 +1,213 @@
+/*-
+ * Copyright (c) 2006 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Attempts to exercise UNIX domain socket races relating to the non-atomic
+ * connect-and-send properties of sendto(). As the result of such a race is
+ * a kernel panic, this test simply completes or doesn't.
+ *
+ * XXX: Despite implementing support for sendto() on stream sockets with
+ * implied connect, the appropriate flag isn't set in the FreeBSD kernel so
+ * it does not work. For now, don't call the stream test.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <err.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#define ITERATIONS 1000000
+
+static char socket_path[] = "tmp.XXXXXX";
+
+static void
+stream_server(int listenfd)
+{
+ int acceptfd;
+
+ while (1) {
+ acceptfd = accept(listenfd, NULL, NULL);
+ if (acceptfd < 0) {
+ warn("stream_server: accept");
+ continue;
+ }
+ sleep(1);
+ close(acceptfd);
+ }
+}
+
+static void
+stream_client(void)
+{
+ struct sockaddr_un sun;
+ ssize_t len;
+ char c = 0;
+ int fd, i;
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_len = sizeof(sun);
+ sun.sun_family = AF_UNIX;
+ strcpy(sun.sun_path, socket_path);
+ for (i = 0; i < ITERATIONS; i++) {
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ warn("stream_client: socket");
+ return;
+ }
+ len = sendto(fd, &c, sizeof(c), 0, (struct sockaddr *)&sun,
+ sizeof(sun));
+ if (len < 0)
+ warn("stream_client: sendto");
+ close(fd);
+ }
+}
+
+static void
+stream_test(void)
+{
+ struct sockaddr_un sun;
+ pid_t childpid;
+ int listenfd;
+
+ listenfd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (listenfd < 0)
+ err(-1, "stream_test: socket");
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_len = sizeof(sun);
+ sun.sun_family = AF_UNIX;
+ strcpy(sun.sun_path, socket_path);
+
+ if (bind(listenfd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
+ err(-1, "stream_test: bind");
+
+ if (listen(listenfd, -1) < 0)
+ err(-1, "stream_test: listen");
+
+ childpid = fork();
+ if (childpid < 0)
+ err(-1, "stream_test: fork");
+
+ if (childpid != 0) {
+ sleep(1);
+ stream_client();
+ kill(childpid, SIGTERM);
+ sleep(1);
+ } else
+ stream_server(listenfd);
+
+ (void)unlink(socket_path);
+}
+
+static void
+datagram_server(int serverfd)
+{
+ ssize_t len;
+ char c;
+
+ while (1) {
+ len = recv(serverfd, &c, sizeof(c), 0);
+ if (len < 0)
+ warn("datagram_server: recv");
+ }
+}
+
+static void
+datagram_client(void)
+{
+ struct sockaddr_un sun;
+ ssize_t len;
+ char c = 0;
+ int fd, i;
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_len = sizeof(sun);
+ sun.sun_family = AF_UNIX;
+ strcpy(sun.sun_path, socket_path);
+ for (i = 0; i < ITERATIONS; i++) {
+ fd = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ warn("datagram_client: socket");
+ return;
+ }
+ len = sendto(fd, &c, sizeof(c), 0, (struct sockaddr *)&sun,
+ sizeof(sun));
+ if (len < 0)
+ warn("datagram_client: sendto");
+ close(fd);
+ }
+}
+
+static void
+datagram_test(void)
+{
+ struct sockaddr_un sun;
+ pid_t childpid;
+ int serverfd;
+
+ serverfd = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (serverfd < 0)
+ err(-1, "datagram_test: socket");
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_len = sizeof(sun);
+ sun.sun_family = AF_UNIX;
+ strcpy(sun.sun_path, socket_path);
+
+ if (bind(serverfd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
+ err(-1, "datagram_test: bind");
+
+ childpid = fork();
+ if (childpid < 0)
+ err(-1, "datagram_test: fork");
+
+ if (childpid != 0) {
+ sleep(1);
+ datagram_client();
+ kill(childpid, SIGTERM);
+ sleep(1);
+ } else
+ datagram_server(serverfd);
+
+ (void)unlink(socket_path);
+}
+
+int
+main(void)
+{
+
+ if (mkstemp(socket_path) == -1)
+ err(1, "mkstemp failed");
+ (void)unlink(socket_path);
+ datagram_test();
+ if (0)
+ stream_test();
+ return (0);
+}
diff --git a/tools/regression/sockets/unix_socket/Makefile b/tools/regression/sockets/unix_socket/Makefile
new file mode 100644
index 000000000000..61612323e98e
--- /dev/null
+++ b/tools/regression/sockets/unix_socket/Makefile
@@ -0,0 +1,4 @@
+PROG= unix_socket
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/unix_socket/unix_socket.c b/tools/regression/sockets/unix_socket/unix_socket.c
new file mode 100644
index 000000000000..487668ab4701
--- /dev/null
+++ b/tools/regression/sockets/unix_socket/unix_socket.c
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 2006 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Simple UNIX domain socket regression test: create and tear down various
+ * supported and unsupported socket types.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ int sock, socks[2];
+
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0)
+ err(-1, "socket(PF_LOCAL, SOCK_STREAM, 0)");
+ close(sock);
+
+ sock = socket(PF_LOCAL, SOCK_DGRAM, 0);
+ if (sock < 0)
+ err(-1, "socket(PF_LOCAL, SOCK_DGRAM, 0)");
+ close(sock);
+
+ sock = socket(PF_LOCAL, SOCK_RAW, 0);
+ if (sock >= 0) {
+ close(sock);
+ errx(-1, "socket(PF_LOCAL, SOCK_RAW, 0) returned %d", sock);
+ }
+ if (errno != EPROTOTYPE)
+ err(-1, "socket(PF_LOCAL, SOCK_RAW, 0)");
+
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, socks) < 0)
+ err(-1, "socketpair(PF_LOCAL, SOCK_STREAM, 0, socks)");
+ if (socks[0] < 0)
+ errx(-1, "socketpair(PF_LOCAL, SOCK_STREAM, 0, socks) [0] < 0");
+ if (socks[1] < 0)
+ errx(-1, "socketpair(PF_LOCAL, SOCK_STREAM, 0, socks) [1] < 1");
+ close(socks[0]);
+ close(socks[1]);
+
+ if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, socks) < 0)
+ err(-1, "socketpair(PF_LOCAL, SOCK_DGRAM, 0, socks)");
+ if (socks[0] < 0)
+ errx(-1, "socketpair(PF_LOCAL, SOCK_DGRAM, 0, socks) [0] < 0");
+ if (socks[1] < 0)
+ errx(-1, "socketpair(PF_LOCAL, SOCK_DGRAM, 0, socks) [1] < 1");
+ close(socks[0]);
+ close(socks[1]);
+
+ return (0);
+}
diff --git a/tools/regression/sockets/unix_sorflush/Makefile b/tools/regression/sockets/unix_sorflush/Makefile
new file mode 100644
index 000000000000..b41cb474f94a
--- /dev/null
+++ b/tools/regression/sockets/unix_sorflush/Makefile
@@ -0,0 +1,4 @@
+PROG= unix_sorflush
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/unix_sorflush/unix_sorflush.c b/tools/regression/sockets/unix_sorflush/unix_sorflush.c
new file mode 100644
index 000000000000..082db5a02d17
--- /dev/null
+++ b/tools/regression/sockets/unix_sorflush/unix_sorflush.c
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 2008 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Reproduce a race in which:
+ *
+ * - Process (a) is blocked in read on a socket waiting on data.
+ * - Process (b) is blocked in shutdown() on a socket waiting on (a).
+ * - Process (c) delivers a signal to (b) interrupting its wait.
+ *
+ * This race is premised on shutdown() not interrupting (a) properly, and the
+ * signal to (b) causing problems in the kernel.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+
+#include <err.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void
+receive_and_exit(int s)
+{
+ ssize_t ssize;
+ char ch;
+
+ ssize = recv(s, &ch, sizeof(ch), 0);
+ if (ssize < 0)
+ err(-1, "receive_and_exit: recv");
+ exit(0);
+}
+
+static void
+shutdown_and_exit(int s)
+{
+
+ if (shutdown(s, SHUT_RD) < 0)
+ err(-1, "shutdown_and_exit: shutdown");
+ exit(0);
+}
+
+int
+main(void)
+{
+ pid_t pida, pidb;
+ int sv[2];
+
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sv) < 0)
+ err(-1, "socketpair");
+
+ pida = fork();
+ if (pida < 0)
+ err(-1, "fork");
+ if (pida == 0)
+ receive_and_exit(sv[1]);
+ sleep(1);
+ pidb = fork();
+ if (pidb < 0) {
+ warn("fork");
+ (void)kill(pida, SIGKILL);
+ exit(-1);
+ }
+ if (pidb == 0)
+ shutdown_and_exit(sv[1]);
+ sleep(1);
+ if (kill(pidb, SIGKILL) < 0)
+ err(-1, "kill");
+ sleep(1);
+ printf("ok 1 - unix_sorflush\n");
+ exit(0);
+}
diff --git a/tools/regression/sockets/zerosend/Makefile b/tools/regression/sockets/zerosend/Makefile
new file mode 100644
index 000000000000..2f270d05cfa2
--- /dev/null
+++ b/tools/regression/sockets/zerosend/Makefile
@@ -0,0 +1,4 @@
+PROG= zerosend
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/sockets/zerosend/zerosend.c b/tools/regression/sockets/zerosend/zerosend.c
new file mode 100644
index 000000000000..347574a2e569
--- /dev/null
+++ b/tools/regression/sockets/zerosend/zerosend.c
@@ -0,0 +1,288 @@
+/*-
+ * Copyright (c) 2007 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define PORT1 10001
+#define PORT2 10002
+
+static void
+try_0send(const char *test, int fd)
+{
+ ssize_t len;
+ char ch;
+
+ ch = 0;
+ len = send(fd, &ch, 0, 0);
+ if (len < 0)
+ err(1, "%s: try_0send", test);
+ if (len != 0)
+ errx(1, "%s: try_0send: returned %zd", test, len);
+}
+
+static void
+try_0write(const char *test, int fd)
+{
+ ssize_t len;
+ char ch;
+
+ ch = 0;
+ len = write(fd, &ch, 0);
+ if (len < 0)
+ err(1, "%s: try_0write", test);
+ if (len != 0)
+ errx(1, "%s: try_0write: returned %zd", test, len);
+}
+
+static void
+setup_udp(const char *test, int *fdp, int port1, int port2)
+{
+ struct sockaddr_in sin;
+ int sock1, sock2;
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ sin.sin_port = htons(port1);
+ sock1 = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock1 < 0)
+ err(1, "%s: setup_udp: socket", test);
+ if (bind(sock1, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ err(1, "%s: setup_udp: bind(%s, %d)", test,
+ inet_ntoa(sin.sin_addr), PORT1);
+ sin.sin_port = htons(port2);
+ if (connect(sock1, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ err(1, "%s: setup_udp: connect(%s, %d)", test,
+ inet_ntoa(sin.sin_addr), PORT2);
+
+ sock2 = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock2 < 0)
+ err(1, "%s: setup_udp: socket", test);
+ if (bind(sock2, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ err(1, "%s: setup_udp: bind(%s, %d)", test,
+ inet_ntoa(sin.sin_addr), PORT2);
+ sin.sin_port = htons(port1);
+ if (connect(sock2, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ err(1, "%s: setup_udp: connect(%s, %d)", test,
+ inet_ntoa(sin.sin_addr), PORT1);
+
+ fdp[0] = sock1;
+ fdp[1] = sock2;
+}
+
+static void
+setup_tcp(const char *test, int *fdp, int port)
+{
+ fd_set writefds, exceptfds;
+ struct sockaddr_in sin;
+ int ret, sock1, sock2, sock3;
+ struct timeval tv;
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ /*
+ * First set up the listen socket.
+ */
+ sin.sin_port = htons(port);
+ sock1 = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock1 < 0)
+ err(1, "%s: setup_tcp: socket", test);
+ if (bind(sock1, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ err(1, "%s: bind(%s, %d)", test, inet_ntoa(sin.sin_addr),
+ PORT1);
+ if (listen(sock1, -1) < 0)
+ err(1, "%s: listen", test);
+
+ /*
+ * Now connect to it, non-blocking so that we don't deadlock against
+ * ourselves.
+ */
+ sock2 = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock2 < 0)
+ err(1, "%s: setup_tcp: socket", test);
+ if (fcntl(sock2, F_SETFL, O_NONBLOCK) < 0)
+ err(1, "%s: setup_tcp: fcntl(O_NONBLOCK)", test);
+ if (connect(sock2, (struct sockaddr *)&sin, sizeof(sin)) < 0 &&
+ errno != EINPROGRESS)
+ err(1, "%s: setup_tcp: connect(%s, %d)", test,
+ inet_ntoa(sin.sin_addr), PORT1);
+
+ /*
+ * Now pick up the connection after sleeping a moment to make sure
+ * there's been time for some packets to go back and forth.
+ */
+ if (sleep(1) != 0)
+ err(1, "%s: sleep(1)", test);
+ sock3 = accept(sock1, NULL, NULL);
+ if (sock3 < 0)
+ err(1, "%s: accept", test);
+ if (sleep(1) != 0)
+ err(1, "%s: sleep(1)", test);
+
+ FD_ZERO(&writefds);
+ FD_SET(sock2, &writefds);
+ FD_ZERO(&exceptfds);
+ FD_SET(sock2, &exceptfds);
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = select(sock2 + 1, NULL, &writefds, &exceptfds, &tv);
+ if (ret < 0)
+ err(1, "%s: setup_tcp: select", test);
+ if (FD_ISSET(sock2, &exceptfds))
+ errx(1, "%s: setup_tcp: select: exception", test);
+ if (!FD_ISSET(sock2, &writefds))
+ errx(1, "%s: setup_tcp: select: not writable", test);
+
+ close(sock1);
+ fdp[0] = sock2;
+ fdp[1] = sock3;
+}
+
+static void
+setup_udsstream(const char *test, int *fdp)
+{
+
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fdp) < 0)
+ err(1, "%s: setup_udsstream: socketpair", test);
+}
+
+static void
+setup_udsdgram(const char *test, int *fdp)
+{
+
+ if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, fdp) < 0)
+ err(1, "%s: setup_udsdgram: socketpair", test);
+}
+
+static void
+setup_pipe(const char *test, int *fdp)
+{
+
+ if (pipe(fdp) < 0)
+ err(1, "%s: setup_pipe: pipe", test);
+}
+
+static void
+setup_fifo(const char *test, int *fdp)
+{
+ char path[] = "0send_fifo.XXXXXXX";
+ int fd1, fd2;
+
+ if (mkstemp(path) == -1)
+ err(1, "%s: setup_fifo: mktemp", test);
+ unlink(path);
+
+ if (mkfifo(path, 0600) < 0)
+ err(1, "%s: setup_fifo: mkfifo(%s)", test, path);
+
+ fd1 = open(path, O_RDONLY | O_NONBLOCK);
+ if (fd1 < 0)
+ err(1, "%s: setup_fifo: open(%s, O_RDONLY)", test, path);
+
+ fd2 = open(path, O_WRONLY | O_NONBLOCK);
+ if (fd2 < 0)
+ err(1, "%s: setup_fifo: open(%s, O_WRONLY)", test, path);
+
+ fdp[0] = fd2;
+ fdp[1] = fd1;
+}
+
+static void
+close_both(int *fdp)
+{
+
+ close(fdp[0]);
+ fdp[0] = -1;
+ close(fdp[1]);
+ fdp[1] = -1;
+}
+
+int
+main(void)
+{
+ int fd[2];
+
+ setup_udp("udp_0send", fd, PORT1, PORT2);
+ try_0send("udp_0send", fd[0]);
+ close_both(fd);
+
+ setup_udp("udp_0write", fd, PORT1 + 10, PORT2 + 10);
+ try_0write("udp_0write", fd[0]);
+ close_both(fd);
+
+ setup_tcp("tcp_0send", fd, PORT1);
+ try_0send("tcp_0send", fd[0]);
+ close_both(fd);
+
+ setup_tcp("tcp_0write", fd, PORT1 + 10);
+ try_0write("tcp_0write", fd[0]);
+ close_both(fd);
+
+ setup_udsstream("udsstream_0send", fd);
+ try_0send("udsstream_0send", fd[0]);
+ close_both(fd);
+
+ setup_udsstream("udsstream_0write", fd);
+ try_0write("udsstream_0write", fd[0]);
+ close_both(fd);
+
+ setup_udsdgram("udsdgram_0send", fd);
+ try_0send("udsdgram_0send", fd[0]);
+ close_both(fd);
+
+ setup_udsdgram("udsdgram_0write", fd);
+ try_0write("udsdgram_0write", fd[0]);
+ close_both(fd);
+
+ setup_pipe("pipe_0write", fd);
+ try_0write("pipd_0write", fd[0]);
+ close_both(fd);
+
+ setup_fifo("fifo_0write", fd);
+ try_0write("fifo_0write", fd[0]);
+ close_both(fd);
+
+ return (0);
+}