xref: /openbsd/regress/sys/kern/unfdpass/unfdpass.c (revision 6f40fd34)
1 /*	$OpenBSD: unfdpass.c,v 1.19 2017/01/26 04:45:46 benno 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 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;
76 	int type = SOCK_STREAM;
77 	extern char *__progname;
78 
79 	pflag = 0;
80 	while ((i = getopt(argc, argv, "pq")) != -1) {
81 		switch (i) {
82 		case 'p':
83 			pflag = 1;
84 			break;
85 		case 'q':
86 			type = SOCK_SEQPACKET;
87 			break;
88 		default:
89 			fprintf(stderr, "usage: %s [-p]\n", __progname);
90 			exit(1);
91 		}
92 	}
93 
94 	/*
95 	 * Create the test files.
96 	 */
97 	for (i = 0; i < 3; i++) {
98 		(void) snprintf(fname, sizeof fname, "file%d", i + 1);
99 		if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
100 			err(1, "open %s", fname);
101 		(void) snprintf(buf, sizeof buf, "This is file %d.\n", i + 1);
102 		if (write(fd, buf, strlen(buf)) != (ssize_t) strlen(buf))
103 			err(1, "write %s", fname);
104 		(void) close(fd);
105 	}
106 
107 	if (pflag) {
108 		/*
109 		 * Create the socketpair
110 		 */
111 		if (socketpair(PF_LOCAL, type, 0, pfd) == -1)
112 			err(1, "socketpair");
113 	} else {
114 		/*
115 		 * Create the listen socket.
116 		 */
117 		if ((listensock = socket(PF_LOCAL, type, 0)) == -1)
118 			err(1, "socket");
119 
120 		(void) unlink(SOCK_NAME);
121 		(void) memset(&sun, 0, sizeof(sun));
122 		sun.sun_family = AF_LOCAL;
123 		(void) strlcpy(sun.sun_path, SOCK_NAME, sizeof sun.sun_path);
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 
263 		if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
264 			err(1, "child connect");
265 	}
266 
267 	(void) memset(&msg, 0, sizeof(msg));
268 	msg.msg_control = &cmsgbuf.buf;
269 	msg.msg_controllen = sizeof(cmsgbuf.buf);
270 
271 	cmp = CMSG_FIRSTHDR(&msg);
272 	cmp->cmsg_len = CMSG_LEN(sizeof(int) * 3);
273 	cmp->cmsg_level = SOL_SOCKET;
274 	cmp->cmsg_type = SCM_RIGHTS;
275 
276 	/*
277 	 * Open the files again, and pass them to the parent over the socket.
278 	 */
279 	files = (int *)CMSG_DATA(cmp);
280 	for (i = 0; i < 3; i++) {
281 		(void) snprintf(fname, sizeof fname, "file%d", i + 1);
282 		if ((fd = open(fname, O_RDONLY, 0666)) == -1)
283 			err(1, "child open %s", fname);
284 		files[i] = fd;
285 	}
286 
287 	if (sendmsg(sock, &msg, 0))
288 		err(1, "child sendmsg");
289 
290 	/*
291 	 * All done!
292 	 */
293 	exit(0);
294 }
295