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