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