xref: /openbsd-src/regress/sys/kern/unfdpass/unfdpass.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: unfdpass.c,v 1.17 2011/07/06 19:48:10 matthew Exp $	*/
2 /*	$NetBSD: unfdpass.c,v 1.3 1998/06/24 23:51:30 thorpej Exp $	*/
3 
4 /*-
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10  * NASA Ames Research Center.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * Test passing of file descriptors over Unix domain sockets and socketpairs.
36  */
37 
38 #include <sys/param.h>
39 #include <sys/socket.h>
40 #include <sys/time.h>
41 #include <sys/wait.h>
42 #include <sys/un.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 
52 #define	SOCK_NAME	"test-sock"
53 
54 int	main(int, char *[]);
55 void	child(int, int);
56 void	catch_sigchld(int);
57 
58 /* ARGSUSED */
59 int
60 main(int argc, char *argv[])
61 {
62 	struct msghdr msg;
63 	int listensock, sock, pfd[2], fd, i;
64 	char fname[16], buf[64];
65 	struct cmsghdr *cmp;
66 	int *files = NULL;
67 	struct sockaddr_un sun, csun;
68 	int csunlen;
69 	pid_t pid;
70 	union {
71 		struct cmsghdr hdr;
72 		char buf[CMSG_SPACE(sizeof(int) * 3)];
73 	} cmsgbuf;
74 	int pflag;
75 	int type = SOCK_STREAM;
76 	extern char *__progname;
77 
78 	pflag = 0;
79 	while ((i = getopt(argc, argv, "pq")) != -1) {
80 		switch (i) {
81 		case 'p':
82 			pflag = 1;
83 			break;
84 		case 'q':
85 			type = SOCK_SEQPACKET;
86 			break;
87 		default:
88 			fprintf(stderr, "usage: %s [-p]\n", __progname);
89 			exit(1);
90 		}
91 	}
92 
93 	/*
94 	 * Create the test files.
95 	 */
96 	for (i = 0; i < 3; i++) {
97 		(void) snprintf(fname, sizeof fname, "file%d", i + 1);
98 		if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
99 			err(1, "open %s", fname);
100 		(void) snprintf(buf, sizeof buf, "This is file %d.\n", i + 1);
101 		if (write(fd, buf, strlen(buf)) != strlen(buf))
102 			err(1, "write %s", fname);
103 		(void) close(fd);
104 	}
105 
106 	if (pflag) {
107 		/*
108 		 * Create the socketpair
109 		 */
110 		if (socketpair(PF_LOCAL, type, 0, pfd) == -1)
111 			err(1, "socketpair");
112 	} else {
113 		/*
114 		 * Create the listen socket.
115 		 */
116 		if ((listensock = socket(PF_LOCAL, type, 0)) == -1)
117 			err(1, "socket");
118 
119 		(void) unlink(SOCK_NAME);
120 		(void) memset(&sun, 0, sizeof(sun));
121 		sun.sun_family = AF_LOCAL;
122 		(void) strlcpy(sun.sun_path, SOCK_NAME, sizeof sun.sun_path);
123 		sun.sun_len = SUN_LEN(&sun);
124 
125 		if (bind(listensock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
126 			err(1, "bind");
127 
128 		if (listen(listensock, 1) == -1)
129 			err(1, "listen");
130 		pfd[0] = pfd[1] = -1;
131 	}
132 
133 	/*
134 	 * Create the sender.
135 	 */
136 	(void) signal(SIGCHLD, catch_sigchld);
137 	pid = fork();
138 	switch (pid) {
139 	case -1:
140 		err(1, "fork");
141 		/* NOTREACHED */
142 
143 	case 0:
144 		if (pfd[0] != -1)
145 			close(pfd[0]);
146 		child(pfd[1], type);
147 		/* NOTREACHED */
148 	}
149 
150 	if (pfd[0] != -1) {
151 		close(pfd[1]);
152 		sock = pfd[0];
153 	} else {
154 		/*
155 		 * Wait for the sender to connect.
156 		 */
157 		if ((sock = accept(listensock, (struct sockaddr *)&csun,
158 		    &csunlen)) == -1)
159 		err(1, "accept");
160 	}
161 
162 	/*
163 	 * Give sender a chance to run.  We will get going again
164 	 * once the SIGCHLD arrives.
165 	 */
166 	(void) sleep(10);
167 
168 	/*
169 	 * Grab the descriptors passed to us.
170 	 */
171 	(void) memset(&msg, 0, sizeof(msg));
172 	msg.msg_control = &cmsgbuf.buf;
173 	msg.msg_controllen = sizeof(cmsgbuf.buf);
174 
175 	if (recvmsg(sock, &msg, 0) < 0)
176 		err(1, "recvmsg");
177 
178 	(void) close(sock);
179 
180 	if (msg.msg_controllen == 0)
181 		errx(1, "no control messages received");
182 
183 	if (msg.msg_flags & MSG_CTRUNC)
184 		errx(1, "lost control message data");
185 
186 	for (cmp = CMSG_FIRSTHDR(&msg); cmp != NULL;
187 	    cmp = CMSG_NXTHDR(&msg, cmp)) {
188 		if (cmp->cmsg_level != SOL_SOCKET)
189 			errx(1, "bad control message level %d",
190 			    cmp->cmsg_level);
191 
192 		switch (cmp->cmsg_type) {
193 		case SCM_RIGHTS:
194 			if (cmp->cmsg_len != CMSG_LEN(sizeof(int) * 3))
195 				errx(1, "bad fd control message length %d",
196 				    cmp->cmsg_len);
197 
198 			files = (int *)CMSG_DATA(cmp);
199 			break;
200 
201 		default:
202 			errx(1, "unexpected control message");
203 			/* NOTREACHED */
204 		}
205 	}
206 
207 	/*
208 	 * Read the files and print their contents.
209 	 */
210 	if (files == NULL)
211 		warnx("didn't get fd control message");
212 	else {
213 		for (i = 0; i < 3; i++) {
214 			(void) memset(buf, 0, sizeof(buf));
215 			if (read(files[i], buf, sizeof(buf)) <= 0)
216 				err(1, "read file %d (%d)", i + 1, files[i]);
217 			printf("%s", buf);
218 		}
219 	}
220 
221 	/*
222 	 * All done!
223 	 */
224 	exit(0);
225 }
226 
227 void
228 catch_sigchld(sig)
229 	int sig;
230 {
231 	int save_errno = errno;
232 	int status;
233 
234 	(void) wait(&status);
235 	errno = save_errno;
236 }
237 
238 void
239 child(int sock, int type)
240 {
241 	struct msghdr msg;
242 	char fname[16];
243 	struct cmsghdr *cmp;
244 	int i, fd;
245 	struct sockaddr_un sun;
246 	union {
247 		struct cmsghdr hdr;
248 		char buf[CMSG_SPACE(sizeof(int) * 3)];
249 	} cmsgbuf;
250 	int *files;
251 
252 	/*
253 	 * Create socket if needed and connect to the receiver.
254 	 */
255 	if (sock == -1) {
256 		if ((sock = socket(PF_LOCAL, type, 0)) == -1)
257 			err(1, "child socket");
258 
259 		(void) memset(&sun, 0, sizeof(sun));
260 		sun.sun_family = AF_LOCAL;
261 		(void) strlcpy(sun.sun_path, SOCK_NAME, sizeof sun.sun_path);
262 		sun.sun_len = SUN_LEN(&sun);
263 
264 		if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
265 			err(1, "child connect");
266 	}
267 
268 	(void) memset(&msg, 0, sizeof(msg));
269 	msg.msg_control = &cmsgbuf.buf;
270 	msg.msg_controllen = sizeof(cmsgbuf.buf);
271 
272 	cmp = CMSG_FIRSTHDR(&msg);
273 	cmp->cmsg_len = CMSG_LEN(sizeof(int) * 3);
274 	cmp->cmsg_level = SOL_SOCKET;
275 	cmp->cmsg_type = SCM_RIGHTS;
276 
277 	/*
278 	 * Open the files again, and pass them to the child over the socket.
279 	 */
280 	files = (int *)CMSG_DATA(cmp);
281 	for (i = 0; i < 3; i++) {
282 		(void) snprintf(fname, sizeof fname, "file%d", i + 1);
283 		if ((fd = open(fname, O_RDONLY, 0666)) == -1)
284 			err(1, "child open %s", fname);
285 		files[i] = fd;
286 	}
287 
288 	if (sendmsg(sock, &msg, 0))
289 		err(1, "child sendmsg");
290 
291 	/*
292 	 * All done!
293 	 */
294 	exit(0);
295 }
296