1*6f5256c2Stb /*	$OpenBSD: sendrecvfd.c,v 1.1 2017/02/22 11:30:00 tb Exp $ */
2*6f5256c2Stb /*
3*6f5256c2Stb  * Copyright (c) 2017 Sebastien Marie <semarie@online.fr>
4*6f5256c2Stb  *
5*6f5256c2Stb  * Permission to use, copy, modify, and distribute this software for any
6*6f5256c2Stb  * purpose with or without fee is hereby granted, provided that the above
7*6f5256c2Stb  * copyright notice and this permission notice appear in all copies.
8*6f5256c2Stb  *
9*6f5256c2Stb  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*6f5256c2Stb  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*6f5256c2Stb  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12*6f5256c2Stb  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*6f5256c2Stb  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*6f5256c2Stb  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15*6f5256c2Stb  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*6f5256c2Stb  */
17*6f5256c2Stb 
18*6f5256c2Stb #include <sys/types.h>
19*6f5256c2Stb #include <sys/socket.h>
20*6f5256c2Stb #include <sys/stat.h>
21*6f5256c2Stb #include <sys/wait.h>
22*6f5256c2Stb 
23*6f5256c2Stb #include <err.h>
24*6f5256c2Stb #include <errno.h>
25*6f5256c2Stb #include <fcntl.h>
26*6f5256c2Stb #include <stdio.h>
27*6f5256c2Stb #include <stdlib.h>
28*6f5256c2Stb #include <string.h>
29*6f5256c2Stb #include <unistd.h>
30*6f5256c2Stb 
31*6f5256c2Stb enum testtype {
32*6f5256c2Stb 	nopledge,
33*6f5256c2Stb 	sendfd,
34*6f5256c2Stb 	recvfd,
35*6f5256c2Stb 	nosendfd,
36*6f5256c2Stb 	norecvfd,
37*6f5256c2Stb };
38*6f5256c2Stb 
39*6f5256c2Stb static void do_receiver(enum testtype type, int sock);
40*6f5256c2Stb static void do_sender(enum testtype type, int sock, int fd);
41*6f5256c2Stb __dead static void usage();
42*6f5256c2Stb 
43*6f5256c2Stb 
44*6f5256c2Stb static void
do_receiver(enum testtype type,int sock)45*6f5256c2Stb do_receiver(enum testtype type, int sock)
46*6f5256c2Stb {
47*6f5256c2Stb 	struct msghdr msg;
48*6f5256c2Stb 	struct cmsghdr *cmsg;
49*6f5256c2Stb 	union {
50*6f5256c2Stb 		struct cmsghdr hdr;
51*6f5256c2Stb 		unsigned char buf[CMSG_SPACE(sizeof(int))];
52*6f5256c2Stb 	} cmsgbuf;
53*6f5256c2Stb 
54*6f5256c2Stb 	/* pledge */
55*6f5256c2Stb 	switch(type) {
56*6f5256c2Stb 	case recvfd:
57*6f5256c2Stb 		if (pledge("stdio recvfd", NULL) == -1)
58*6f5256c2Stb 			err(EXIT_FAILURE, "receiver: pledge");
59*6f5256c2Stb 		break;
60*6f5256c2Stb 
61*6f5256c2Stb 	case norecvfd:
62*6f5256c2Stb 		if (pledge("stdio", NULL) == -1)
63*6f5256c2Stb 			err(EXIT_FAILURE, "receiver: pledge");
64*6f5256c2Stb 		break;
65*6f5256c2Stb 
66*6f5256c2Stb 	default:
67*6f5256c2Stb 		/* no pledge */
68*6f5256c2Stb 		break;
69*6f5256c2Stb 	}
70*6f5256c2Stb 
71*6f5256c2Stb 	memset(&msg, 0, sizeof(msg));
72*6f5256c2Stb 	msg.msg_control = &cmsgbuf.buf;
73*6f5256c2Stb 	msg.msg_controllen = sizeof(cmsgbuf.buf);
74*6f5256c2Stb 
75*6f5256c2Stb 	if (recvmsg(sock, &msg, 0) == -1)
76*6f5256c2Stb 		err(EXIT_FAILURE, "receiver: recvmsg");
77*6f5256c2Stb 
78*6f5256c2Stb 	if ((msg.msg_flags & MSG_TRUNC) || (msg.msg_flags & MSG_CTRUNC))
79*6f5256c2Stb 		errx(EXIT_FAILURE, "receiver: control message truncated");
80*6f5256c2Stb 
81*6f5256c2Stb 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
82*6f5256c2Stb 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
83*6f5256c2Stb 		if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) &&
84*6f5256c2Stb 		    cmsg->cmsg_level == SOL_SOCKET &&
85*6f5256c2Stb 		    cmsg->cmsg_type == SCM_RIGHTS) {
86*6f5256c2Stb 
87*6f5256c2Stb 			int fd = *(int *)CMSG_DATA(cmsg);
88*6f5256c2Stb 			struct stat sb;
89*6f5256c2Stb 
90*6f5256c2Stb 			/* test received fd */
91*6f5256c2Stb 			if (fstat(fd, &sb) == -1)
92*6f5256c2Stb 				err(EXIT_FAILURE, "receiver: fstat");
93*6f5256c2Stb 		}
94*6f5256c2Stb 	}
95*6f5256c2Stb }
96*6f5256c2Stb 
97*6f5256c2Stb static void
do_sender(enum testtype type,int sock,int fd)98*6f5256c2Stb do_sender(enum testtype type, int sock, int fd)
99*6f5256c2Stb {
100*6f5256c2Stb 	struct msghdr msg;
101*6f5256c2Stb 	struct cmsghdr *cmsg;
102*6f5256c2Stb 	union {
103*6f5256c2Stb 		struct cmsghdr hdr;
104*6f5256c2Stb 		unsigned char buf[CMSG_SPACE(sizeof(int))];
105*6f5256c2Stb 	} cmsgbuf;
106*6f5256c2Stb 
107*6f5256c2Stb 	/* pledge */
108*6f5256c2Stb 	switch (type) {
109*6f5256c2Stb 	case sendfd:
110*6f5256c2Stb 		if (pledge("stdio sendfd", NULL) == -1)
111*6f5256c2Stb 			err(EXIT_FAILURE, "sender: pledge");
112*6f5256c2Stb 		break;
113*6f5256c2Stb 
114*6f5256c2Stb 	case nosendfd:
115*6f5256c2Stb 		if (pledge("stdio", NULL) == -1)
116*6f5256c2Stb 			err(EXIT_FAILURE, "sender: pledge");
117*6f5256c2Stb 		break;
118*6f5256c2Stb 
119*6f5256c2Stb 	default:
120*6f5256c2Stb 		/* no pledge */
121*6f5256c2Stb 		break;
122*6f5256c2Stb 	}
123*6f5256c2Stb 
124*6f5256c2Stb 	memset(&msg, 0, sizeof(msg));
125*6f5256c2Stb 	msg.msg_control = &cmsgbuf.buf;
126*6f5256c2Stb 	msg.msg_controllen = sizeof(cmsgbuf.buf);
127*6f5256c2Stb 
128*6f5256c2Stb 	cmsg = CMSG_FIRSTHDR(&msg);
129*6f5256c2Stb 	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
130*6f5256c2Stb 	cmsg->cmsg_level = SOL_SOCKET;
131*6f5256c2Stb 	cmsg->cmsg_type = SCM_RIGHTS;
132*6f5256c2Stb 	*(int *)CMSG_DATA(cmsg) = fd;
133*6f5256c2Stb 
134*6f5256c2Stb 	if (sendmsg(sock, &msg, 0) == -1)
135*6f5256c2Stb 		err(EXIT_FAILURE, "sender: sendmsg");
136*6f5256c2Stb }
137*6f5256c2Stb 
138*6f5256c2Stb __dead static void
usage()139*6f5256c2Stb usage()
140*6f5256c2Stb {
141*6f5256c2Stb 	printf("usage: %s testtype vnodetype\n", getprogname());
142*6f5256c2Stb 	printf("  testtype  = nopledge sendfd recvfd nosendfd norecvfd\n");
143*6f5256c2Stb 	printf("  vnodetype = VREG VDIR VBLK VCHAR VLNK VSOCK VFIFO\n");
144*6f5256c2Stb 	exit(EXIT_FAILURE);
145*6f5256c2Stb }
146*6f5256c2Stb 
147*6f5256c2Stb int
main(int argc,char * argv[])148*6f5256c2Stb main(int argc, char *argv[])
149*6f5256c2Stb {
150*6f5256c2Stb 	enum testtype type;
151*6f5256c2Stb 	int fd;
152*6f5256c2Stb 	int sv[2], status;
153*6f5256c2Stb 	pid_t child;
154*6f5256c2Stb 
155*6f5256c2Stb 	/*
156*6f5256c2Stb 	 * parse arguments
157*6f5256c2Stb 	 */
158*6f5256c2Stb 
159*6f5256c2Stb 	if (argc != 3)
160*6f5256c2Stb 		usage();
161*6f5256c2Stb 
162*6f5256c2Stb 	if (strcmp(argv[1], "nopledge") == 0 ) {
163*6f5256c2Stb 		/* test sendfd/recvfd without pledge */
164*6f5256c2Stb 		type = nopledge;
165*6f5256c2Stb 
166*6f5256c2Stb 	} else if (strcmp(argv[1], "sendfd") == 0) {
167*6f5256c2Stb 		/* test sendfd process with "stdio sendfd" */
168*6f5256c2Stb 		type = sendfd;
169*6f5256c2Stb 
170*6f5256c2Stb 	} else if (strcmp(argv[1], "recvfd") == 0) {
171*6f5256c2Stb 		/* test recvfd process with "stdio recvfd" */
172*6f5256c2Stb 		type = recvfd;
173*6f5256c2Stb 
174*6f5256c2Stb 	} else if (strcmp(argv[1], "nosendfd") == 0) {
175*6f5256c2Stb 		/* test sendfd process with "stdio" (without "sendfd") */
176*6f5256c2Stb 		type = nosendfd;
177*6f5256c2Stb 
178*6f5256c2Stb 	} else if (strcmp(argv[1], "norecvfd") == 0) {
179*6f5256c2Stb 		/* test recvfd process with "stdio" (without "recvfd") */
180*6f5256c2Stb 		type = norecvfd;
181*6f5256c2Stb 
182*6f5256c2Stb 	} else
183*6f5256c2Stb 		usage();
184*6f5256c2Stb 
185*6f5256c2Stb 	/* open a file descriptor according to vnodetype requested */
186*6f5256c2Stb 	if (strcmp(argv[2], "VREG") == 0) {
187*6f5256c2Stb 		if ((fd = open("/etc/passwd", O_RDONLY)) == -1)
188*6f5256c2Stb 			err(EXIT_FAILURE, "open: VREG: /etc/passwd");
189*6f5256c2Stb 
190*6f5256c2Stb 	} else if (strcmp(argv[2], "VDIR") == 0) {
191*6f5256c2Stb 		if ((fd = open("/dev", O_RDONLY)) == -1)
192*6f5256c2Stb 			err(EXIT_FAILURE, "open: VDIR: /dev");
193*6f5256c2Stb 
194*6f5256c2Stb 	} else if (strcmp(argv[2], "VBLK") == 0) {
195*6f5256c2Stb 		if ((fd = open("/dev/vnd0c", O_RDONLY)) == -1)
196*6f5256c2Stb 		    err(EXIT_FAILURE, "open: VBLK: /dev/vnd0c");
197*6f5256c2Stb 
198*6f5256c2Stb 	} else if (strcmp(argv[2], "VCHR") == 0) {
199*6f5256c2Stb 		if ((fd = open("/dev/null", O_RDONLY)) == -1)
200*6f5256c2Stb 			err(EXIT_FAILURE, "open: VCHR: /dev/null");
201*6f5256c2Stb 
202*6f5256c2Stb 	} else if (strcmp(argv[2], "VLNK") == 0) {
203*6f5256c2Stb 		if ((fd = open("/etc/termcap", O_RDONLY)) == -1)
204*6f5256c2Stb 			err(EXIT_FAILURE, "open: VCHR: /etc/termcap");
205*6f5256c2Stb 
206*6f5256c2Stb 	} else if (strcmp(argv[2], "VSOCK") == 0) {
207*6f5256c2Stb 		/* create socket */
208*6f5256c2Stb 		if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
209*6f5256c2Stb 			err(EXIT_FAILURE, "socket: VSOCK");
210*6f5256c2Stb 
211*6f5256c2Stb 	} else if (strcmp(argv[2], "VFIFO") == 0) {
212*6f5256c2Stb 		/* unlink possibly existing file (from previous run) */
213*6f5256c2Stb 		unlink("fifo");
214*6f5256c2Stb 
215*6f5256c2Stb 		/* create a new named fifo */
216*6f5256c2Stb 		if (mkfifo("fifo", 0600) == -1)
217*6f5256c2Stb 			err(EXIT_FAILURE, "mkfifo: VFIFO");
218*6f5256c2Stb 
219*6f5256c2Stb 		/* open it */
220*6f5256c2Stb 		if ((fd = open("fifo", O_RDONLY|O_NONBLOCK)) == -1)
221*6f5256c2Stb 			err(EXIT_FAILURE, "open: VFIFO: fifo");
222*6f5256c2Stb 
223*6f5256c2Stb 		/* unlink the file now */
224*6f5256c2Stb 		unlink("fifo");
225*6f5256c2Stb 	} else
226*6f5256c2Stb 		usage();
227*6f5256c2Stb 
228*6f5256c2Stb 
229*6f5256c2Stb 	/*
230*6f5256c2Stb 	 * do test
231*6f5256c2Stb 	 */
232*6f5256c2Stb 
233*6f5256c2Stb 	/* communication socket */
234*6f5256c2Stb 	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, sv) == -1)
235*6f5256c2Stb 		err(EXIT_FAILURE, "socketpair");
236*6f5256c2Stb 
237*6f5256c2Stb 	/* create two procs and pass fd from one to another */
238*6f5256c2Stb 	switch (child = fork()) {
239*6f5256c2Stb 	case -1:	/* error */
240*6f5256c2Stb 		err(EXIT_FAILURE, "fork");
241*6f5256c2Stb 
242*6f5256c2Stb 	case 0:		/* child: receiver */
243*6f5256c2Stb 		close(fd);
244*6f5256c2Stb 		close(sv[0]);
245*6f5256c2Stb 
246*6f5256c2Stb 		do_receiver(type, sv[1]);
247*6f5256c2Stb 		_exit(EXIT_SUCCESS);
248*6f5256c2Stb 
249*6f5256c2Stb 	default:	/* parent: sender */
250*6f5256c2Stb 		close(sv[1]);
251*6f5256c2Stb 
252*6f5256c2Stb 		do_sender(type, sv[0], fd);
253*6f5256c2Stb 
254*6f5256c2Stb 		/* wait for child */
255*6f5256c2Stb 		while (waitpid(child, &status, 0) < 0) {
256*6f5256c2Stb 			if (errno == EAGAIN)
257*6f5256c2Stb 				continue;
258*6f5256c2Stb 
259*6f5256c2Stb 			err(EXIT_FAILURE, "waitpid");
260*6f5256c2Stb 		}
261*6f5256c2Stb 
262*6f5256c2Stb 		if (! WIFEXITED(status)) {
263*6f5256c2Stb 			if (WIFSIGNALED(status))
264*6f5256c2Stb 				errx(EXIT_FAILURE,
265*6f5256c2Stb 				    "child (receiver): WTERMSIG(): %d",
266*6f5256c2Stb 				    WTERMSIG(status));
267*6f5256c2Stb 
268*6f5256c2Stb 			errx(EXIT_FAILURE, "child (receiver): !WIFEXITED");
269*6f5256c2Stb 		}
270*6f5256c2Stb 
271*6f5256c2Stb 		if (WEXITSTATUS(status) != 0)
272*6f5256c2Stb 			errx(EXIT_FAILURE, "child(receiver): WEXITSTATUS(): %d",
273*6f5256c2Stb 			    WEXITSTATUS(status));
274*6f5256c2Stb 
275*6f5256c2Stb 		exit(EXIT_SUCCESS);
276*6f5256c2Stb 	}
277*6f5256c2Stb }
278