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