1152251Srwatson/*-
2152251Srwatson * Copyright (c) 2005 Robert N. M. Watson
3152251Srwatson * All rights reserved.
4152251Srwatson *
5152251Srwatson * Redistribution and use in source and binary forms, with or without
6152251Srwatson * modification, are permitted provided that the following conditions
7152251Srwatson * are met:
8152251Srwatson * 1. Redistributions of source code must retain the above copyright
9152251Srwatson *    notice, this list of conditions and the following disclaimer.
10152251Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11152251Srwatson *    notice, this list of conditions and the following disclaimer in the
12152251Srwatson *    documentation and/or other materials provided with the distribution.
13152251Srwatson *
14152251Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15152251Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16152251Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17152251Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18152251Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19152251Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20152251Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21152251Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22152251Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23152251Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24152251Srwatson * SUCH DAMAGE.
25152251Srwatson *
26152251Srwatson * $FreeBSD: releng/10.2/tools/regression/sockets/unix_passfd/unix_passfd.c 281974 2015-04-25 05:31:52Z ngie $
27152251Srwatson */
28152251Srwatson
29152251Srwatson#include <sys/types.h>
30152251Srwatson#include <sys/socket.h>
31152251Srwatson#include <sys/stat.h>
32281974Sngie#include <sys/sysctl.h>
33281974Sngie#include <sys/un.h>
34152251Srwatson
35152251Srwatson#include <err.h>
36228371Sjhb#include <fcntl.h>
37152251Srwatson#include <limits.h>
38152251Srwatson#include <stdio.h>
39281974Sngie#include <stdlib.h>
40152251Srwatson#include <string.h>
41152251Srwatson#include <unistd.h>
42152251Srwatson
43152251Srwatson/*
44152251Srwatson * UNIX domain sockets allow file descriptors to be passed via "ancillary
45152251Srwatson * data", or control messages.  This regression test is intended to exercise
46152251Srwatson * this facility, both performing some basic tests that it operates, and also
47152251Srwatson * causing some kernel edge cases to execute, such as garbage collection when
48152251Srwatson * there are cyclic file descriptor references.  Right now we test only with
49152251Srwatson * stream sockets, but ideally we'd also test with datagram sockets.
50152251Srwatson */
51152251Srwatson
52152251Srwatsonstatic void
53152251Srwatsondomainsocketpair(const char *test, int *fdp)
54152251Srwatson{
55152251Srwatson
56152251Srwatson	if (socketpair(PF_UNIX, SOCK_STREAM, 0, fdp) < 0)
57152251Srwatson		err(-1, "%s: socketpair(PF_UNIX, SOCK_STREAM)", test);
58152251Srwatson}
59152251Srwatson
60152251Srwatsonstatic void
61152251Srwatsonclosesocketpair(int *fdp)
62152251Srwatson{
63152251Srwatson
64152251Srwatson	close(fdp[0]);
65152251Srwatson	close(fdp[1]);
66152251Srwatson}
67152251Srwatson
68152251Srwatsonstatic void
69228371Sjhbdevnull(const char *test, int *fdp)
70228371Sjhb{
71228371Sjhb	int fd;
72228371Sjhb
73228371Sjhb	fd = open("/dev/null", O_RDONLY);
74228371Sjhb	if (fd < 0)
75228371Sjhb		err(-1, "%s: open(/dev/null)", test);
76228371Sjhb	*fdp = fd;
77228371Sjhb}
78228371Sjhb
79228371Sjhbstatic void
80152251Srwatsontempfile(const char *test, int *fdp)
81152251Srwatson{
82152251Srwatson	char path[PATH_MAX];
83152251Srwatson	int fd;
84152251Srwatson
85152251Srwatson	snprintf(path, PATH_MAX, "/tmp/unix_passfd.XXXXXXXXXXXXXXX");
86152251Srwatson	fd = mkstemp(path);
87152251Srwatson	if (fd < 0)
88152251Srwatson		err(-1, "%s: mkstemp(%s)", test, path);
89152251Srwatson	(void)unlink(path);
90152251Srwatson	*fdp = fd;
91152251Srwatson}
92152251Srwatson
93152251Srwatsonstatic void
94152251Srwatsondofstat(const char *test, int fd, struct stat *sb)
95152251Srwatson{
96152251Srwatson
97152251Srwatson	if (fstat(fd, sb) < 0)
98152251Srwatson		err(-1, "%s: fstat", test);
99152251Srwatson}
100152251Srwatson
101152251Srwatsonstatic void
102152251Srwatsonsamefile(const char *test, struct stat *sb1, struct stat *sb2)
103152251Srwatson{
104152251Srwatson
105152251Srwatson	if (sb1->st_dev != sb2->st_dev)
106228371Sjhb		errx(-1, "%s: samefile: different device", test);
107152251Srwatson	if (sb1->st_ino != sb2->st_ino)
108228371Sjhb		errx(-1, "%s: samefile: different inode", test);
109152251Srwatson}
110152251Srwatson
111152251Srwatsonstatic void
112281974Sngiesendfd_payload(const char *test, int sockfd, int sendfd,
113281974Sngie    void *payload, size_t paylen)
114152251Srwatson{
115152251Srwatson	struct iovec iovec;
116228371Sjhb	char message[CMSG_SPACE(sizeof(int))];
117228371Sjhb	struct cmsghdr *cmsghdr;
118152251Srwatson	struct msghdr msghdr;
119152251Srwatson	ssize_t len;
120152251Srwatson
121152251Srwatson	bzero(&msghdr, sizeof(msghdr));
122152251Srwatson	bzero(&message, sizeof(message));
123152251Srwatson
124228371Sjhb	msghdr.msg_control = message;
125152251Srwatson	msghdr.msg_controllen = sizeof(message);
126152251Srwatson
127281974Sngie	iovec.iov_base = payload;
128281974Sngie	iovec.iov_len = paylen;
129152251Srwatson
130152251Srwatson	msghdr.msg_iov = &iovec;
131152251Srwatson	msghdr.msg_iovlen = 1;
132152251Srwatson
133228371Sjhb	cmsghdr = (struct cmsghdr *)message;
134228371Sjhb	cmsghdr->cmsg_len = CMSG_LEN(sizeof(int));
135228371Sjhb	cmsghdr->cmsg_level = SOL_SOCKET;
136228371Sjhb	cmsghdr->cmsg_type = SCM_RIGHTS;
137228371Sjhb	*(int *)CMSG_DATA(cmsghdr) = sendfd;
138152251Srwatson
139152251Srwatson	len = sendmsg(sockfd, &msghdr, 0);
140152251Srwatson	if (len < 0)
141152251Srwatson		err(-1, "%s: sendmsg", test);
142281974Sngie	if ((size_t)len != paylen)
143228371Sjhb		errx(-1, "%s: sendmsg: %zd bytes sent", test, len);
144152251Srwatson}
145152251Srwatson
146152251Srwatsonstatic void
147281974Sngiesendfd(const char *test, int sockfd, int sendfd)
148152251Srwatson{
149281974Sngie	char ch;
150281974Sngie
151281974Sngie	return (sendfd_payload(test, sockfd, sendfd, &ch, sizeof(ch)));
152281974Sngie}
153281974Sngie
154281974Sngiestatic void
155281974Sngierecvfd_payload(const char *test, int sockfd, int *recvfd,
156281974Sngie    void *buf, size_t buflen)
157281974Sngie{
158228371Sjhb	struct cmsghdr *cmsghdr;
159281974Sngie	char message[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + sizeof(int)];
160152251Srwatson	struct msghdr msghdr;
161152251Srwatson	struct iovec iovec;
162152251Srwatson	ssize_t len;
163152251Srwatson
164152251Srwatson	bzero(&msghdr, sizeof(msghdr));
165152251Srwatson
166228371Sjhb	msghdr.msg_control = message;
167152251Srwatson	msghdr.msg_controllen = sizeof(message);
168152251Srwatson
169281974Sngie	iovec.iov_base = buf;
170281974Sngie	iovec.iov_len = buflen;
171152251Srwatson
172152251Srwatson	msghdr.msg_iov = &iovec;
173152251Srwatson	msghdr.msg_iovlen = 1;
174152251Srwatson
175152251Srwatson	len = recvmsg(sockfd, &msghdr, 0);
176152251Srwatson	if (len < 0)
177152251Srwatson		err(-1, "%s: recvmsg", test);
178281974Sngie	if ((size_t)len != buflen)
179228371Sjhb		errx(-1, "%s: recvmsg: %zd bytes received", test, len);
180281974Sngie
181228371Sjhb	cmsghdr = CMSG_FIRSTHDR(&msghdr);
182228371Sjhb	if (cmsghdr == NULL)
183228371Sjhb		errx(-1, "%s: recvmsg: did not receive control message", test);
184281974Sngie	*recvfd = -1;
185281974Sngie	for (; cmsghdr != NULL; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) {
186281974Sngie		if (cmsghdr->cmsg_level == SOL_SOCKET &&
187281974Sngie		    cmsghdr->cmsg_type == SCM_RIGHTS &&
188281974Sngie		    cmsghdr->cmsg_len == CMSG_LEN(sizeof(int))) {
189281974Sngie			*recvfd = *(int *)CMSG_DATA(cmsghdr);
190281974Sngie			if (*recvfd == -1)
191281974Sngie				errx(-1, "%s: recvmsg: received fd -1", test);
192281974Sngie		}
193281974Sngie	}
194281974Sngie	if (*recvfd == -1)
195228371Sjhb		errx(-1, "%s: recvmsg: did not receive single-fd message",
196228371Sjhb		    test);
197152251Srwatson}
198152251Srwatson
199281974Sngiestatic void
200281974Sngierecvfd(const char *test, int sockfd, int *recvfd)
201281974Sngie{
202281974Sngie	char ch;
203281974Sngie
204281974Sngie	return (recvfd_payload(test, sockfd, recvfd, &ch, sizeof(ch)));
205281974Sngie}
206281974Sngie
207152251Srwatsonint
208281974Sngiemain(void)
209152251Srwatson{
210152251Srwatson	struct stat putfd_1_stat, putfd_2_stat, getfd_1_stat, getfd_2_stat;
211152251Srwatson	int fd[2], putfd_1, putfd_2, getfd_1, getfd_2;
212152251Srwatson	const char *test;
213152251Srwatson
214152251Srwatson	/*
215152251Srwatson	 * First test: put a temporary file into a UNIX domain socket, then
216152251Srwatson	 * take it out and make sure it's the same file.  First time around,
217152251Srwatson	 * don't close the reference after sending.
218152251Srwatson	 */
219152251Srwatson	test = "test1-simplesendfd";
220152251Srwatson	printf("beginning %s\n", test);
221152251Srwatson
222152251Srwatson	domainsocketpair(test, fd);
223152251Srwatson	tempfile(test, &putfd_1);
224152251Srwatson	dofstat(test, putfd_1, &putfd_1_stat);
225152251Srwatson	sendfd(test, fd[0], putfd_1);
226152251Srwatson	recvfd(test, fd[1], &getfd_1);
227152251Srwatson	dofstat(test, getfd_1, &getfd_1_stat);
228152251Srwatson	samefile(test, &putfd_1_stat, &getfd_1_stat);
229152251Srwatson	close(putfd_1);
230152251Srwatson	close(getfd_1);
231152251Srwatson	closesocketpair(fd);
232152251Srwatson
233152251Srwatson	printf("%s passed\n", test);
234152251Srwatson
235152251Srwatson	/*
236152251Srwatson	 * Second test: same as first, only close the file reference after
237152251Srwatson	 * sending, so that the only reference is the descriptor in the UNIX
238152251Srwatson	 * domain socket buffer.
239152251Srwatson	 */
240152251Srwatson	test = "test2-sendandclose";
241152251Srwatson	printf("beginning %s\n", test);
242152251Srwatson
243152251Srwatson	domainsocketpair(test, fd);
244152251Srwatson	tempfile(test, &putfd_1);
245152251Srwatson	dofstat(test, putfd_1, &putfd_1_stat);
246152251Srwatson	sendfd(test, fd[0], putfd_1);
247152251Srwatson	close(putfd_1);
248152251Srwatson	recvfd(test, fd[1], &getfd_1);
249152251Srwatson	dofstat(test, getfd_1, &getfd_1_stat);
250152251Srwatson	samefile(test, &putfd_1_stat, &getfd_1_stat);
251152251Srwatson	close(getfd_1);
252152251Srwatson	closesocketpair(fd);
253152251Srwatson
254152251Srwatson	printf("%s passed\n", test);
255152251Srwatson
256152251Srwatson	/*
257152251Srwatson	 * Third test: put a temporary file into a UNIX domain socket, then
258152251Srwatson	 * close both endpoints causing garbage collection to kick off.
259152251Srwatson	 */
260152251Srwatson	test = "test3-sendandcancel";
261152251Srwatson	printf("beginning %s\n", test);
262152251Srwatson
263152251Srwatson	domainsocketpair(test, fd);
264152251Srwatson	tempfile(test, &putfd_1);
265152251Srwatson	sendfd(test, fd[0], putfd_1);
266152251Srwatson	close(putfd_1);
267152251Srwatson	closesocketpair(fd);
268152251Srwatson
269152251Srwatson	printf("%s passed\n", test);
270152251Srwatson
271152251Srwatson	/*
272152251Srwatson	 * Send two files.  Then receive them.  Make sure they are returned
273152251Srwatson	 * in the right order, and both get there.
274152251Srwatson	 */
275152251Srwatson
276152251Srwatson	test = "test4-twofile";
277152251Srwatson	printf("beginning %s\n", test);
278152251Srwatson
279152251Srwatson	domainsocketpair(test, fd);
280152251Srwatson	tempfile(test, &putfd_1);
281152251Srwatson	tempfile(test, &putfd_2);
282152251Srwatson	dofstat(test, putfd_1, &putfd_1_stat);
283152251Srwatson	dofstat(test, putfd_2, &putfd_2_stat);
284152251Srwatson	sendfd(test, fd[0], putfd_1);
285152251Srwatson	sendfd(test, fd[0], putfd_2);
286152251Srwatson	close(putfd_1);
287152251Srwatson	close(putfd_2);
288152251Srwatson	recvfd(test, fd[1], &getfd_1);
289152251Srwatson	recvfd(test, fd[1], &getfd_2);
290152251Srwatson	dofstat(test, getfd_1, &getfd_1_stat);
291152251Srwatson	dofstat(test, getfd_2, &getfd_2_stat);
292152251Srwatson	samefile(test, &putfd_1_stat, &getfd_1_stat);
293152251Srwatson	samefile(test, &putfd_2_stat, &getfd_2_stat);
294152251Srwatson	close(getfd_1);
295152251Srwatson	close(getfd_2);
296152251Srwatson	closesocketpair(fd);
297152251Srwatson
298152251Srwatson	printf("%s passed\n", test);
299152251Srwatson
300152251Srwatson	/*
301152251Srwatson	 * Big bundling test.  Send an endpoint of the UNIX domain socket
302152251Srwatson	 * over itself, closing the door behind it.
303152251Srwatson	 */
304152251Srwatson
305152251Srwatson	test = "test5-bundle";
306152251Srwatson	printf("beginning %s\n", test);
307152251Srwatson
308152251Srwatson	domainsocketpair(test, fd);
309152251Srwatson
310152251Srwatson	sendfd(test, fd[0], fd[0]);
311152251Srwatson	close(fd[0]);
312152251Srwatson	recvfd(test, fd[1], &getfd_1);
313152251Srwatson	close(getfd_1);
314152251Srwatson	close(fd[1]);
315152251Srwatson
316152251Srwatson	printf("%s passed\n", test);
317152251Srwatson
318152251Srwatson	/*
319152251Srwatson	 * Big bundling test part two: Send an endpoint of the UNIX domain
320152251Srwatson	 * socket over itself, close the door behind it, and never remove it
321152251Srwatson	 * from the other end.
322152251Srwatson	 */
323152251Srwatson
324152251Srwatson	test = "test6-bundlecancel";
325152251Srwatson	printf("beginning %s\n", test);
326152251Srwatson
327152251Srwatson	domainsocketpair(test, fd);
328152251Srwatson	sendfd(test, fd[0], fd[0]);
329152251Srwatson	sendfd(test, fd[1], fd[0]);
330152251Srwatson	closesocketpair(fd);
331152251Srwatson
332152251Srwatson	printf("%s passed\n", test);
333152251Srwatson
334228371Sjhb	/*
335228371Sjhb	 * Test for PR 151758: Send an character device over the UNIX
336228371Sjhb	 * domain socket and then close both sockets to orphan the
337228371Sjhb	 * device.
338228371Sjhb	 */
339228371Sjhb
340228371Sjhb	test = "test7-devfsorphan";
341228371Sjhb	printf("beginning %s\n", test);
342228371Sjhb
343228371Sjhb	domainsocketpair(test, fd);
344228371Sjhb	devnull(test, &putfd_1);
345228371Sjhb	sendfd(test, fd[0], putfd_1);
346228371Sjhb	close(putfd_1);
347228371Sjhb	closesocketpair(fd);
348228371Sjhb
349228371Sjhb	printf("%s passed\n", test);
350281974Sngie
351281974Sngie	/*
352281974Sngie	 * Test for PR 181741. Receiver sets LOCAL_CREDS, and kernel
353281974Sngie	 * prepends a control message to the data. Sender sends large
354281974Sngie	 * payload. Payload + SCM_RIGHTS + LOCAL_CREDS hit socket buffer
355281974Sngie	 * limit, and receiver receives truncated data.
356281974Sngie	 */
357281974Sngie	test = "test8-rights+creds+payload";
358281974Sngie	printf("beginning %s\n", test);
359281974Sngie
360281974Sngie	{
361281974Sngie		const int on = 1;
362281974Sngie		u_long sendspace;
363281974Sngie		size_t len;
364281974Sngie		void *buf;
365281974Sngie
366281974Sngie		len = sizeof(sendspace);
367281974Sngie		if (sysctlbyname("net.local.stream.sendspace", &sendspace,
368281974Sngie		    &len, NULL, 0) < 0)
369281974Sngie			err(-1, "%s: sysctlbyname(net.local.stream.sendspace)",
370281974Sngie			    test);
371281974Sngie
372281974Sngie		if ((buf = malloc(sendspace)) == NULL)
373281974Sngie			err(-1, "%s: malloc", test);
374281974Sngie
375281974Sngie		domainsocketpair(test, fd);
376281974Sngie		if (setsockopt(fd[1], 0, LOCAL_CREDS, &on, sizeof(on)) < 0)
377281974Sngie			err(-1, "%s: setsockopt(LOCAL_CREDS)", test);
378281974Sngie		tempfile(test, &putfd_1);
379281974Sngie		sendfd_payload(test, fd[0], putfd_1, buf, sendspace);
380281974Sngie		recvfd_payload(test, fd[1], &getfd_1, buf, sendspace);
381281974Sngie		close(putfd_1);
382281974Sngie		close(getfd_1);
383281974Sngie		closesocketpair(fd);
384281974Sngie	}
385281974Sngie
386281974Sngie	printf("%s passed\n", test);
387228371Sjhb
388152251Srwatson	return (0);
389152251Srwatson}
390