xref: /openbsd-src/regress/sys/kern/unfdpass/unfdpass.c (revision de8cc8edbc71bd3e3bc7fbffa27ba0e564c37d8b)
1 /*	$OpenBSD: unfdpass.c,v 1.20 2018/11/28 08:06:22 claudio 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, int);
56 void	catch_sigchld(int);
57 
58 /* ARGSUSED */
59 int
60 main(int argc, char *argv[])
61 {
62 	struct msghdr msg;
63 	int sock, pfd[2], fd, i;
64 	int listensock = -1;
65 	char fname[16], buf[64];
66 	struct cmsghdr *cmp;
67 	int *files = NULL;
68 	struct sockaddr_un sun, csun;
69 	int csunlen;
70 	pid_t pid;
71 	union {
72 		struct cmsghdr hdr;
73 		char buf[CMSG_SPACE(sizeof(int) * 3)];
74 	} cmsgbuf;
75 	int pflag, oflag, rflag;
76 	int type = SOCK_STREAM;
77 	extern char *__progname;
78 
79 	pflag = 0;
80 	oflag = 0;
81 	rflag = 0;
82 	while ((i = getopt(argc, argv, "opqr")) != -1) {
83 		switch (i) {
84 		case 'o':
85 			oflag = 1;
86 			break;
87 		case 'p':
88 			pflag = 1;
89 			break;
90 		case 'q':
91 			type = SOCK_SEQPACKET;
92 			break;
93 		case 'r':
94 			rflag = 1;
95 			break;
96 		default:
97 			fprintf(stderr, "usage: %s [-opqr]\n", __progname);
98 			exit(1);
99 		}
100 	}
101 
102 	/*
103 	 * Create the test files.
104 	 */
105 	for (i = 0; i < 5; i++) {
106 		(void) snprintf(fname, sizeof fname, "file%d", i + 1);
107 		if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
108 			err(1, "open %s", fname);
109 		(void) snprintf(buf, sizeof buf, "This is file %d.\n", i + 1);
110 		if (write(fd, buf, strlen(buf)) != (ssize_t) strlen(buf))
111 			err(1, "write %s", fname);
112 		(void) close(fd);
113 	}
114 
115 	if (pflag) {
116 		/*
117 		 * Create the socketpair
118 		 */
119 		if (socketpair(PF_LOCAL, type, 0, pfd) == -1)
120 			err(1, "socketpair");
121 	} else {
122 		/*
123 		 * Create the listen socket.
124 		 */
125 		if ((listensock = socket(PF_LOCAL, type, 0)) == -1)
126 			err(1, "socket");
127 
128 		(void) unlink(SOCK_NAME);
129 		(void) memset(&sun, 0, sizeof(sun));
130 		sun.sun_family = AF_LOCAL;
131 		(void) strlcpy(sun.sun_path, SOCK_NAME, sizeof sun.sun_path);
132 
133 		if (bind(listensock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
134 			err(1, "bind");
135 
136 		if (listen(listensock, 1) == -1)
137 			err(1, "listen");
138 		pfd[0] = pfd[1] = -1;
139 	}
140 
141 	/*
142 	 * Create the sender.
143 	 */
144 	(void) signal(SIGCHLD, catch_sigchld);
145 	pid = fork();
146 	switch (pid) {
147 	case -1:
148 		err(1, "fork");
149 		/* NOTREACHED */
150 
151 	case 0:
152 		if (pfd[0] != -1)
153 			close(pfd[0]);
154 		child(pfd[1], type, oflag);
155 		/* NOTREACHED */
156 	}
157 
158 	if (pfd[0] != -1) {
159 		close(pfd[1]);
160 		sock = pfd[0];
161 	} else {
162 		/*
163 		 * Wait for the sender to connect.
164 		 */
165 		if ((sock = accept(listensock, (struct sockaddr *)&csun,
166 		    &csunlen)) == -1)
167 		err(1, "accept");
168 	}
169 
170 	/*
171 	 * Give sender a chance to run.  We will get going again
172 	 * once the SIGCHLD arrives.
173 	 */
174 	(void) sleep(10);
175 
176 	if (rflag) {
177 		if (read(sock, buf, sizeof(buf)) < 0)
178 			err(1, "read");
179 		printf("read successfully returned\n");
180 		exit(0);
181 	}
182 
183 	/*
184 	 * Grab the descriptors passed to us.
185 	 */
186 	memset(&msg, 0, sizeof(msg));
187 	msg.msg_control = &cmsgbuf.buf;
188 	msg.msg_controllen = sizeof(cmsgbuf.buf);
189 
190 	if (recvmsg(sock, &msg, 0) < 0) {
191 		if (errno == EMSGSIZE) {
192 			printf("recvmsg returned EMSGSIZE\n");
193 			exit(0);
194 		} else
195 			err(1, "recvmsg");
196 	}
197 
198 	(void) close(sock);
199 
200 	if (msg.msg_controllen == 0)
201 		errx(1, "no control messages received");
202 
203 	if (msg.msg_flags & MSG_CTRUNC)
204 		errx(1, "lost control message data");
205 
206 	for (cmp = CMSG_FIRSTHDR(&msg); cmp != NULL;
207 	    cmp = CMSG_NXTHDR(&msg, cmp)) {
208 		if (cmp->cmsg_level != SOL_SOCKET)
209 			errx(1, "bad control message level %d",
210 			    cmp->cmsg_level);
211 
212 		switch (cmp->cmsg_type) {
213 		case SCM_RIGHTS:
214 			if (cmp->cmsg_len != CMSG_LEN(sizeof(int) * 3))
215 				errx(1, "bad fd control message length %d",
216 				    cmp->cmsg_len);
217 
218 			files = (int *)CMSG_DATA(cmp);
219 			break;
220 
221 		default:
222 			errx(1, "unexpected control message");
223 			/* NOTREACHED */
224 		}
225 	}
226 
227 	/*
228 	 * Read the files and print their contents.
229 	 */
230 	if (files == NULL)
231 		warnx("didn't get fd control message");
232 	else {
233 		for (i = 0; i < 3; i++) {
234 			(void) memset(buf, 0, sizeof(buf));
235 			if (read(files[i], buf, sizeof(buf)) <= 0)
236 				err(1, "read file %d (%d)", i + 1, files[i]);
237 			printf("%s", buf);
238 		}
239 	}
240 
241 	/*
242 	 * All done!
243 	 */
244 	exit(0);
245 }
246 
247 void
248 catch_sigchld(sig)
249 	int sig;
250 {
251 	int save_errno = errno;
252 	int status;
253 
254 	(void) wait(&status);
255 	errno = save_errno;
256 }
257 
258 void
259 child(int sock, int type, int oflag)
260 {
261 	struct msghdr msg;
262 	char fname[16];
263 	struct cmsghdr *cmp;
264 	int i, fd, nfds = 3;
265 	struct sockaddr_un sun;
266 	size_t len;
267 	char *cmsgbuf;
268 	int *files;
269 
270 	/*
271 	 * Create socket if needed and connect to the receiver.
272 	 */
273 	if (sock == -1) {
274 		if ((sock = socket(PF_LOCAL, type, 0)) == -1)
275 			err(1, "child socket");
276 
277 		(void) memset(&sun, 0, sizeof(sun));
278 		sun.sun_family = AF_LOCAL;
279 		(void) strlcpy(sun.sun_path, SOCK_NAME, sizeof sun.sun_path);
280 
281 		if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
282 			err(1, "child connect");
283 	}
284 
285 	if (oflag)
286 		nfds = 5;
287 	len = CMSG_SPACE(sizeof(int) * nfds);
288 	if ((cmsgbuf = malloc(len)) == NULL)
289 		err(1, "child");
290 
291 	(void) memset(&msg, 0, sizeof(msg));
292 	msg.msg_control = cmsgbuf;
293 	msg.msg_controllen = len;
294 
295 	cmp = CMSG_FIRSTHDR(&msg);
296 	cmp->cmsg_len = CMSG_LEN((sizeof(int) * nfds));
297 	cmp->cmsg_level = SOL_SOCKET;
298 	cmp->cmsg_type = SCM_RIGHTS;
299 
300 	/*
301 	 * Open the files again, and pass them to the parent over the socket.
302 	 */
303 	files = (int *)CMSG_DATA(cmp);
304 	for (i = 0; i < nfds; i++) {
305 		(void) snprintf(fname, sizeof fname, "file%d", i + 1);
306 		if ((fd = open(fname, O_RDONLY, 0666)) == -1)
307 			err(1, "child open %s", fname);
308 		files[i] = fd;
309 	}
310 
311 	if (sendmsg(sock, &msg, 0))
312 		err(1, "child sendmsg");
313 
314 	/*
315 	 * All done!
316 	 */
317 	exit(0);
318 }
319