unix_passfd_test.c revision 152251
1/*-
2 * Copyright (c) 2005 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/tools/regression/sockets/unix_passfd/unix_passfd.c 152251 2005-11-09 21:41:20Z rwatson $
27 */
28
29#include <sys/types.h>
30#include <sys/socket.h>
31#include <sys/stat.h>
32
33#include <err.h>
34#include <limits.h>
35#include <stdio.h>
36#include <string.h>
37#include <unistd.h>
38
39/*
40 * UNIX domain sockets allow file descriptors to be passed via "ancillary
41 * data", or control messages.  This regression test is intended to exercise
42 * this facility, both performing some basic tests that it operates, and also
43 * causing some kernel edge cases to execute, such as garbage collection when
44 * there are cyclic file descriptor references.  Right now we test only with
45 * stream sockets, but ideally we'd also test with datagram sockets.
46 */
47
48static void
49domainsocketpair(const char *test, int *fdp)
50{
51
52	if (socketpair(PF_UNIX, SOCK_STREAM, 0, fdp) < 0)
53		err(-1, "%s: socketpair(PF_UNIX, SOCK_STREAM)", test);
54}
55
56static void
57closesocketpair(int *fdp)
58{
59
60	close(fdp[0]);
61	close(fdp[1]);
62}
63
64static void
65tempfile(const char *test, int *fdp)
66{
67	char path[PATH_MAX];
68	int fd;
69
70	snprintf(path, PATH_MAX, "/tmp/unix_passfd.XXXXXXXXXXXXXXX");
71	fd = mkstemp(path);
72	if (fd < 0)
73		err(-1, "%s: mkstemp(%s)", test, path);
74	(void)unlink(path);
75	*fdp = fd;
76}
77
78static void
79dofstat(const char *test, int fd, struct stat *sb)
80{
81
82	if (fstat(fd, sb) < 0)
83		err(-1, "%s: fstat", test);
84}
85
86static void
87samefile(const char *test, struct stat *sb1, struct stat *sb2)
88{
89
90	if (sb1->st_dev != sb2->st_dev)
91		err(-1, "%s: samefile: different device", test);
92	if (sb1->st_ino != sb2->st_ino)
93		err(-1, "%s: samefile: different inode", test);
94}
95
96static void
97sendfd(const char *test, int sockfd, int sendfd)
98{
99	struct iovec iovec;
100	char ch;
101
102	struct {
103		struct cmsghdr cmsghdr;
104		int fd;
105	} message;
106	struct msghdr msghdr;
107	ssize_t len;
108
109	bzero(&msghdr, sizeof(msghdr));
110	bzero(&message, sizeof(message));
111	ch = 0;
112
113	msghdr.msg_control = &message;
114	msghdr.msg_controllen = sizeof(message);
115
116	iovec.iov_base = &ch;
117	iovec.iov_len = sizeof(ch);
118
119	msghdr.msg_iov = &iovec;
120	msghdr.msg_iovlen = 1;
121
122	message.cmsghdr.cmsg_len = sizeof(message);
123	message.cmsghdr.cmsg_level = SOL_SOCKET;
124	message.cmsghdr.cmsg_type = SCM_RIGHTS;
125	message.fd = sendfd;
126
127	len = sendmsg(sockfd, &msghdr, 0);
128	if (len < 0)
129		err(-1, "%s: sendmsg", test);
130	if (len != sizeof(ch))
131		errx(-1, "%s: sendmsg: %d bytes sent", test, len);
132}
133
134static void
135recvfd(const char *test, int sockfd, int *recvfd)
136{
137	struct {
138		struct cmsghdr cmsghdr;
139		int fd;
140	} message;
141	struct msghdr msghdr;
142	struct iovec iovec;
143	ssize_t len;
144	char ch;
145
146	bzero(&msghdr, sizeof(msghdr));
147	bzero(&message, sizeof(message));
148	ch = 0;
149
150	msghdr.msg_control = &message;
151	msghdr.msg_controllen = sizeof(message);
152
153	iovec.iov_base = &ch;
154	iovec.iov_len = sizeof(ch);
155
156	msghdr.msg_iov = &iovec;
157	msghdr.msg_iovlen = 1;
158
159	iovec.iov_len = sizeof(ch);
160
161	msghdr.msg_iov = &iovec;
162	msghdr.msg_iovlen = 1;
163
164	message.cmsghdr.cmsg_len = sizeof(message);
165	message.cmsghdr.cmsg_level = SOL_SOCKET;
166	message.cmsghdr.cmsg_type = SCM_RIGHTS;
167	message.fd = -1;
168
169	len = recvmsg(sockfd, &msghdr, 0);
170	if (len < 0)
171		err(-1, "%s: recvmsg", test);
172	if (len != sizeof(ch))
173		errx(-1, "%s: recvmsg: %d bytes received", test, len);
174	if (message.fd == -1)
175		errx(-1, "%s: recvmsg: received fd -1", test);
176	*recvfd = message.fd;
177}
178
179int
180main(int argc, char *argv[])
181{
182	struct stat putfd_1_stat, putfd_2_stat, getfd_1_stat, getfd_2_stat;
183	int fd[2], putfd_1, putfd_2, getfd_1, getfd_2;
184	const char *test;
185
186	/*
187	 * First test: put a temporary file into a UNIX domain socket, then
188	 * take it out and make sure it's the same file.  First time around,
189	 * don't close the reference after sending.
190	 */
191	test = "test1-simplesendfd";
192	printf("beginning %s\n", test);
193
194	domainsocketpair(test, fd);
195	tempfile(test, &putfd_1);
196	dofstat(test, putfd_1, &putfd_1_stat);
197	sendfd(test, fd[0], putfd_1);
198	recvfd(test, fd[1], &getfd_1);
199	dofstat(test, getfd_1, &getfd_1_stat);
200	samefile(test, &putfd_1_stat, &getfd_1_stat);
201	close(putfd_1);
202	close(getfd_1);
203	closesocketpair(fd);
204
205	printf("%s passed\n", test);
206
207	/*
208	 * Second test: same as first, only close the file reference after
209	 * sending, so that the only reference is the descriptor in the UNIX
210	 * domain socket buffer.
211	 */
212	test = "test2-sendandclose";
213	printf("beginning %s\n", test);
214
215	domainsocketpair(test, fd);
216	tempfile(test, &putfd_1);
217	dofstat(test, putfd_1, &putfd_1_stat);
218	sendfd(test, fd[0], putfd_1);
219	close(putfd_1);
220	recvfd(test, fd[1], &getfd_1);
221	dofstat(test, getfd_1, &getfd_1_stat);
222	samefile(test, &putfd_1_stat, &getfd_1_stat);
223	close(getfd_1);
224	closesocketpair(fd);
225
226	printf("%s passed\n", test);
227
228	/*
229	 * Third test: put a temporary file into a UNIX domain socket, then
230	 * close both endpoints causing garbage collection to kick off.
231	 */
232	test = "test3-sendandcancel";
233	printf("beginning %s\n", test);
234
235	domainsocketpair(test, fd);
236	tempfile(test, &putfd_1);
237	sendfd(test, fd[0], putfd_1);
238	close(putfd_1);
239	closesocketpair(fd);
240
241	printf("%s passed\n", test);
242
243	/*
244	 * Send two files.  Then receive them.  Make sure they are returned
245	 * in the right order, and both get there.
246	 */
247
248	test = "test4-twofile";
249	printf("beginning %s\n", test);
250
251	domainsocketpair(test, fd);
252	tempfile(test, &putfd_1);
253	tempfile(test, &putfd_2);
254	dofstat(test, putfd_1, &putfd_1_stat);
255	dofstat(test, putfd_2, &putfd_2_stat);
256	sendfd(test, fd[0], putfd_1);
257	sendfd(test, fd[0], putfd_2);
258	close(putfd_1);
259	close(putfd_2);
260	recvfd(test, fd[1], &getfd_1);
261	recvfd(test, fd[1], &getfd_2);
262	dofstat(test, getfd_1, &getfd_1_stat);
263	dofstat(test, getfd_2, &getfd_2_stat);
264	samefile(test, &putfd_1_stat, &getfd_1_stat);
265	samefile(test, &putfd_2_stat, &getfd_2_stat);
266	close(getfd_1);
267	close(getfd_2);
268	closesocketpair(fd);
269
270	printf("%s passed\n", test);
271
272	/*
273	 * Big bundling test.  Send an endpoint of the UNIX domain socket
274	 * over itself, closing the door behind it.
275	 */
276
277	test = "test5-bundle";
278	printf("beginning %s\n", test);
279
280	domainsocketpair(test, fd);
281
282	sendfd(test, fd[0], fd[0]);
283	close(fd[0]);
284	recvfd(test, fd[1], &getfd_1);
285	close(getfd_1);
286	close(fd[1]);
287
288	printf("%s passed\n", test);
289
290	/*
291	 * Big bundling test part two: Send an endpoint of the UNIX domain
292	 * socket over itself, close the door behind it, and never remove it
293	 * from the other end.
294	 */
295
296	test = "test6-bundlecancel";
297	printf("beginning %s\n", test);
298
299	domainsocketpair(test, fd);
300	sendfd(test, fd[0], fd[0]);
301	sendfd(test, fd[1], fd[0]);
302	closesocketpair(fd);
303
304	printf("%s passed\n", test);
305
306	return (0);
307}
308