xref: /freebsd/tests/sys/kern/unix_connectat.c (revision 3baada83bf2deb48c7251f8de3f73e489338d4f1)
1e181e234SJohn Ericson /*-
2e181e234SJohn Ericson  * SPDX-License-Identifier: BSD-2-Clause
3e181e234SJohn Ericson  *
4e181e234SJohn Ericson  * Copyright (c) 2026 John Ericson
5e181e234SJohn Ericson  *
6e181e234SJohn Ericson  * Redistribution and use in source and binary forms, with or without
7e181e234SJohn Ericson  * modification, are permitted provided that the following conditions
8e181e234SJohn Ericson  * are met:
9e181e234SJohn Ericson  * 1. Redistributions of source code must retain the above copyright
10e181e234SJohn Ericson  *    notice, this list of conditions and the following disclaimer.
11e181e234SJohn Ericson  * 2. Redistributions in binary form must reproduce the above copyright
12e181e234SJohn Ericson  *    notice, this list of conditions and the following disclaimer in the
13e181e234SJohn Ericson  *    documentation and/or other materials provided with the distribution.
14e181e234SJohn Ericson  *
15e181e234SJohn Ericson  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16e181e234SJohn Ericson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17e181e234SJohn Ericson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18e181e234SJohn Ericson  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19e181e234SJohn Ericson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20e181e234SJohn Ericson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21e181e234SJohn Ericson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22e181e234SJohn Ericson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23e181e234SJohn Ericson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24e181e234SJohn Ericson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25e181e234SJohn Ericson  * SUCH DAMAGE.
26e181e234SJohn Ericson  */
27e181e234SJohn Ericson 
28e181e234SJohn Ericson /*
29e181e234SJohn Ericson  * Tests for connectat(2) naming a unix-domain peer by descriptor.
30e181e234SJohn Ericson  *
31e181e234SJohn Ericson  * A peer can be named three ways -- as the socket object itself, as a bound
32e181e234SJohn Ericson  * socket's filesystem node, or as an fdescfs /dev/fd node standing in for a
33e181e234SJohn Ericson  * socket descriptor -- and each of the two filesystem nodes can be reached
34e181e234SJohn Ericson  * either by an empty sun_path over a descriptor or by a pathname.  The socket
35e181e234SJohn Ericson  * object has no pathname form (a path that names a descriptor is the /dev/fd
36e181e234SJohn Ericson  * node, not the socket directly), giving five combinations, all of which must
37e181e234SJohn Ericson  * reach the same peer:
38e181e234SJohn Ericson  *
39e181e234SJohn Ericson  *                          | empty sun_path (fd)      | pathname
40e181e234SJohn Ericson  *   -----------------------+--------------------------+-----------------------
41e181e234SJohn Ericson  *   socket object          | fd is the socket         | (n/a: a path to a
42e181e234SJohn Ericson  *                          | -> stream, dgram, ...    |  descriptor is /dev/fd)
43e181e234SJohn Ericson  *   -----------------------+--------------------------+-----------------------
44e181e234SJohn Ericson  *   bound socket file      | O_PATH handle of the     | classic bind-path
45e181e234SJohn Ericson  *   (VSOCK vnode)          | socket's vnode           | lookup
46e181e234SJohn Ericson  *                          | -> empty_path_vnode      | -> path
47e181e234SJohn Ericson  *   -----------------------+--------------------------+-----------------------
48e181e234SJohn Ericson  *   fdescfs node of a      | O_PATH handle of the     | the "N" pathname,
49e181e234SJohn Ericson  *   socket descriptor      | fdescfs node             | absolute or relative
50e181e234SJohn Ericson  *   (VNON vnode)           | -> empty_path_devfd      | -> devfd,
51e181e234SJohn Ericson  *                          |                          |    devfd_relative
52e181e234SJohn Ericson  *
53e181e234SJohn Ericson  * An empty sun_path is signalled by sun_len == offsetof(.., sun_path).
54e181e234SJohn Ericson  *
55e181e234SJohn Ericson  * The fdescfs cases mount their own fdescfs instance rather than relying on
56e181e234SJohn Ericson  * the host's /dev/fd, so they require root; see mount_fdescfs() below.
57e181e234SJohn Ericson  */
58e181e234SJohn Ericson 
59e181e234SJohn Ericson #include <sys/param.h>
60e181e234SJohn Ericson #include <sys/capsicum.h>
61e181e234SJohn Ericson #include <sys/mount.h>
62e181e234SJohn Ericson #include <sys/socket.h>
63e181e234SJohn Ericson #include <sys/stat.h>
64e181e234SJohn Ericson #include <sys/un.h>
65e181e234SJohn Ericson #include <errno.h>
66e181e234SJohn Ericson #include <fcntl.h>
67e181e234SJohn Ericson #include <mntopts.h>
68e181e234SJohn Ericson #include <netinet/in.h>
69e181e234SJohn Ericson #include <stdio.h>
70e181e234SJohn Ericson #include <string.h>
71e181e234SJohn Ericson #include <unistd.h>
72e181e234SJohn Ericson 
73e181e234SJohn Ericson #include <atf-c.h>
74e181e234SJohn Ericson 
75e181e234SJohn Ericson /* An AF_UNIX address with an empty path: "the fd is the peer". */
76e181e234SJohn Ericson static const struct sockaddr_un empty_sun = {
77e181e234SJohn Ericson 	.sun_family = AF_UNIX,
78e181e234SJohn Ericson 	.sun_len = offsetof(struct sockaddr_un, sun_path),
79e181e234SJohn Ericson };
80e181e234SJohn Ericson 
81*3baada83SDevin Teske /* A nonempty address whose path starts with NUL, as Linux abstract names do. */
82*3baada83SDevin Teske static const struct sockaddr_un nul_sun = {
83*3baada83SDevin Teske 	.sun_family = AF_UNIX,
84*3baada83SDevin Teske 	.sun_len = offsetof(struct sockaddr_un, sun_path) + 2,
85*3baada83SDevin Teske 	.sun_path = "\0x",
86*3baada83SDevin Teske };
87*3baada83SDevin Teske 
88ce3750b5SJohn Ericson /* Make a bound, listening stream socket. */
89e181e234SJohn Ericson static int
mklistener(const char * path)90e181e234SJohn Ericson mklistener(const char *path)
91e181e234SJohn Ericson {
92e181e234SJohn Ericson 	struct sockaddr_un sun = { .sun_family = AF_UNIX };
93e181e234SJohn Ericson 	int l;
94e181e234SJohn Ericson 
95e181e234SJohn Ericson 	strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
96e181e234SJohn Ericson 	sun.sun_len = SUN_LEN(&sun);
97e181e234SJohn Ericson 	ATF_REQUIRE((l = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
98e181e234SJohn Ericson 	ATF_REQUIRE_MSG(bind(l, (struct sockaddr *)&sun, sun.sun_len) == 0,
99e181e234SJohn Ericson 	    "bind(%s): %s", path, strerror(errno));
100e181e234SJohn Ericson 	ATF_REQUIRE_MSG(listen(l, 1) == 0, "listen: %s", strerror(errno));
101e181e234SJohn Ericson 	return (l);
102e181e234SJohn Ericson }
103e181e234SJohn Ericson 
104e181e234SJohn Ericson static int
fdconnect(int fd,int s)105e181e234SJohn Ericson fdconnect(int fd, int s)
106e181e234SJohn Ericson {
107e181e234SJohn Ericson 	return (connectat(fd, s, (const struct sockaddr *)&empty_sun,
108e181e234SJohn Ericson 	    empty_sun.sun_len));
109e181e234SJohn Ericson }
110e181e234SJohn Ericson 
111e181e234SJohn Ericson /* connectat(2) to a pathname, relative to fd (AT_FDCWD for absolute). */
112e181e234SJohn Ericson static int
pathconnect(int fd,int s,const char * path)113e181e234SJohn Ericson pathconnect(int fd, int s, const char *path)
114e181e234SJohn Ericson {
115e181e234SJohn Ericson 	struct sockaddr_un sun = { .sun_family = AF_UNIX };
116e181e234SJohn Ericson 
117e181e234SJohn Ericson 	strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
118e181e234SJohn Ericson 	sun.sun_len = SUN_LEN(&sun);
119e181e234SJohn Ericson 	return (connectat(fd, s, (const struct sockaddr *)&sun, sun.sun_len));
120e181e234SJohn Ericson }
121e181e234SJohn Ericson 
122e181e234SJohn Ericson /* Where the fdescfs cases mount fdescfs, inside the test's work directory. */
123e181e234SJohn Ericson #define	FDDIR	"fd"
124e181e234SJohn Ericson 
125e181e234SJohn Ericson /*
126e181e234SJohn Ericson  * Mount an fdescfs instance on FDDIR, enabling each mount option flag in the
127e181e234SJohn Ericson  * NULL-terminated 'opts' (NULL for a plain mount).  Mounting our own instance
128e181e234SJohn Ericson  * rather than relying on the host's /dev/fd keeps the fdescfs cases
129e181e234SJohn Ericson  * self-contained: they exercise real fdescfs lookups regardless of how the
130e181e234SJohn Ericson  * host is set up, and the mode-specific behaviour below is then well defined.
131e181e234SJohn Ericson  * Skips if the kernel has no fdescfs.
132e181e234SJohn Ericson  */
133e181e234SJohn Ericson static void
mount_fdescfs(const char * const * opts)134e181e234SJohn Ericson mount_fdescfs(const char * const *opts)
135e181e234SJohn Ericson {
136e181e234SJohn Ericson 	struct iovec *iov;
137e181e234SJohn Ericson 	char errmsg[1024];
138e181e234SJohn Ericson 	int error, iovlen;
139e181e234SJohn Ericson 
140e181e234SJohn Ericson 	ATF_REQUIRE_MSG(mkdir(FDDIR, 0755) == 0 || errno == EEXIST,
141e181e234SJohn Ericson 	    "mkdir %s: %s", FDDIR, strerror(errno));
142e181e234SJohn Ericson 
143e181e234SJohn Ericson 	iov = NULL;
144e181e234SJohn Ericson 	iovlen = 0;
145e181e234SJohn Ericson 	build_iovec(&iov, &iovlen, __DECONST(char *, "fstype"),
146e181e234SJohn Ericson 	    __DECONST(char *, "fdescfs"), (size_t)-1);
147e181e234SJohn Ericson 	build_iovec(&iov, &iovlen, __DECONST(char *, "fspath"),
148e181e234SJohn Ericson 	    __DECONST(char *, FDDIR), (size_t)-1);
149e181e234SJohn Ericson 	for (; opts != NULL && *opts != NULL; opts++)
150e181e234SJohn Ericson 		build_iovec(&iov, &iovlen, __DECONST(char *, *opts), NULL,
151e181e234SJohn Ericson 		    (size_t)-1);
152e181e234SJohn Ericson 	build_iovec(&iov, &iovlen, __DECONST(char *, "errmsg"), errmsg,
153e181e234SJohn Ericson 	    sizeof(errmsg));
154e181e234SJohn Ericson 
155e181e234SJohn Ericson 	errmsg[0] = '\0';
156e181e234SJohn Ericson 	error = nmount(iov, iovlen, 0);
157e181e234SJohn Ericson 	if (error != 0 && errno == ENODEV)
158e181e234SJohn Ericson 		atf_tc_skip("no fdescfs support in the kernel");
159e181e234SJohn Ericson 	ATF_REQUIRE_MSG(error == 0, "mount fdescfs on %s: %s", FDDIR,
160e181e234SJohn Ericson 	    errmsg[0] != '\0' ? errmsg : strerror(errno));
161e181e234SJohn Ericson 
162e181e234SJohn Ericson 	free_iovec(&iov, &iovlen);
163e181e234SJohn Ericson }
164e181e234SJohn Ericson 
165e181e234SJohn Ericson /* Name descriptor 'fd' within the fdescfs mounted above. */
166e181e234SJohn Ericson static void
fdpath(char * buf,size_t len,int fd)167e181e234SJohn Ericson fdpath(char *buf, size_t len, int fd)
168e181e234SJohn Ericson {
169e181e234SJohn Ericson 	int n;
170e181e234SJohn Ericson 
171e181e234SJohn Ericson 	n = snprintf(buf, len, FDDIR "/%d", fd);
172e181e234SJohn Ericson 	ATF_REQUIRE(n > 0 && (size_t)n < len);
173e181e234SJohn Ericson }
174e181e234SJohn Ericson 
175e181e234SJohn Ericson /*
176e181e234SJohn Ericson  * Boilerplate for a case that mounts fdescfs: mounting requires root, and the
177e181e234SJohn Ericson  * mount has to be undone even when the body fails, or the work directory
178e181e234SJohn Ericson  * cannot be removed.  Each body calls mount_fdescfs() itself, choosing the
179e181e234SJohn Ericson  * mount options it wants to exercise.
180e181e234SJohn Ericson  */
181e181e234SJohn Ericson #define	FDESCFS_TC(name)						\
182e181e234SJohn Ericson 	ATF_TC_WITH_CLEANUP(name);					\
183e181e234SJohn Ericson 	ATF_TC_HEAD(name, tc)						\
184e181e234SJohn Ericson 	{								\
185e181e234SJohn Ericson 		atf_tc_set_md_var(tc, "require.user", "root");		\
186e181e234SJohn Ericson 	}								\
187e181e234SJohn Ericson 	ATF_TC_CLEANUP(name, tc)					\
188e181e234SJohn Ericson 	{								\
189e181e234SJohn Ericson 		(void)unmount(FDDIR, 0);				\
190e181e234SJohn Ericson 	}
191e181e234SJohn Ericson 
192e181e234SJohn Ericson /* Connect to a listening stream socket by its fd; pass data. */
193e181e234SJohn Ericson ATF_TC_WITHOUT_HEAD(stream);
ATF_TC_BODY(stream,tc)194e181e234SJohn Ericson ATF_TC_BODY(stream, tc)
195e181e234SJohn Ericson {
196e181e234SJohn Ericson 	char buf[8];
197e181e234SJohn Ericson 	int l, s, a;
198e181e234SJohn Ericson 
199e181e234SJohn Ericson 	l = mklistener("stream.sock");
200e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
201e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, fdconnect(l, s));
202e181e234SJohn Ericson 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
203e181e234SJohn Ericson 
204e181e234SJohn Ericson 	ATF_REQUIRE_EQ(5, write(s, "hello", 5));
205e181e234SJohn Ericson 	ATF_REQUIRE_EQ(5, read(a, buf, sizeof(buf)));
206e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, memcmp(buf, "hello", 5));
207e181e234SJohn Ericson 	ATF_REQUIRE_EQ(5, write(a, "world", 5));
208e181e234SJohn Ericson 	ATF_REQUIRE_EQ(5, read(s, buf, sizeof(buf)));
209e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, memcmp(buf, "world", 5));
210e181e234SJohn Ericson 
211e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(a));
212e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
213e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
214e181e234SJohn Ericson }
215e181e234SJohn Ericson 
216e181e234SJohn Ericson /* A bound listener's path is still reported to the connecting side. */
217e181e234SJohn Ericson ATF_TC_WITHOUT_HEAD(stream_bound);
ATF_TC_BODY(stream_bound,tc)218e181e234SJohn Ericson ATF_TC_BODY(stream_bound, tc)
219e181e234SJohn Ericson {
220e181e234SJohn Ericson 	struct sockaddr_un sun;
221e181e234SJohn Ericson 	socklen_t len;
222e181e234SJohn Ericson 	int l, s;
223e181e234SJohn Ericson 
224e181e234SJohn Ericson 	l = mklistener("bound.sock");
225e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
226e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, fdconnect(l, s));
227e181e234SJohn Ericson 
228e181e234SJohn Ericson 	memset(&sun, 0, sizeof(sun));
229e181e234SJohn Ericson 	len = sizeof(sun);
230e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, getpeername(s, (struct sockaddr *)&sun, &len));
231e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, strcmp(sun.sun_path, "bound.sock"));
232e181e234SJohn Ericson 
233e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
234e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
235e181e234SJohn Ericson }
236e181e234SJohn Ericson 
237ce3750b5SJohn Ericson /*
238ce3750b5SJohn Ericson  * A socket may listen while unbound, and connectat(2) reaches it by
239ce3750b5SJohn Ericson  * descriptor: with no pathname there is nothing else that could name it.
240ce3750b5SJohn Ericson  * mklistener() cannot be used, as it binds first.
241ce3750b5SJohn Ericson  */
242ce3750b5SJohn Ericson ATF_TC_WITHOUT_HEAD(listen_unbound);
ATF_TC_BODY(listen_unbound,tc)243ce3750b5SJohn Ericson ATF_TC_BODY(listen_unbound, tc)
244ce3750b5SJohn Ericson {
245ce3750b5SJohn Ericson 	char buf[8];
246ce3750b5SJohn Ericson 	int l, s, a;
247ce3750b5SJohn Ericson 
248ce3750b5SJohn Ericson 	ATF_REQUIRE((l = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
249ce3750b5SJohn Ericson 	ATF_REQUIRE_MSG(listen(l, 1) == 0, "listen: %s", strerror(errno));
250ce3750b5SJohn Ericson 
251ce3750b5SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
252ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, fdconnect(l, s));
253ce3750b5SJohn Ericson 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
254ce3750b5SJohn Ericson 
255ce3750b5SJohn Ericson 	/* A real connection, not just an accepted descriptor. */
256ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(5, write(s, "hello", 5));
257ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(5, read(a, buf, sizeof(buf)));
258ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, memcmp(buf, "hello", 5));
259ce3750b5SJohn Ericson 
260ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, close(a));
261ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
262ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
263ce3750b5SJohn Ericson }
264ce3750b5SJohn Ericson 
265ce3750b5SJohn Ericson /*
266ce3750b5SJohn Ericson  * A socket may be bound after it listens, so a listener can be published only
267ce3750b5SJohn Ericson  * once it is ready to accept, rather than leaving a window in which the socket
268ce3750b5SJohn Ericson  * file exists but connections to it are refused.  The late-bound name behaves
269ce3750b5SJohn Ericson  * like any other.  mklistener() cannot be used: it binds first.
270ce3750b5SJohn Ericson  */
271ce3750b5SJohn Ericson ATF_TC_WITHOUT_HEAD(bind_after_listen);
ATF_TC_BODY(bind_after_listen,tc)272ce3750b5SJohn Ericson ATF_TC_BODY(bind_after_listen, tc)
273ce3750b5SJohn Ericson {
274ce3750b5SJohn Ericson 	struct sockaddr_un sun = { .sun_family = AF_UNIX };
275ce3750b5SJohn Ericson 	struct sockaddr_un peer;
276ce3750b5SJohn Ericson 	socklen_t len;
277ce3750b5SJohn Ericson 	int l, s, a;
278ce3750b5SJohn Ericson 
279ce3750b5SJohn Ericson 	ATF_REQUIRE((l = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
280ce3750b5SJohn Ericson 	ATF_REQUIRE_MSG(listen(l, 1) == 0, "listen: %s", strerror(errno));
281ce3750b5SJohn Ericson 
282ce3750b5SJohn Ericson 	strlcpy(sun.sun_path, "late.sock", sizeof(sun.sun_path));
283ce3750b5SJohn Ericson 	sun.sun_len = SUN_LEN(&sun);
284ce3750b5SJohn Ericson 	ATF_REQUIRE_MSG(bind(l, (struct sockaddr *)&sun, sun.sun_len) == 0,
285ce3750b5SJohn Ericson 	    "bind after listen: %s", strerror(errno));
286ce3750b5SJohn Ericson 
287ce3750b5SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
288ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, pathconnect(AT_FDCWD, s, "late.sock"));
289ce3750b5SJohn Ericson 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
290ce3750b5SJohn Ericson 
291ce3750b5SJohn Ericson 	/* The name bound after listen(2) is reported to the peer. */
292ce3750b5SJohn Ericson 	memset(&peer, 0, sizeof(peer));
293ce3750b5SJohn Ericson 	len = sizeof(peer);
294ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, getpeername(s, (struct sockaddr *)&peer, &len));
295ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, strcmp(peer.sun_path, "late.sock"));
296ce3750b5SJohn Ericson 
297ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, close(a));
298ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
299ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
300ce3750b5SJohn Ericson }
301ce3750b5SJohn Ericson 
302ce3750b5SJohn Ericson /*
303ce3750b5SJohn Ericson  * A socket whose connection has gone away may become a listener in its own
304ce3750b5SJohn Ericson  * right: unp_soisdisconnected() leaves only SS_ISDISCONNECTED set, which
305ce3750b5SJohn Ericson  * solisten_proto_check() does not reject, and unp_disconnect() has already
306ce3750b5SJohn Ericson  * cleared unp_conn.  Only the bind requirement stood in the way, and then only
307ce3750b5SJohn Ericson  * for the usual client socket, which has no name.
308ce3750b5SJohn Ericson  *
309ce3750b5SJohn Ericson  * Note: this case is here only to document the current behavior and to catch
310ce3750b5SJohn Ericson  * it changing in the future.  Such socket reuse is not covered by the
311ce3750b5SJohn Ericson  * specification, and is discouraged and should not be utilized in real-world
312ce3750b5SJohn Ericson  * programs.
313ce3750b5SJohn Ericson  */
314ce3750b5SJohn Ericson ATF_TC_WITHOUT_HEAD(listen_after_disconnect);
ATF_TC_BODY(listen_after_disconnect,tc)315ce3750b5SJohn Ericson ATF_TC_BODY(listen_after_disconnect, tc)
316ce3750b5SJohn Ericson {
317ce3750b5SJohn Ericson 	int l, c, s, a;
318ce3750b5SJohn Ericson 
319ce3750b5SJohn Ericson 	/* Connect a pair, then drop the accepted end to disconnect 'c'. */
320ce3750b5SJohn Ericson 	ATF_REQUIRE((l = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
321ce3750b5SJohn Ericson 	ATF_REQUIRE_MSG(listen(l, 1) == 0, "listen: %s", strerror(errno));
322ce3750b5SJohn Ericson 	ATF_REQUIRE((c = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
323ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, fdconnect(l, c));
324ce3750b5SJohn Ericson 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
325ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, close(a));
326ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
327ce3750b5SJohn Ericson 
328ce3750b5SJohn Ericson 	/* The survivor listens, and takes a connection of its own. */
329ce3750b5SJohn Ericson 	ATF_REQUIRE_MSG(listen(c, 1) == 0, "listen: %s", strerror(errno));
330ce3750b5SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
331ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, fdconnect(c, s));
332ce3750b5SJohn Ericson 	ATF_REQUIRE((a = accept(c, NULL, NULL)) >= 0);
333ce3750b5SJohn Ericson 
334ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, close(a));
335ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
336ce3750b5SJohn Ericson 	ATF_REQUIRE_EQ(0, close(c));
337ce3750b5SJohn Ericson }
338ce3750b5SJohn Ericson 
339e181e234SJohn Ericson /* Connect a datagram socket to an unbound peer by its fd. */
340e181e234SJohn Ericson ATF_TC_WITHOUT_HEAD(dgram);
ATF_TC_BODY(dgram,tc)341e181e234SJohn Ericson ATF_TC_BODY(dgram, tc)
342e181e234SJohn Ericson {
343e181e234SJohn Ericson 	char buf[8];
344e181e234SJohn Ericson 	int p, s;
345e181e234SJohn Ericson 
346e181e234SJohn Ericson 	ATF_REQUIRE((p = socket(PF_UNIX, SOCK_DGRAM, 0)) >= 0);
347e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_DGRAM, 0)) >= 0);
348e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, fdconnect(p, s));
349e181e234SJohn Ericson 	ATF_REQUIRE_EQ(5, send(s, "hello", 5, 0));
350e181e234SJohn Ericson 	ATF_REQUIRE_EQ(5, recv(p, buf, sizeof(buf), 0));
351e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, memcmp(buf, "hello", 5));
352e181e234SJohn Ericson 
353e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
354e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(p));
355e181e234SJohn Ericson }
356e181e234SJohn Ericson 
357e181e234SJohn Ericson /*
358e181e234SJohn Ericson  * Matrix cell: empty path + a descriptor that names a bound socket's *vnode*
359e181e234SJohn Ericson  * (an O_PATH handle), not the socket object.  getsock() sees a non-socket and
360e181e234SJohn Ericson  * the connect falls back to an EMPTYPATH lookup that resolves the vnode.
361e181e234SJohn Ericson  */
362e181e234SJohn Ericson ATF_TC_WITHOUT_HEAD(empty_path_vnode);
ATF_TC_BODY(empty_path_vnode,tc)363e181e234SJohn Ericson ATF_TC_BODY(empty_path_vnode, tc)
364e181e234SJohn Ericson {
365e181e234SJohn Ericson 	int l, s, a, pathfd;
366e181e234SJohn Ericson 
367e181e234SJohn Ericson 	l = mklistener("evnode.sock");
368e181e234SJohn Ericson 	ATF_REQUIRE_MSG((pathfd = open("evnode.sock", O_PATH)) >= 0,
369e181e234SJohn Ericson 	    "open(O_PATH): %s", strerror(errno));
370e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
371e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, fdconnect(pathfd, s));
372e181e234SJohn Ericson 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
373e181e234SJohn Ericson 
374e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(a));
375e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
376e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(pathfd));
377e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
378e181e234SJohn Ericson }
379e181e234SJohn Ericson 
380e181e234SJohn Ericson /*
381e181e234SJohn Ericson  * Matrix cell: non-empty path naming a bound socket's vnode -- the classic
382e181e234SJohn Ericson  * connect-by-pathname case, here spelled through connectat(2).
383e181e234SJohn Ericson  */
384e181e234SJohn Ericson ATF_TC_WITHOUT_HEAD(path);
ATF_TC_BODY(path,tc)385e181e234SJohn Ericson ATF_TC_BODY(path, tc)
386e181e234SJohn Ericson {
387e181e234SJohn Ericson 	int l, s, a;
388e181e234SJohn Ericson 
389e181e234SJohn Ericson 	l = mklistener("path.sock");
390e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
391e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, pathconnect(AT_FDCWD, s, "path.sock"));
392e181e234SJohn Ericson 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
393e181e234SJohn Ericson 
394e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(a));
395e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
396e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
397e181e234SJohn Ericson }
398e181e234SJohn Ericson 
399e181e234SJohn Ericson /*
400e181e234SJohn Ericson  * Matrix cell: non-empty path that resolves to the socket *object* -- an
401e181e234SJohn Ericson  * fdescfs pathname naming the listener's descriptor.  This is plain
402e181e234SJohn Ericson  * connect(2), no empty path involved.
403e181e234SJohn Ericson  */
404e181e234SJohn Ericson FDESCFS_TC(devfd);
ATF_TC_BODY(devfd,tc)405e181e234SJohn Ericson ATF_TC_BODY(devfd, tc)
406e181e234SJohn Ericson {
407e181e234SJohn Ericson 	char path[32];
408e181e234SJohn Ericson 	int l, s, a;
409e181e234SJohn Ericson 
410e181e234SJohn Ericson 	mount_fdescfs(NULL);
411e181e234SJohn Ericson 	l = mklistener("devfd.sock");
412e181e234SJohn Ericson 	fdpath(path, sizeof(path), l);
413e181e234SJohn Ericson 
414e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
415e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, pathconnect(AT_FDCWD, s, path));
416e181e234SJohn Ericson 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
417e181e234SJohn Ericson 
418e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(a));
419e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
420e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
421e181e234SJohn Ericson }
422e181e234SJohn Ericson 
423e181e234SJohn Ericson /*
424e181e234SJohn Ericson  * Matrix cell variant of `devfd`: the same socket-object lookup, but reached
425e181e234SJohn Ericson  * through connectat(2)'s dirfd-relative resolution.  A directory descriptor
426e181e234SJohn Ericson  * for the fdescfs mount serves as the base, and the peer is named by the
427e181e234SJohn Ericson  * *relative* path "N" -- the listener's descriptor number.  NDINIT_ATRIGHTS
428e181e234SJohn Ericson  * anchors namei() at the dirfd, and fdescfs resolves that descriptor to the
429e181e234SJohn Ericson  * socket unp_connectat() connects to.
430e181e234SJohn Ericson  */
431e181e234SJohn Ericson FDESCFS_TC(devfd_relative);
ATF_TC_BODY(devfd_relative,tc)432e181e234SJohn Ericson ATF_TC_BODY(devfd_relative, tc)
433e181e234SJohn Ericson {
434e181e234SJohn Ericson 	char path[32];
435e181e234SJohn Ericson 	int l, s, a, dirfd;
436e181e234SJohn Ericson 
437e181e234SJohn Ericson 	mount_fdescfs(NULL);
438e181e234SJohn Ericson 	l = mklistener("devfd_rel.sock");
439e181e234SJohn Ericson 	ATF_REQUIRE_MSG((dirfd = open(FDDIR, O_DIRECTORY)) >= 0,
440e181e234SJohn Ericson 	    "open(%s, O_DIRECTORY): %s", FDDIR, strerror(errno));
441e181e234SJohn Ericson 
442e181e234SJohn Ericson 	/* Name the listener by its fd number, relative to the fdescfs dir. */
443e181e234SJohn Ericson 	ATF_REQUIRE(snprintf(path, sizeof(path), "%d", l) > 0);
444e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
445e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, pathconnect(dirfd, s, path));
446e181e234SJohn Ericson 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
447e181e234SJohn Ericson 
448e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(a));
449e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
450e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(dirfd));
451e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
452e181e234SJohn Ericson }
453e181e234SJohn Ericson 
454e181e234SJohn Ericson /*
455e181e234SJohn Ericson  * Matrix cell: empty path + an O_PATH handle to an fdescfs node.  getsock()
456e181e234SJohn Ericson  * sees a non-socket, the EMPTYPATH lookup resolves the synthetic fdescfs node,
457e181e234SJohn Ericson  * and opening that node yields the underlying descriptor -- the same socket.
458e181e234SJohn Ericson  * Reaches the fdescfs node by descriptor rather than by pathname.
459e181e234SJohn Ericson  */
460e181e234SJohn Ericson FDESCFS_TC(empty_path_devfd);
ATF_TC_BODY(empty_path_devfd,tc)461e181e234SJohn Ericson ATF_TC_BODY(empty_path_devfd, tc)
462e181e234SJohn Ericson {
463e181e234SJohn Ericson 	char path[32];
464e181e234SJohn Ericson 	int l, s, a, pathfd;
465e181e234SJohn Ericson 
466e181e234SJohn Ericson 	mount_fdescfs(NULL);
467e181e234SJohn Ericson 	l = mklistener("edevfd.sock");
468e181e234SJohn Ericson 	fdpath(path, sizeof(path), l);
469e181e234SJohn Ericson 	ATF_REQUIRE_MSG((pathfd = open(path, O_PATH)) >= 0,
470e181e234SJohn Ericson 	    "open(%s, O_PATH): %s", path, strerror(errno));
471e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
472e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, fdconnect(pathfd, s));
473e181e234SJohn Ericson 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
474e181e234SJohn Ericson 
475e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(a));
476e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
477e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(pathfd));
478e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
479e181e234SJohn Ericson }
480e181e234SJohn Ericson 
481e181e234SJohn Ericson /*
482e181e234SJohn Ericson  * An fdescfs pathname is resolved a single level: the descriptor it names must
483e181e234SJohn Ericson  * be the peer socket itself.  A node naming an O_PATH handle instead -- of the
484e181e234SJohn Ericson  * socket's *file* (VNON -> VSOCK), or of another fdescfs node (VNON -> VNON)
485e181e234SJohn Ericson  * -- is not chased another level, and the connect fails with ENOTSOCK.
486e181e234SJohn Ericson  *
487e181e234SJohn Ericson  * The descriptor is rejected by getsock(), before the vnode behind it is ever
488e181e234SJohn Ericson  * examined, so both indirections fail the same way.
489e181e234SJohn Ericson  */
490e181e234SJohn Ericson FDESCFS_TC(devfd_indirect);
ATF_TC_BODY(devfd_indirect,tc)491e181e234SJohn Ericson ATF_TC_BODY(devfd_indirect, tc)
492e181e234SJohn Ericson {
493e181e234SJohn Ericson 	char path[32], node[32];
494e181e234SJohn Ericson 	int l, s, pathfd, devfdfd;
495e181e234SJohn Ericson 
496e181e234SJohn Ericson 	mount_fdescfs(NULL);
497e181e234SJohn Ericson 	l = mklistener("devfd_ind.sock");
498e181e234SJohn Ericson 	fdpath(node, sizeof(node), l);
499e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
500e181e234SJohn Ericson 
501e181e234SJohn Ericson 	/* A node naming an O_PATH handle of the socket's file. */
502e181e234SJohn Ericson 	ATF_REQUIRE_MSG((pathfd = open("devfd_ind.sock", O_PATH)) >= 0,
503e181e234SJohn Ericson 	    "open(O_PATH): %s", strerror(errno));
504e181e234SJohn Ericson 	fdpath(path, sizeof(path), pathfd);
505e181e234SJohn Ericson 	ATF_REQUIRE_ERRNO(ENOTSOCK, pathconnect(AT_FDCWD, s, path) == -1);
506e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(pathfd));
507e181e234SJohn Ericson 
508e181e234SJohn Ericson 	/* A node naming an O_PATH handle of another such node. */
509e181e234SJohn Ericson 	ATF_REQUIRE_MSG((devfdfd = open(node, O_PATH)) >= 0,
510e181e234SJohn Ericson 	    "open(%s, O_PATH): %s", node, strerror(errno));
511e181e234SJohn Ericson 	fdpath(path, sizeof(path), devfdfd);
512e181e234SJohn Ericson 	ATF_REQUIRE_ERRNO(ENOTSOCK, pathconnect(AT_FDCWD, s, path) == -1);
513e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(devfdfd));
514e181e234SJohn Ericson 
515e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
516e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
517e181e234SJohn Ericson }
518e181e234SJohn Ericson 
519e181e234SJohn Ericson /*
520e181e234SJohn Ericson  * The same indirection under a "nodup" mount, which is where the single-level
521e181e234SJohn Ericson  * rule shows its seam: for a descriptor naming a vnode, fdescfs itself
522e181e234SJohn Ericson  * dereferences to that vnode rather than presenting a synthetic node, so the
523e181e234SJohn Ericson  * O_PATH handle of the socket's file resolves to the bound socket and the
524e181e234SJohn Ericson  * connect succeeds.  Whether the O_PATH is followed is the mount's business;
525e181e234SJohn Ericson  * resolving no more than one descriptor is ours.
526e181e234SJohn Ericson  */
527e181e234SJohn Ericson FDESCFS_TC(devfd_indirect_nodup);
ATF_TC_BODY(devfd_indirect_nodup,tc)528e181e234SJohn Ericson ATF_TC_BODY(devfd_indirect_nodup, tc)
529e181e234SJohn Ericson {
530e181e234SJohn Ericson 	static const char * const opts[] = { "nodup", NULL };
531e181e234SJohn Ericson 	char path[32];
532e181e234SJohn Ericson 	int l, s, a, pathfd;
533e181e234SJohn Ericson 
534e181e234SJohn Ericson 	mount_fdescfs(opts);
535e181e234SJohn Ericson 	l = mklistener("devfd_nodup.sock");
536e181e234SJohn Ericson 
537e181e234SJohn Ericson 	ATF_REQUIRE_MSG((pathfd = open("devfd_nodup.sock", O_PATH)) >= 0,
538e181e234SJohn Ericson 	    "open(O_PATH): %s", strerror(errno));
539e181e234SJohn Ericson 	fdpath(path, sizeof(path), pathfd);
540e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
541e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, pathconnect(AT_FDCWD, s, path));
542e181e234SJohn Ericson 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
543e181e234SJohn Ericson 
544e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(a));
545e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
546e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(pathfd));
547e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
548e181e234SJohn Ericson }
549e181e234SJohn Ericson 
550e181e234SJohn Ericson /*
551e181e234SJohn Ericson  * The mount modes differ in how fdescfs presents a descriptor node, which
552e181e234SJohn Ericson  * decides whether the node can name a peer at all:
553e181e234SJohn Ericson  *
554e181e234SJohn Ericson  *	(plain)			VNON node, dup semantics	connects
555e181e234SJohn Ericson  *	nodup			VNON node, since a socket is	connects
556e181e234SJohn Ericson  *				not a vnode descriptor
557e181e234SJohn Ericson  *	linrdlnk		VNON node, readlink for the	connects
558e181e234SJohn Ericson  *				Linux ABI
559e181e234SJohn Ericson  *	rdlnk			VLNK node, followed by namei	fails
560e181e234SJohn Ericson  *
561e181e234SJohn Ericson  * Only rdlnk makes the node a real symlink, and namei() then follows it;
562e181e234SJohn Ericson  * fdesc_readlink() has no path to offer for a socket, so the lookup ends on
563e181e234SJohn Ericson  * its "anon_inode:[unknown]" placeholder instead of the peer.  nodup composes
564e181e234SJohn Ericson  * with either readlink mode without changing this: it only redirects
565e181e234SJohn Ericson  * descriptors that name a vnode, which a socket descriptor does not.
566e181e234SJohn Ericson  */
567e181e234SJohn Ericson /*
568e181e234SJohn Ericson  * Mount fdescfs with 'opts' and connect to a listener through its node.
569e181e234SJohn Ericson  * 'error' is 0 if the connect must reach the peer, otherwise the errno it
570e181e234SJohn Ericson  * must fail with.
571e181e234SJohn Ericson  */
572e181e234SJohn Ericson static void
devfd_mode(const char * const * opts,int error)573e181e234SJohn Ericson devfd_mode(const char * const *opts, int error)
574e181e234SJohn Ericson {
575e181e234SJohn Ericson 	char path[32];
576e181e234SJohn Ericson 	int l, s, a, ret;
577e181e234SJohn Ericson 
578e181e234SJohn Ericson 	mount_fdescfs(opts);
579e181e234SJohn Ericson 	l = mklistener("mode.sock");
580e181e234SJohn Ericson 	fdpath(path, sizeof(path), l);
581e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
582e181e234SJohn Ericson 
583e181e234SJohn Ericson 	ret = pathconnect(AT_FDCWD, s, path);
584e181e234SJohn Ericson 	if (error == 0) {
585e181e234SJohn Ericson 		ATF_REQUIRE_MSG(ret == 0, "connect: %s", strerror(errno));
586e181e234SJohn Ericson 		ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
587e181e234SJohn Ericson 		ATF_REQUIRE_EQ(0, close(a));
588e181e234SJohn Ericson 	} else {
589e181e234SJohn Ericson 		ATF_REQUIRE_MSG(ret == -1 && errno == error,
590e181e234SJohn Ericson 		    "expected %s, got %s", strerror(error),
591e181e234SJohn Ericson 		    ret == 0 ? "success" : strerror(errno));
592e181e234SJohn Ericson 	}
593e181e234SJohn Ericson 
594e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
595e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
596e181e234SJohn Ericson }
597e181e234SJohn Ericson 
598e181e234SJohn Ericson /* Dup semantics, the plain mount: the node names the descriptor. */
599e181e234SJohn Ericson FDESCFS_TC(devfd_mode_plain);
ATF_TC_BODY(devfd_mode_plain,tc)600e181e234SJohn Ericson ATF_TC_BODY(devfd_mode_plain, tc)
601e181e234SJohn Ericson {
602e181e234SJohn Ericson 	static const char * const opts[] = { NULL };
603e181e234SJohn Ericson 
604e181e234SJohn Ericson 	devfd_mode(opts, 0);
605e181e234SJohn Ericson }
606e181e234SJohn Ericson 
607e181e234SJohn Ericson /* nodup only redirects descriptors that name a vnode, which a socket is not. */
608e181e234SJohn Ericson FDESCFS_TC(devfd_mode_nodup);
ATF_TC_BODY(devfd_mode_nodup,tc)609e181e234SJohn Ericson ATF_TC_BODY(devfd_mode_nodup, tc)
610e181e234SJohn Ericson {
611e181e234SJohn Ericson 	static const char * const opts[] = { "nodup", NULL };
612e181e234SJohn Ericson 
613e181e234SJohn Ericson 	devfd_mode(opts, 0);
614e181e234SJohn Ericson }
615e181e234SJohn Ericson 
616e181e234SJohn Ericson /* linrdlnk only adds readlink for the Linux ABI; the node stays VNON. */
617e181e234SJohn Ericson FDESCFS_TC(devfd_mode_linrdlnk);
ATF_TC_BODY(devfd_mode_linrdlnk,tc)618e181e234SJohn Ericson ATF_TC_BODY(devfd_mode_linrdlnk, tc)
619e181e234SJohn Ericson {
620e181e234SJohn Ericson 	static const char * const opts[] = { "linrdlnk", NULL };
621e181e234SJohn Ericson 
622e181e234SJohn Ericson 	devfd_mode(opts, 0);
623e181e234SJohn Ericson }
624e181e234SJohn Ericson 
625e181e234SJohn Ericson FDESCFS_TC(devfd_mode_nodup_linrdlnk);
ATF_TC_BODY(devfd_mode_nodup_linrdlnk,tc)626e181e234SJohn Ericson ATF_TC_BODY(devfd_mode_nodup_linrdlnk, tc)
627e181e234SJohn Ericson {
628e181e234SJohn Ericson 	static const char * const opts[] = { "nodup", "linrdlnk", NULL };
629e181e234SJohn Ericson 
630e181e234SJohn Ericson 	devfd_mode(opts, 0);
631e181e234SJohn Ericson }
632e181e234SJohn Ericson 
633e181e234SJohn Ericson /*
634e181e234SJohn Ericson  * rdlnk makes the node a real symlink, which namei() follows.
635e181e234SJohn Ericson  * fdesc_readlink() has no path to offer for a socket, so the lookup ends on
636e181e234SJohn Ericson  * its "anon_inode:[unknown]" placeholder rather than the peer.
637e181e234SJohn Ericson  */
638e181e234SJohn Ericson FDESCFS_TC(devfd_mode_rdlnk);
ATF_TC_BODY(devfd_mode_rdlnk,tc)639e181e234SJohn Ericson ATF_TC_BODY(devfd_mode_rdlnk, tc)
640e181e234SJohn Ericson {
641e181e234SJohn Ericson 	static const char * const opts[] = { "rdlnk", NULL };
642e181e234SJohn Ericson 
643e181e234SJohn Ericson 	devfd_mode(opts, ENOENT);
644e181e234SJohn Ericson }
645e181e234SJohn Ericson 
646e181e234SJohn Ericson FDESCFS_TC(devfd_mode_nodup_rdlnk);
ATF_TC_BODY(devfd_mode_nodup_rdlnk,tc)647e181e234SJohn Ericson ATF_TC_BODY(devfd_mode_nodup_rdlnk, tc)
648e181e234SJohn Ericson {
649e181e234SJohn Ericson 	static const char * const opts[] = { "nodup", "rdlnk", NULL };
650e181e234SJohn Ericson 
651e181e234SJohn Ericson 	devfd_mode(opts, ENOENT);
652e181e234SJohn Ericson }
653e181e234SJohn Ericson 
654e181e234SJohn Ericson /* An empty path is only meaningful with a real descriptor. */
655e181e234SJohn Ericson ATF_TC_WITHOUT_HEAD(empty_path_at_fdcwd);
ATF_TC_BODY(empty_path_at_fdcwd,tc)656e181e234SJohn Ericson ATF_TC_BODY(empty_path_at_fdcwd, tc)
657e181e234SJohn Ericson {
658e181e234SJohn Ericson 	int s;
659e181e234SJohn Ericson 
660e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
661e181e234SJohn Ericson 	ATF_REQUIRE_ERRNO(EINVAL, connect(s,
662e181e234SJohn Ericson 	    (const struct sockaddr *)&empty_sun, empty_sun.sun_len) == -1);
663e181e234SJohn Ericson 	ATF_REQUIRE_ERRNO(EINVAL, fdconnect(AT_FDCWD, s) == -1);
664e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
665e181e234SJohn Ericson }
666e181e234SJohn Ericson 
667*3baada83SDevin Teske /*
668*3baada83SDevin Teske  * A NUL-leading path with a nonzero length is not the empty-path
669*3baada83SDevin Teske  * extension: connect(2) and connectat(2) with AT_FDCWD must perform a
670*3baada83SDevin Teske  * pathname lookup and fail with ENOENT, not treat AT_FDCWD as a peer
671*3baada83SDevin Teske  * descriptor and fail with EBADF.
672*3baada83SDevin Teske  *
673*3baada83SDevin Teske  * Such addresses occur in the wild: they name Linux abstract namespace
674*3baada83SDevin Teske  * sockets, and the linuxulator passes them through with the leading NUL
675*3baada83SDevin Teske  * intact.  libxcb tries the abstract X11 socket first and falls back to
676*3baada83SDevin Teske  * the pathname socket only on ENOENT or ECONNREFUSED, so when connect(2)
677*3baada83SDevin Teske  * briefly returned EBADF here, every Linux X11 client on the linuxulator
678*3baada83SDevin Teske  * failed at startup with "Missing X server or $DISPLAY".
679*3baada83SDevin Teske  */
680*3baada83SDevin Teske ATF_TC_WITHOUT_HEAD(nul_path_at_fdcwd);
ATF_TC_BODY(nul_path_at_fdcwd,tc)681*3baada83SDevin Teske ATF_TC_BODY(nul_path_at_fdcwd, tc)
682*3baada83SDevin Teske {
683*3baada83SDevin Teske 	int s;
684*3baada83SDevin Teske 
685*3baada83SDevin Teske 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
686*3baada83SDevin Teske 	ATF_REQUIRE_ERRNO(ENOENT, connect(s,
687*3baada83SDevin Teske 	    (const struct sockaddr *)&nul_sun, nul_sun.sun_len) == -1);
688*3baada83SDevin Teske 	ATF_REQUIRE_ERRNO(ENOENT, connectat(AT_FDCWD, s,
689*3baada83SDevin Teske 	    (const struct sockaddr *)&nul_sun, nul_sun.sun_len) == -1);
690*3baada83SDevin Teske 	ATF_REQUIRE_EQ(0, close(s));
691*3baada83SDevin Teske }
692*3baada83SDevin Teske 
693e181e234SJohn Ericson /* Error matrix for unsuitable descriptors and peers. */
694e181e234SJohn Ericson ATF_TC_WITHOUT_HEAD(bad_peers);
ATF_TC_BODY(bad_peers,tc)695e181e234SJohn Ericson ATF_TC_BODY(bad_peers, tc)
696e181e234SJohn Ericson {
697e181e234SJohn Ericson 	int s, d, fd;
698e181e234SJohn Ericson 
699e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
700e181e234SJohn Ericson 	ATF_REQUIRE((d = socket(PF_UNIX, SOCK_DGRAM, 0)) >= 0);
701e181e234SJohn Ericson 
702e181e234SJohn Ericson 	/* Non-socket descriptor. */
703e181e234SJohn Ericson 	ATF_REQUIRE((fd = open(".", O_RDONLY)) >= 0);
704e181e234SJohn Ericson 	ATF_REQUIRE_ERRNO(ENOTSOCK, fdconnect(fd, s) == -1);
705e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(fd));
706e181e234SJohn Ericson 
707e181e234SJohn Ericson 	/* Socket from another domain. */
708e181e234SJohn Ericson 	ATF_REQUIRE((fd = socket(PF_INET, SOCK_STREAM, 0)) >= 0);
709e181e234SJohn Ericson 	ATF_REQUIRE_ERRNO(EPROTOTYPE, fdconnect(fd, s) == -1);
710e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(fd));
711e181e234SJohn Ericson 
712e181e234SJohn Ericson 	/* Type mismatch between the two unix sockets. */
713e181e234SJohn Ericson 	fd = mklistener("mismatch.sock");
714e181e234SJohn Ericson 	ATF_REQUIRE_ERRNO(EPROTOTYPE, fdconnect(fd, d) == -1);
715e181e234SJohn Ericson 
716e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(fd));
717e181e234SJohn Ericson 
718e181e234SJohn Ericson 	/* Stream peer that is not listening: 's' never called listen(2). */
719e181e234SJohn Ericson 	ATF_REQUIRE((fd = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
720e181e234SJohn Ericson 	ATF_REQUIRE_ERRNO(ECONNREFUSED, fdconnect(s, fd) == -1);
721e181e234SJohn Ericson 
722e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(fd));
723e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(d));
724e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
725e181e234SJohn Ericson }
726e181e234SJohn Ericson 
727e181e234SJohn Ericson /*
728e181e234SJohn Ericson  * A descriptor limited to CAP_CONNECTAT is a pure connect-to-me token:
729e181e234SJohn Ericson  * it can be connected to, but not listened on, accepted from, or read.
730e181e234SJohn Ericson  */
731e181e234SJohn Ericson ATF_TC_WITHOUT_HEAD(cap_connectat);
ATF_TC_BODY(cap_connectat,tc)732e181e234SJohn Ericson ATF_TC_BODY(cap_connectat, tc)
733e181e234SJohn Ericson {
734e181e234SJohn Ericson 	cap_rights_t rights;
735e181e234SJohn Ericson 	char buf[8];
736e181e234SJohn Ericson 	int l, s, token, a;
737e181e234SJohn Ericson 
738e181e234SJohn Ericson 	l = mklistener("cap.sock");
739e181e234SJohn Ericson 	ATF_REQUIRE((token = dup(l)) >= 0);
740e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, cap_rights_limit(token,
741e181e234SJohn Ericson 	    cap_rights_init(&rights, CAP_CONNECTAT)));
742e181e234SJohn Ericson 
743e181e234SJohn Ericson 	ATF_REQUIRE_ERRNO(ENOTCAPABLE, listen(token, 1) == -1);
744e181e234SJohn Ericson 	ATF_REQUIRE_ERRNO(ENOTCAPABLE, accept(token, NULL, NULL) == -1);
745e181e234SJohn Ericson 	ATF_REQUIRE_ERRNO(ENOTCAPABLE, read(token, buf, sizeof(buf)) == -1);
746e181e234SJohn Ericson 
747e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
748e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, fdconnect(token, s));
749e181e234SJohn Ericson 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
750e181e234SJohn Ericson 
751e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(a));
752e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
753e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(token));
754e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
755e181e234SJohn Ericson }
756e181e234SJohn Ericson 
757e181e234SJohn Ericson /* Without CAP_CONNECTAT, the descriptor cannot be a connect target. */
758e181e234SJohn Ericson ATF_TC_WITHOUT_HEAD(cap_connectat_denied);
ATF_TC_BODY(cap_connectat_denied,tc)759e181e234SJohn Ericson ATF_TC_BODY(cap_connectat_denied, tc)
760e181e234SJohn Ericson {
761e181e234SJohn Ericson 	cap_rights_t rights;
762e181e234SJohn Ericson 	int l, s, token;
763e181e234SJohn Ericson 
764e181e234SJohn Ericson 	l = mklistener("capdeny.sock");
765e181e234SJohn Ericson 	ATF_REQUIRE((token = dup(l)) >= 0);
766e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, cap_rights_limit(token,
767e181e234SJohn Ericson 	    cap_rights_init(&rights, CAP_READ, CAP_WRITE)));
768e181e234SJohn Ericson 
769e181e234SJohn Ericson 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
770e181e234SJohn Ericson 	ATF_REQUIRE_ERRNO(ENOTCAPABLE, fdconnect(token, s) == -1);
771e181e234SJohn Ericson 
772e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(s));
773e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(token));
774e181e234SJohn Ericson 	ATF_REQUIRE_EQ(0, close(l));
775e181e234SJohn Ericson }
776e181e234SJohn Ericson 
ATF_TP_ADD_TCS(tp)777e181e234SJohn Ericson ATF_TP_ADD_TCS(tp)
778e181e234SJohn Ericson {
779e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, stream);
780e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, stream_bound);
781ce3750b5SJohn Ericson 	ATF_TP_ADD_TC(tp, listen_unbound);
782ce3750b5SJohn Ericson 	ATF_TP_ADD_TC(tp, bind_after_listen);
783ce3750b5SJohn Ericson 	ATF_TP_ADD_TC(tp, listen_after_disconnect);
784e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, dgram);
785e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, empty_path_vnode);
786e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, path);
787e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, devfd);
788e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, devfd_relative);
789e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, empty_path_devfd);
790e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, devfd_indirect);
791e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, devfd_indirect_nodup);
792e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, devfd_mode_plain);
793e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, devfd_mode_nodup);
794e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, devfd_mode_linrdlnk);
795e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, devfd_mode_nodup_linrdlnk);
796e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, devfd_mode_rdlnk);
797e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, devfd_mode_nodup_rdlnk);
798e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, empty_path_at_fdcwd);
799*3baada83SDevin Teske 	ATF_TP_ADD_TC(tp, nul_path_at_fdcwd);
800e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, bad_peers);
801e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, cap_connectat);
802e181e234SJohn Ericson 	ATF_TP_ADD_TC(tp, cap_connectat_denied);
803e181e234SJohn Ericson 
804e181e234SJohn Ericson 	return (atf_no_error());
805e181e234SJohn Ericson }
806