xref: /openbsd-src/regress/sys/kern/unfdpass/unfdpass.c (revision cb39b41371628601fbe4c618205356d538b9d08a)
1 /*	$OpenBSD: unfdpass.c,v 1.18 2015/01/19 00:22:30 guenther 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 
124 		if (bind(listensock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
125 			err(1, "bind");
126 
127 		if (listen(listensock, 1) == -1)
128 			err(1, "listen");
129 		pfd[0] = pfd[1] = -1;
130 	}
131 
132 	/*
133 	 * Create the sender.
134 	 */
135 	(void) signal(SIGCHLD, catch_sigchld);
136 	pid = fork();
137 	switch (pid) {
138 	case -1:
139 		err(1, "fork");
140 		/* NOTREACHED */
141 
142 	case 0:
143 		if (pfd[0] != -1)
144 			close(pfd[0]);
145 		child(pfd[1], type);
146 		/* NOTREACHED */
147 	}
148 
149 	if (pfd[0] != -1) {
150 		close(pfd[1]);
151 		sock = pfd[0];
152 	} else {
153 		/*
154 		 * Wait for the sender to connect.
155 		 */
156 		if ((sock = accept(listensock, (struct sockaddr *)&csun,
157 		    &csunlen)) == -1)
158 		err(1, "accept");
159 	}
160 
161 	/*
162 	 * Give sender a chance to run.  We will get going again
163 	 * once the SIGCHLD arrives.
164 	 */
165 	(void) sleep(10);
166 
167 	/*
168 	 * Grab the descriptors passed to us.
169 	 */
170 	(void) memset(&msg, 0, sizeof(msg));
171 	msg.msg_control = &cmsgbuf.buf;
172 	msg.msg_controllen = sizeof(cmsgbuf.buf);
173 
174 	if (recvmsg(sock, &msg, 0) < 0)
175 		err(1, "recvmsg");
176 
177 	(void) close(sock);
178 
179 	if (msg.msg_controllen == 0)
180 		errx(1, "no control messages received");
181 
182 	if (msg.msg_flags & MSG_CTRUNC)
183 		errx(1, "lost control message data");
184 
185 	for (cmp = CMSG_FIRSTHDR(&msg); cmp != NULL;
186 	    cmp = CMSG_NXTHDR(&msg, cmp)) {
187 		if (cmp->cmsg_level != SOL_SOCKET)
188 			errx(1, "bad control message level %d",
189 			    cmp->cmsg_level);
190 
191 		switch (cmp->cmsg_type) {
192 		case SCM_RIGHTS:
193 			if (cmp->cmsg_len != CMSG_LEN(sizeof(int) * 3))
194 				errx(1, "bad fd control message length %d",
195 				    cmp->cmsg_len);
196 
197 			files = (int *)CMSG_DATA(cmp);
198 			break;
199 
200 		default:
201 			errx(1, "unexpected control message");
202 			/* NOTREACHED */
203 		}
204 	}
205 
206 	/*
207 	 * Read the files and print their contents.
208 	 */
209 	if (files == NULL)
210 		warnx("didn't get fd control message");
211 	else {
212 		for (i = 0; i < 3; i++) {
213 			(void) memset(buf, 0, sizeof(buf));
214 			if (read(files[i], buf, sizeof(buf)) <= 0)
215 				err(1, "read file %d (%d)", i + 1, files[i]);
216 			printf("%s", buf);
217 		}
218 	}
219 
220 	/*
221 	 * All done!
222 	 */
223 	exit(0);
224 }
225 
226 void
227 catch_sigchld(sig)
228 	int sig;
229 {
230 	int save_errno = errno;
231 	int status;
232 
233 	(void) wait(&status);
234 	errno = save_errno;
235 }
236 
237 void
238 child(int sock, int type)
239 {
240 	struct msghdr msg;
241 	char fname[16];
242 	struct cmsghdr *cmp;
243 	int i, fd;
244 	struct sockaddr_un sun;
245 	union {
246 		struct cmsghdr hdr;
247 		char buf[CMSG_SPACE(sizeof(int) * 3)];
248 	} cmsgbuf;
249 	int *files;
250 
251 	/*
252 	 * Create socket if needed and connect to the receiver.
253 	 */
254 	if (sock == -1) {
255 		if ((sock = socket(PF_LOCAL, type, 0)) == -1)
256 			err(1, "child socket");
257 
258 		(void) memset(&sun, 0, sizeof(sun));
259 		sun.sun_family = AF_LOCAL;
260 		(void) strlcpy(sun.sun_path, SOCK_NAME, sizeof sun.sun_path);
261 
262 		if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
263 			err(1, "child connect");
264 	}
265 
266 	(void) memset(&msg, 0, sizeof(msg));
267 	msg.msg_control = &cmsgbuf.buf;
268 	msg.msg_controllen = sizeof(cmsgbuf.buf);
269 
270 	cmp = CMSG_FIRSTHDR(&msg);
271 	cmp->cmsg_len = CMSG_LEN(sizeof(int) * 3);
272 	cmp->cmsg_level = SOL_SOCKET;
273 	cmp->cmsg_type = SCM_RIGHTS;
274 
275 	/*
276 	 * Open the files again, and pass them to the child over the socket.
277 	 */
278 	files = (int *)CMSG_DATA(cmp);
279 	for (i = 0; i < 3; i++) {
280 		(void) snprintf(fname, sizeof fname, "file%d", i + 1);
281 		if ((fd = open(fname, O_RDONLY, 0666)) == -1)
282 			err(1, "child open %s", fname);
283 		files[i] = fd;
284 	}
285 
286 	if (sendmsg(sock, &msg, 0))
287 		err(1, "child sendmsg");
288 
289 	/*
290 	 * All done!
291 	 */
292 	exit(0);
293 }
294