19d623225SRobert Watson /*-
29d623225SRobert Watson * Copyright (c) 2005 Robert N. M. Watson
39d623225SRobert Watson * All rights reserved.
49d623225SRobert Watson *
59d623225SRobert Watson * Redistribution and use in source and binary forms, with or without
69d623225SRobert Watson * modification, are permitted provided that the following conditions
79d623225SRobert Watson * are met:
89d623225SRobert Watson * 1. Redistributions of source code must retain the above copyright
99d623225SRobert Watson * notice, this list of conditions and the following disclaimer.
109d623225SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright
119d623225SRobert Watson * notice, this list of conditions and the following disclaimer in the
129d623225SRobert Watson * documentation and/or other materials provided with the distribution.
139d623225SRobert Watson *
149d623225SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
159d623225SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
169d623225SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
179d623225SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
189d623225SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
199d623225SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
209d623225SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
219d623225SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
229d623225SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
239d623225SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
249d623225SRobert Watson * SUCH DAMAGE.
259d623225SRobert Watson */
269d623225SRobert Watson
279d623225SRobert Watson #include <sys/types.h>
289d623225SRobert Watson #include <sys/socket.h>
299d623225SRobert Watson #include <sys/un.h>
309d623225SRobert Watson
319d623225SRobert Watson #include <err.h>
329d623225SRobert Watson #include <errno.h>
339d623225SRobert Watson #include <limits.h>
349d623225SRobert Watson #include <stdio.h>
359d623225SRobert Watson #include <string.h>
369d623225SRobert Watson #include <unistd.h>
379d623225SRobert Watson
389d623225SRobert Watson /*
399d623225SRobert Watson * Simple regression test to exercise some error cases relating to the use of
409d623225SRobert Watson * bind() and connect() on UNIX domain sockets. In particular, make sure
419d623225SRobert Watson * that when two sockets rendezvous using the file system name space, they
429d623225SRobert Watson * get the expected success/failure cases.
439d623225SRobert Watson *
449d623225SRobert Watson * TODO:
459d623225SRobert Watson * - Check that the resulting file mode/owner are right.
469d623225SRobert Watson * - Do the same tests with UNIX domain sockets.
479d623225SRobert Watson * - Check the results of getsockaddr() and getpeeraddr().
489d623225SRobert Watson */
499d623225SRobert Watson
509d623225SRobert Watson #define SOCK_NAME_ONE "socket.1"
519d623225SRobert Watson #define SOCK_NAME_TWO "socket.2"
529d623225SRobert Watson
539d623225SRobert Watson #define UNWIND_MAX 1024
549d623225SRobert Watson
5538a7f1e4SEnji Cooper static int unwind_len;
5638a7f1e4SEnji Cooper static struct unwind {
579d623225SRobert Watson char u_path[PATH_MAX];
589d623225SRobert Watson } unwind_list[UNWIND_MAX];
599d623225SRobert Watson
609d623225SRobert Watson static void
push_path(const char * path)619d623225SRobert Watson push_path(const char *path)
629d623225SRobert Watson {
639d623225SRobert Watson
649d623225SRobert Watson if (unwind_len >= UNWIND_MAX)
659d623225SRobert Watson err(-1, "push_path: one path too many (%s)", path);
669d623225SRobert Watson
679d623225SRobert Watson strlcpy(unwind_list[unwind_len].u_path, path, PATH_MAX);
689d623225SRobert Watson unwind_len++;
699d623225SRobert Watson }
709d623225SRobert Watson
719d623225SRobert Watson static void
unwind(void)729d623225SRobert Watson unwind(void)
739d623225SRobert Watson {
749d623225SRobert Watson int i;
759d623225SRobert Watson
769d623225SRobert Watson for (i = unwind_len - 1; i >= 0; i--) {
779d623225SRobert Watson unlink(unwind_list[i].u_path);
789d623225SRobert Watson rmdir(unwind_list[i].u_path);
799d623225SRobert Watson }
809d623225SRobert Watson }
819d623225SRobert Watson
829d623225SRobert Watson static int
bind_test(const char * directory_path)839d623225SRobert Watson bind_test(const char *directory_path)
849d623225SRobert Watson {
859d623225SRobert Watson char socket_path[PATH_MAX];
869d623225SRobert Watson struct sockaddr_un sun;
879d623225SRobert Watson int sock1, sock2;
889d623225SRobert Watson
899d623225SRobert Watson sock1 = socket(PF_UNIX, SOCK_STREAM, 0);
909d623225SRobert Watson if (sock1 < 0) {
919d623225SRobert Watson warn("bind_test: socket(PF_UNIX, SOCK_STREAM, 0)");
929d623225SRobert Watson return (-1);
939d623225SRobert Watson }
949d623225SRobert Watson
959d623225SRobert Watson if (snprintf(socket_path, sizeof(socket_path), "%s/%s",
969d623225SRobert Watson directory_path, SOCK_NAME_ONE) >= PATH_MAX) {
979d623225SRobert Watson warn("bind_test: snprintf(socket_path)");
989d623225SRobert Watson close(sock1);
999d623225SRobert Watson return (-1);
1009d623225SRobert Watson }
1019d623225SRobert Watson
1029d623225SRobert Watson bzero(&sun, sizeof(sun));
1039d623225SRobert Watson sun.sun_len = sizeof(sun);
1049d623225SRobert Watson sun.sun_family = AF_UNIX;
1059d623225SRobert Watson if (snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path)
10638a7f1e4SEnji Cooper >= (int)sizeof(sun.sun_path)) {
1079d623225SRobert Watson warn("bind_test: snprintf(sun.sun_path)");
1089d623225SRobert Watson close(sock1);
1099d623225SRobert Watson return (-1);
1109d623225SRobert Watson }
1119d623225SRobert Watson
1129d623225SRobert Watson if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
1139d623225SRobert Watson warn("bind_test: bind(sun) #1");
1149d623225SRobert Watson close(sock1);
1159d623225SRobert Watson return (-1);
1169d623225SRobert Watson }
1179d623225SRobert Watson
1189d623225SRobert Watson push_path(socket_path);
1199d623225SRobert Watson
1209d623225SRobert Watson /*
1219d623225SRobert Watson * Once a STREAM UNIX domain socket has been bound, it can't be
1229d623225SRobert Watson * rebound. Expected error is EINVAL.
1239d623225SRobert Watson */
1249d623225SRobert Watson if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
1259d623225SRobert Watson warnx("bind_test: bind(sun) #2 succeeded");
1269d623225SRobert Watson close(sock1);
1279d623225SRobert Watson return (-1);
1289d623225SRobert Watson }
1299d623225SRobert Watson if (errno != EINVAL) {
1309d623225SRobert Watson warn("bind_test: bind(sun) #2");
1319d623225SRobert Watson close(sock1);
1329d623225SRobert Watson return (-1);
1339d623225SRobert Watson }
1349d623225SRobert Watson
1359d623225SRobert Watson sock2 = socket(PF_UNIX, SOCK_STREAM, 0);
1369d623225SRobert Watson if (sock2 < 0) {
1379d623225SRobert Watson warn("bind_test: socket(PF_UNIX, SOCK_STREAM, 0)");
1389d623225SRobert Watson close(sock1);
1399d623225SRobert Watson return (-1);
1409d623225SRobert Watson }
1419d623225SRobert Watson
1429d623225SRobert Watson /*
1439d623225SRobert Watson * Since a socket is already bound to the pathname, it can't be bound
1449d623225SRobert Watson * to a second socket. Expected error is EADDRINUSE.
1459d623225SRobert Watson */
1469d623225SRobert Watson if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
1479d623225SRobert Watson warnx("bind_test: bind(sun) #3 succeeded");
1489d623225SRobert Watson close(sock1);
1499d623225SRobert Watson close(sock2);
1509d623225SRobert Watson return (-1);
1519d623225SRobert Watson }
1529d623225SRobert Watson if (errno != EADDRINUSE) {
1539d623225SRobert Watson warn("bind_test: bind(sun) #2");
1549d623225SRobert Watson close(sock1);
1559d623225SRobert Watson close(sock2);
1569d623225SRobert Watson return (-1);
1579d623225SRobert Watson }
1589d623225SRobert Watson
1599d623225SRobert Watson close(sock1);
1609d623225SRobert Watson
1619d623225SRobert Watson /*
1629d623225SRobert Watson * The socket bound to the pathname has been closed, but the pathname
1639d623225SRobert Watson * can't be reused without first being unlinked. Expected error is
1649d623225SRobert Watson * EADDRINUSE.
1659d623225SRobert Watson */
1669d623225SRobert Watson if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
1679d623225SRobert Watson warnx("bind_test: bind(sun) #4 succeeded");
1689d623225SRobert Watson close(sock2);
1699d623225SRobert Watson return (-1);
1709d623225SRobert Watson }
1719d623225SRobert Watson if (errno != EADDRINUSE) {
1729d623225SRobert Watson warn("bind_test: bind(sun) #4");
1739d623225SRobert Watson close(sock2);
1749d623225SRobert Watson return (-1);
1759d623225SRobert Watson }
1769d623225SRobert Watson
1779d623225SRobert Watson unlink(socket_path);
1789d623225SRobert Watson
1799d623225SRobert Watson /*
1809d623225SRobert Watson * The pathname is now free, so the socket should be able to bind to
1819d623225SRobert Watson * it.
1829d623225SRobert Watson */
1839d623225SRobert Watson if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
1849d623225SRobert Watson warn("bind_test: bind(sun) #5");
1859d623225SRobert Watson close(sock2);
1869d623225SRobert Watson return (-1);
1879d623225SRobert Watson }
1889d623225SRobert Watson
1899d623225SRobert Watson close(sock2);
1909d623225SRobert Watson return (0);
1919d623225SRobert Watson }
1929d623225SRobert Watson
1939d623225SRobert Watson static int
connect_test(const char * directory_path)1949d623225SRobert Watson connect_test(const char *directory_path)
1959d623225SRobert Watson {
1969d623225SRobert Watson char socket_path[PATH_MAX];
1979d623225SRobert Watson struct sockaddr_un sun;
1989d623225SRobert Watson int sock1, sock2;
1999d623225SRobert Watson
2009d623225SRobert Watson sock1 = socket(PF_UNIX, SOCK_STREAM, 0);
2019d623225SRobert Watson if (sock1 < 0) {
2029d623225SRobert Watson warn("connect_test: socket(PF_UNIX, SOCK_STREAM, 0)");
2039d623225SRobert Watson return (-1);
2049d623225SRobert Watson }
2059d623225SRobert Watson
2069d623225SRobert Watson if (snprintf(socket_path, sizeof(socket_path), "%s/%s",
2079d623225SRobert Watson directory_path, SOCK_NAME_TWO) >= PATH_MAX) {
2089d623225SRobert Watson warn("connect_test: snprintf(socket_path)");
2099d623225SRobert Watson close(sock1);
2109d623225SRobert Watson return (-1);
2119d623225SRobert Watson }
2129d623225SRobert Watson
2139d623225SRobert Watson bzero(&sun, sizeof(sun));
2149d623225SRobert Watson sun.sun_len = sizeof(sun);
2159d623225SRobert Watson sun.sun_family = AF_UNIX;
2169d623225SRobert Watson if (snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path)
21738a7f1e4SEnji Cooper >= (int)sizeof(sun.sun_path)) {
2189d623225SRobert Watson warn("connect_test: snprintf(sun.sun_path)");
2199d623225SRobert Watson close(sock1);
2209d623225SRobert Watson return (-1);
2219d623225SRobert Watson }
2229d623225SRobert Watson
2239d623225SRobert Watson /*
2249d623225SRobert Watson * Try connecting to a path that doesn't yet exist. Should fail with
2259d623225SRobert Watson * ENOENT.
2269d623225SRobert Watson */
2279d623225SRobert Watson if (connect(sock1, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
2289d623225SRobert Watson warnx("connect_test: connect(sun) #1 succeeded");
2299d623225SRobert Watson close(sock1);
2309d623225SRobert Watson return (-1);
2319d623225SRobert Watson }
2329d623225SRobert Watson if (errno != ENOENT) {
2339d623225SRobert Watson warn("connect_test: connect(sun) #1");
2349d623225SRobert Watson close(sock1);
2359d623225SRobert Watson return (-1);
2369d623225SRobert Watson }
2379d623225SRobert Watson
2389d623225SRobert Watson if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
2399d623225SRobert Watson warn("connect_test: bind(sun) #1");
2409d623225SRobert Watson close(sock1);
2419d623225SRobert Watson return (-1);
2429d623225SRobert Watson }
2439d623225SRobert Watson
2449d623225SRobert Watson if (listen(sock1, 3) < 0) {
2459d623225SRobert Watson warn("connect_test: listen(sock1)");
2469d623225SRobert Watson close(sock1);
2479d623225SRobert Watson return (-1);
2489d623225SRobert Watson }
2499d623225SRobert Watson
2509d623225SRobert Watson push_path(socket_path);
2519d623225SRobert Watson
2529d623225SRobert Watson sock2 = socket(PF_UNIX, SOCK_STREAM, 0);
2539d623225SRobert Watson if (sock2 < 0) {
2549d623225SRobert Watson warn("socket(PF_UNIX, SOCK_STREAM, 0)");
2559d623225SRobert Watson close(sock1);
2569d623225SRobert Watson return (-1);
2579d623225SRobert Watson }
2589d623225SRobert Watson
2599d623225SRobert Watson /*
2609d623225SRobert Watson * Do a simple connect and make sure that works.
2619d623225SRobert Watson */
2629d623225SRobert Watson if (connect(sock2, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
2639d623225SRobert Watson warn("connect(sun) #2");
2649d623225SRobert Watson close(sock1);
2659d623225SRobert Watson return (-1);
2669d623225SRobert Watson }
2679d623225SRobert Watson
2689d623225SRobert Watson close(sock2);
2699d623225SRobert Watson
2709d623225SRobert Watson close(sock1);
2719d623225SRobert Watson
2729d623225SRobert Watson sock2 = socket(PF_UNIX, SOCK_STREAM, 0);
2739d623225SRobert Watson if (sock2 < 0) {
2749d623225SRobert Watson warn("socket(PF_UNIX, SOCK_STREAM, 0)");
2759d623225SRobert Watson return (-1);
2769d623225SRobert Watson }
2779d623225SRobert Watson
2789d623225SRobert Watson /*
2799d623225SRobert Watson * Confirm that once the listen socket is closed, we get a
2809d623225SRobert Watson * connection refused (ECONNREFUSED) when attempting to connect to
2819d623225SRobert Watson * the pathname.
2829d623225SRobert Watson */
2839d623225SRobert Watson if (connect(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
2849d623225SRobert Watson warnx("connect(sun) #3 succeeded");
2859d623225SRobert Watson close(sock2);
2869d623225SRobert Watson return (-1);
2879d623225SRobert Watson }
2889d623225SRobert Watson if (errno != ECONNREFUSED) {
2899d623225SRobert Watson warn("connect(sun) #3");
2909d623225SRobert Watson close(sock2);
2919d623225SRobert Watson return (-1);
2929d623225SRobert Watson }
2939d623225SRobert Watson
2949d623225SRobert Watson close(sock2);
2959d623225SRobert Watson unlink(socket_path);
2969d623225SRobert Watson return (0);
2979d623225SRobert Watson }
2989d623225SRobert Watson int
main(void)29938a7f1e4SEnji Cooper main(void)
3009d623225SRobert Watson {
3019d623225SRobert Watson char directory_path[PATH_MAX];
3029d623225SRobert Watson int error;
3039d623225SRobert Watson
3049d623225SRobert Watson strlcpy(directory_path, "/tmp/unix_bind.XXXXXXX", PATH_MAX);
3059d623225SRobert Watson if (mkdtemp(directory_path) == NULL)
3069d623225SRobert Watson err(-1, "mkdtemp");
3079d623225SRobert Watson push_path(directory_path);
3089d623225SRobert Watson
3099d623225SRobert Watson error = bind_test(directory_path);
3109d623225SRobert Watson
3119d623225SRobert Watson if (error == 0)
3129d623225SRobert Watson error = connect_test(directory_path);
3139d623225SRobert Watson
3149d623225SRobert Watson unwind();
3159d623225SRobert Watson return (error);
3169d623225SRobert Watson }
317