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