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