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$
27152251Srwatson */
28152251Srwatson
29152251Srwatson#include <sys/types.h>
30152251Srwatson#include <sys/socket.h>
31152251Srwatson#include <sys/stat.h>
32152251Srwatson
33152251Srwatson#include <err.h>
34152251Srwatson#include <limits.h>
35152251Srwatson#include <stdio.h>
36152251Srwatson#include <string.h>
37152251Srwatson#include <unistd.h>
38152251Srwatson
39152251Srwatson/*
40152251Srwatson * UNIX domain sockets allow file descriptors to be passed via "ancillary
41152251Srwatson * data", or control messages.  This regression test is intended to exercise
42152251Srwatson * this facility, both performing some basic tests that it operates, and also
43152251Srwatson * causing some kernel edge cases to execute, such as garbage collection when
44152251Srwatson * there are cyclic file descriptor references.  Right now we test only with
45152251Srwatson * stream sockets, but ideally we'd also test with datagram sockets.
46152251Srwatson */
47152251Srwatson
48152251Srwatsonstatic void
49152251Srwatsondomainsocketpair(const char *test, int *fdp)
50152251Srwatson{
51152251Srwatson
52152251Srwatson	if (socketpair(PF_UNIX, SOCK_STREAM, 0, fdp) < 0)
53152251Srwatson		err(-1, "%s: socketpair(PF_UNIX, SOCK_STREAM)", test);
54152251Srwatson}
55152251Srwatson
56152251Srwatsonstatic void
57152251Srwatsonclosesocketpair(int *fdp)
58152251Srwatson{
59152251Srwatson
60152251Srwatson	close(fdp[0]);
61152251Srwatson	close(fdp[1]);
62152251Srwatson}
63152251Srwatson
64152251Srwatsonstatic void
65152251Srwatsontempfile(const char *test, int *fdp)
66152251Srwatson{
67152251Srwatson	char path[PATH_MAX];
68152251Srwatson	int fd;
69152251Srwatson
70152251Srwatson	snprintf(path, PATH_MAX, "/tmp/unix_passfd.XXXXXXXXXXXXXXX");
71152251Srwatson	fd = mkstemp(path);
72152251Srwatson	if (fd < 0)
73152251Srwatson		err(-1, "%s: mkstemp(%s)", test, path);
74152251Srwatson	(void)unlink(path);
75152251Srwatson	*fdp = fd;
76152251Srwatson}
77152251Srwatson
78152251Srwatsonstatic void
79152251Srwatsondofstat(const char *test, int fd, struct stat *sb)
80152251Srwatson{
81152251Srwatson
82152251Srwatson	if (fstat(fd, sb) < 0)
83152251Srwatson		err(-1, "%s: fstat", test);
84152251Srwatson}
85152251Srwatson
86152251Srwatsonstatic void
87152251Srwatsonsamefile(const char *test, struct stat *sb1, struct stat *sb2)
88152251Srwatson{
89152251Srwatson
90152251Srwatson	if (sb1->st_dev != sb2->st_dev)
91152251Srwatson		err(-1, "%s: samefile: different device", test);
92152251Srwatson	if (sb1->st_ino != sb2->st_ino)
93152251Srwatson		err(-1, "%s: samefile: different inode", test);
94152251Srwatson}
95152251Srwatson
96152251Srwatsonstatic void
97152251Srwatsonsendfd(const char *test, int sockfd, int sendfd)
98152251Srwatson{
99152251Srwatson	struct iovec iovec;
100152251Srwatson	char ch;
101152251Srwatson
102152251Srwatson	struct {
103152251Srwatson		struct cmsghdr cmsghdr;
104152251Srwatson		int fd;
105152251Srwatson	} message;
106152251Srwatson	struct msghdr msghdr;
107152251Srwatson	ssize_t len;
108152251Srwatson
109152251Srwatson	bzero(&msghdr, sizeof(msghdr));
110152251Srwatson	bzero(&message, sizeof(message));
111152251Srwatson	ch = 0;
112152251Srwatson
113152251Srwatson	msghdr.msg_control = &message;
114152251Srwatson	msghdr.msg_controllen = sizeof(message);
115152251Srwatson
116152251Srwatson	iovec.iov_base = &ch;
117152251Srwatson	iovec.iov_len = sizeof(ch);
118152251Srwatson
119152251Srwatson	msghdr.msg_iov = &iovec;
120152251Srwatson	msghdr.msg_iovlen = 1;
121152251Srwatson
122152251Srwatson	message.cmsghdr.cmsg_len = sizeof(message);
123152251Srwatson	message.cmsghdr.cmsg_level = SOL_SOCKET;
124152251Srwatson	message.cmsghdr.cmsg_type = SCM_RIGHTS;
125152251Srwatson	message.fd = sendfd;
126152251Srwatson
127152251Srwatson	len = sendmsg(sockfd, &msghdr, 0);
128152251Srwatson	if (len < 0)
129152251Srwatson		err(-1, "%s: sendmsg", test);
130152251Srwatson	if (len != sizeof(ch))
131152251Srwatson		errx(-1, "%s: sendmsg: %d bytes sent", test, len);
132152251Srwatson}
133152251Srwatson
134152251Srwatsonstatic void
135152251Srwatsonrecvfd(const char *test, int sockfd, int *recvfd)
136152251Srwatson{
137152251Srwatson	struct {
138152251Srwatson		struct cmsghdr cmsghdr;
139152251Srwatson		int fd;
140152251Srwatson	} message;
141152251Srwatson	struct msghdr msghdr;
142152251Srwatson	struct iovec iovec;
143152251Srwatson	ssize_t len;
144152251Srwatson	char ch;
145152251Srwatson
146152251Srwatson	bzero(&msghdr, sizeof(msghdr));
147152251Srwatson	bzero(&message, sizeof(message));
148152251Srwatson	ch = 0;
149152251Srwatson
150152251Srwatson	msghdr.msg_control = &message;
151152251Srwatson	msghdr.msg_controllen = sizeof(message);
152152251Srwatson
153152251Srwatson	iovec.iov_base = &ch;
154152251Srwatson	iovec.iov_len = sizeof(ch);
155152251Srwatson
156152251Srwatson	msghdr.msg_iov = &iovec;
157152251Srwatson	msghdr.msg_iovlen = 1;
158152251Srwatson
159152251Srwatson	iovec.iov_len = sizeof(ch);
160152251Srwatson
161152251Srwatson	msghdr.msg_iov = &iovec;
162152251Srwatson	msghdr.msg_iovlen = 1;
163152251Srwatson
164152251Srwatson	message.cmsghdr.cmsg_len = sizeof(message);
165152251Srwatson	message.cmsghdr.cmsg_level = SOL_SOCKET;
166152251Srwatson	message.cmsghdr.cmsg_type = SCM_RIGHTS;
167152251Srwatson	message.fd = -1;
168152251Srwatson
169152251Srwatson	len = recvmsg(sockfd, &msghdr, 0);
170152251Srwatson	if (len < 0)
171152251Srwatson		err(-1, "%s: recvmsg", test);
172152251Srwatson	if (len != sizeof(ch))
173152251Srwatson		errx(-1, "%s: recvmsg: %d bytes received", test, len);
174152251Srwatson	if (message.fd == -1)
175152251Srwatson		errx(-1, "%s: recvmsg: received fd -1", test);
176152251Srwatson	*recvfd = message.fd;
177152251Srwatson}
178152251Srwatson
179152251Srwatsonint
180152251Srwatsonmain(int argc, char *argv[])
181152251Srwatson{
182152251Srwatson	struct stat putfd_1_stat, putfd_2_stat, getfd_1_stat, getfd_2_stat;
183152251Srwatson	int fd[2], putfd_1, putfd_2, getfd_1, getfd_2;
184152251Srwatson	const char *test;
185152251Srwatson
186152251Srwatson	/*
187152251Srwatson	 * First test: put a temporary file into a UNIX domain socket, then
188152251Srwatson	 * take it out and make sure it's the same file.  First time around,
189152251Srwatson	 * don't close the reference after sending.
190152251Srwatson	 */
191152251Srwatson	test = "test1-simplesendfd";
192152251Srwatson	printf("beginning %s\n", test);
193152251Srwatson
194152251Srwatson	domainsocketpair(test, fd);
195152251Srwatson	tempfile(test, &putfd_1);
196152251Srwatson	dofstat(test, putfd_1, &putfd_1_stat);
197152251Srwatson	sendfd(test, fd[0], putfd_1);
198152251Srwatson	recvfd(test, fd[1], &getfd_1);
199152251Srwatson	dofstat(test, getfd_1, &getfd_1_stat);
200152251Srwatson	samefile(test, &putfd_1_stat, &getfd_1_stat);
201152251Srwatson	close(putfd_1);
202152251Srwatson	close(getfd_1);
203152251Srwatson	closesocketpair(fd);
204152251Srwatson
205152251Srwatson	printf("%s passed\n", test);
206152251Srwatson
207152251Srwatson	/*
208152251Srwatson	 * Second test: same as first, only close the file reference after
209152251Srwatson	 * sending, so that the only reference is the descriptor in the UNIX
210152251Srwatson	 * domain socket buffer.
211152251Srwatson	 */
212152251Srwatson	test = "test2-sendandclose";
213152251Srwatson	printf("beginning %s\n", test);
214152251Srwatson
215152251Srwatson	domainsocketpair(test, fd);
216152251Srwatson	tempfile(test, &putfd_1);
217152251Srwatson	dofstat(test, putfd_1, &putfd_1_stat);
218152251Srwatson	sendfd(test, fd[0], putfd_1);
219152251Srwatson	close(putfd_1);
220152251Srwatson	recvfd(test, fd[1], &getfd_1);
221152251Srwatson	dofstat(test, getfd_1, &getfd_1_stat);
222152251Srwatson	samefile(test, &putfd_1_stat, &getfd_1_stat);
223152251Srwatson	close(getfd_1);
224152251Srwatson	closesocketpair(fd);
225152251Srwatson
226152251Srwatson	printf("%s passed\n", test);
227152251Srwatson
228152251Srwatson	/*
229152251Srwatson	 * Third test: put a temporary file into a UNIX domain socket, then
230152251Srwatson	 * close both endpoints causing garbage collection to kick off.
231152251Srwatson	 */
232152251Srwatson	test = "test3-sendandcancel";
233152251Srwatson	printf("beginning %s\n", test);
234152251Srwatson
235152251Srwatson	domainsocketpair(test, fd);
236152251Srwatson	tempfile(test, &putfd_1);
237152251Srwatson	sendfd(test, fd[0], putfd_1);
238152251Srwatson	close(putfd_1);
239152251Srwatson	closesocketpair(fd);
240152251Srwatson
241152251Srwatson	printf("%s passed\n", test);
242152251Srwatson
243152251Srwatson	/*
244152251Srwatson	 * Send two files.  Then receive them.  Make sure they are returned
245152251Srwatson	 * in the right order, and both get there.
246152251Srwatson	 */
247152251Srwatson
248152251Srwatson	test = "test4-twofile";
249152251Srwatson	printf("beginning %s\n", test);
250152251Srwatson
251152251Srwatson	domainsocketpair(test, fd);
252152251Srwatson	tempfile(test, &putfd_1);
253152251Srwatson	tempfile(test, &putfd_2);
254152251Srwatson	dofstat(test, putfd_1, &putfd_1_stat);
255152251Srwatson	dofstat(test, putfd_2, &putfd_2_stat);
256152251Srwatson	sendfd(test, fd[0], putfd_1);
257152251Srwatson	sendfd(test, fd[0], putfd_2);
258152251Srwatson	close(putfd_1);
259152251Srwatson	close(putfd_2);
260152251Srwatson	recvfd(test, fd[1], &getfd_1);
261152251Srwatson	recvfd(test, fd[1], &getfd_2);
262152251Srwatson	dofstat(test, getfd_1, &getfd_1_stat);
263152251Srwatson	dofstat(test, getfd_2, &getfd_2_stat);
264152251Srwatson	samefile(test, &putfd_1_stat, &getfd_1_stat);
265152251Srwatson	samefile(test, &putfd_2_stat, &getfd_2_stat);
266152251Srwatson	close(getfd_1);
267152251Srwatson	close(getfd_2);
268152251Srwatson	closesocketpair(fd);
269152251Srwatson
270152251Srwatson	printf("%s passed\n", test);
271152251Srwatson
272152251Srwatson	/*
273152251Srwatson	 * Big bundling test.  Send an endpoint of the UNIX domain socket
274152251Srwatson	 * over itself, closing the door behind it.
275152251Srwatson	 */
276152251Srwatson
277152251Srwatson	test = "test5-bundle";
278152251Srwatson	printf("beginning %s\n", test);
279152251Srwatson
280152251Srwatson	domainsocketpair(test, fd);
281152251Srwatson
282152251Srwatson	sendfd(test, fd[0], fd[0]);
283152251Srwatson	close(fd[0]);
284152251Srwatson	recvfd(test, fd[1], &getfd_1);
285152251Srwatson	close(getfd_1);
286152251Srwatson	close(fd[1]);
287152251Srwatson
288152251Srwatson	printf("%s passed\n", test);
289152251Srwatson
290152251Srwatson	/*
291152251Srwatson	 * Big bundling test part two: Send an endpoint of the UNIX domain
292152251Srwatson	 * socket over itself, close the door behind it, and never remove it
293152251Srwatson	 * from the other end.
294152251Srwatson	 */
295152251Srwatson
296152251Srwatson	test = "test6-bundlecancel";
297152251Srwatson	printf("beginning %s\n", test);
298152251Srwatson
299152251Srwatson	domainsocketpair(test, fd);
300152251Srwatson	sendfd(test, fd[0], fd[0]);
301152251Srwatson	sendfd(test, fd[1], fd[0]);
302152251Srwatson	closesocketpair(fd);
303152251Srwatson
304152251Srwatson	printf("%s passed\n", test);
305152251Srwatson
306152251Srwatson	return (0);
307152251Srwatson}
308