]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - tools/regression/sockets/unix_passfd/unix_passfd.c
MFC r292814:
[FreeBSD/stable/10.git] / tools / regression / sockets / unix_passfd / unix_passfd.c
1 /*-
2  * Copyright (c) 2005 Robert N. M. Watson
3  * Copyright (c) 2015 Mark Johnston
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33 #include <sys/sysctl.h>
34 #include <sys/un.h>
35
36 #include <err.h>
37 #include <fcntl.h>
38 #include <limits.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43
44 /*
45  * UNIX domain sockets allow file descriptors to be passed via "ancillary
46  * data", or control messages.  This regression test is intended to exercise
47  * this facility, both performing some basic tests that it operates, and also
48  * causing some kernel edge cases to execute, such as garbage collection when
49  * there are cyclic file descriptor references.  Right now we test only with
50  * stream sockets, but ideally we'd also test with datagram sockets.
51  */
52
53 static void
54 domainsocketpair(const char *test, int *fdp)
55 {
56
57         if (socketpair(PF_UNIX, SOCK_STREAM, 0, fdp) < 0)
58                 err(-1, "%s: socketpair(PF_UNIX, SOCK_STREAM)", test);
59 }
60
61 static void
62 closesocketpair(int *fdp)
63 {
64
65         close(fdp[0]);
66         close(fdp[1]);
67 }
68
69 static void
70 devnull(const char *test, int *fdp)
71 {
72         int fd;
73
74         fd = open("/dev/null", O_RDONLY);
75         if (fd < 0)
76                 err(-1, "%s: open(/dev/null)", test);
77         *fdp = fd;
78 }
79
80 static void
81 tempfile(const char *test, int *fdp)
82 {
83         char path[PATH_MAX];
84         int fd;
85
86         snprintf(path, PATH_MAX, "/tmp/unix_passfd.XXXXXXXXXXXXXXX");
87         fd = mkstemp(path);
88         if (fd < 0)
89                 err(-1, "%s: mkstemp(%s)", test, path);
90         (void)unlink(path);
91         *fdp = fd;
92 }
93
94 static void
95 dofstat(const char *test, int fd, struct stat *sb)
96 {
97
98         if (fstat(fd, sb) < 0)
99                 err(-1, "%s: fstat", test);
100 }
101
102 static void
103 samefile(const char *test, struct stat *sb1, struct stat *sb2)
104 {
105
106         if (sb1->st_dev != sb2->st_dev)
107                 errx(-1, "%s: samefile: different device", test);
108         if (sb1->st_ino != sb2->st_ino)
109                 errx(-1, "%s: samefile: different inode", test);
110 }
111
112 static void
113 sendfd_payload(const char *test, int sockfd, int sendfd,
114     void *payload, size_t paylen)
115 {
116         struct iovec iovec;
117         char message[CMSG_SPACE(sizeof(int))];
118         struct cmsghdr *cmsghdr;
119         struct msghdr msghdr;
120         ssize_t len;
121
122         bzero(&msghdr, sizeof(msghdr));
123         bzero(&message, sizeof(message));
124
125         msghdr.msg_control = message;
126         msghdr.msg_controllen = sizeof(message);
127
128         iovec.iov_base = payload;
129         iovec.iov_len = paylen;
130
131         msghdr.msg_iov = &iovec;
132         msghdr.msg_iovlen = 1;
133
134         cmsghdr = (struct cmsghdr *)message;
135         cmsghdr->cmsg_len = CMSG_LEN(sizeof(int));
136         cmsghdr->cmsg_level = SOL_SOCKET;
137         cmsghdr->cmsg_type = SCM_RIGHTS;
138         *(int *)CMSG_DATA(cmsghdr) = sendfd;
139
140         len = sendmsg(sockfd, &msghdr, 0);
141         if (len < 0)
142                 err(-1, "%s: sendmsg", test);
143         if ((size_t)len != paylen)
144                 errx(-1, "%s: sendmsg: %zd bytes sent", test, len);
145 }
146
147 static void
148 sendfd(const char *test, int sockfd, int sendfd)
149 {
150         char ch = 0;
151
152         return (sendfd_payload(test, sockfd, sendfd, &ch, sizeof(ch)));
153 }
154
155 static void
156 recvfd_payload(const char *test, int sockfd, int *recvfd,
157     void *buf, size_t buflen)
158 {
159         struct cmsghdr *cmsghdr;
160         char message[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + sizeof(int)];
161         struct msghdr msghdr;
162         struct iovec iovec;
163         ssize_t len;
164
165         bzero(&msghdr, sizeof(msghdr));
166
167         msghdr.msg_control = message;
168         msghdr.msg_controllen = sizeof(message);
169
170         iovec.iov_base = buf;
171         iovec.iov_len = buflen;
172
173         msghdr.msg_iov = &iovec;
174         msghdr.msg_iovlen = 1;
175
176         len = recvmsg(sockfd, &msghdr, 0);
177         if (len < 0)
178                 err(-1, "%s: recvmsg", test);
179         if ((size_t)len != buflen)
180                 errx(-1, "%s: recvmsg: %zd bytes received", test, len);
181
182         cmsghdr = CMSG_FIRSTHDR(&msghdr);
183         if (cmsghdr == NULL)
184                 errx(-1, "%s: recvmsg: did not receive control message", test);
185         *recvfd = -1;
186         for (; cmsghdr != NULL; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) {
187                 if (cmsghdr->cmsg_level == SOL_SOCKET &&
188                     cmsghdr->cmsg_type == SCM_RIGHTS &&
189                     cmsghdr->cmsg_len == CMSG_LEN(sizeof(int))) {
190                         *recvfd = *(int *)CMSG_DATA(cmsghdr);
191                         if (*recvfd == -1)
192                                 errx(-1, "%s: recvmsg: received fd -1", test);
193                 }
194         }
195         if (*recvfd == -1)
196                 errx(-1, "%s: recvmsg: did not receive single-fd message",
197                     test);
198 }
199
200 static void
201 recvfd(const char *test, int sockfd, int *recvfd)
202 {
203         char ch = 0;
204
205         return (recvfd_payload(test, sockfd, recvfd, &ch, sizeof(ch)));
206 }
207
208 int
209 main(void)
210 {
211         struct stat putfd_1_stat, putfd_2_stat, getfd_1_stat, getfd_2_stat;
212         int fd[2], putfd_1, putfd_2, getfd_1, getfd_2;
213         const char *test;
214
215         /*
216          * First test: put a temporary file into a UNIX domain socket, then
217          * take it out and make sure it's the same file.  First time around,
218          * don't close the reference after sending.
219          */
220         test = "test1-simplesendfd";
221         printf("beginning %s\n", test);
222
223         domainsocketpair(test, fd);
224         tempfile(test, &putfd_1);
225         dofstat(test, putfd_1, &putfd_1_stat);
226         sendfd(test, fd[0], putfd_1);
227         recvfd(test, fd[1], &getfd_1);
228         dofstat(test, getfd_1, &getfd_1_stat);
229         samefile(test, &putfd_1_stat, &getfd_1_stat);
230         close(putfd_1);
231         close(getfd_1);
232         closesocketpair(fd);
233
234         printf("%s passed\n", test);
235
236         /*
237          * Second test: same as first, only close the file reference after
238          * sending, so that the only reference is the descriptor in the UNIX
239          * domain socket buffer.
240          */
241         test = "test2-sendandclose";
242         printf("beginning %s\n", test);
243
244         domainsocketpair(test, fd);
245         tempfile(test, &putfd_1);
246         dofstat(test, putfd_1, &putfd_1_stat);
247         sendfd(test, fd[0], putfd_1);
248         close(putfd_1);
249         recvfd(test, fd[1], &getfd_1);
250         dofstat(test, getfd_1, &getfd_1_stat);
251         samefile(test, &putfd_1_stat, &getfd_1_stat);
252         close(getfd_1);
253         closesocketpair(fd);
254
255         printf("%s passed\n", test);
256
257         /*
258          * Third test: put a temporary file into a UNIX domain socket, then
259          * close both endpoints causing garbage collection to kick off.
260          */
261         test = "test3-sendandcancel";
262         printf("beginning %s\n", test);
263
264         domainsocketpair(test, fd);
265         tempfile(test, &putfd_1);
266         sendfd(test, fd[0], putfd_1);
267         close(putfd_1);
268         closesocketpair(fd);
269
270         printf("%s passed\n", test);
271
272         /*
273          * Send two files.  Then receive them.  Make sure they are returned
274          * in the right order, and both get there.
275          */
276
277         test = "test4-twofile";
278         printf("beginning %s\n", test);
279
280         domainsocketpair(test, fd);
281         tempfile(test, &putfd_1);
282         tempfile(test, &putfd_2);
283         dofstat(test, putfd_1, &putfd_1_stat);
284         dofstat(test, putfd_2, &putfd_2_stat);
285         sendfd(test, fd[0], putfd_1);
286         sendfd(test, fd[0], putfd_2);
287         close(putfd_1);
288         close(putfd_2);
289         recvfd(test, fd[1], &getfd_1);
290         recvfd(test, fd[1], &getfd_2);
291         dofstat(test, getfd_1, &getfd_1_stat);
292         dofstat(test, getfd_2, &getfd_2_stat);
293         samefile(test, &putfd_1_stat, &getfd_1_stat);
294         samefile(test, &putfd_2_stat, &getfd_2_stat);
295         close(getfd_1);
296         close(getfd_2);
297         closesocketpair(fd);
298
299         printf("%s passed\n", test);
300
301         /*
302          * Big bundling test.  Send an endpoint of the UNIX domain socket
303          * over itself, closing the door behind it.
304          */
305
306         test = "test5-bundle";
307         printf("beginning %s\n", test);
308
309         domainsocketpair(test, fd);
310
311         sendfd(test, fd[0], fd[0]);
312         close(fd[0]);
313         recvfd(test, fd[1], &getfd_1);
314         close(getfd_1);
315         close(fd[1]);
316
317         printf("%s passed\n", test);
318
319         /*
320          * Big bundling test part two: Send an endpoint of the UNIX domain
321          * socket over itself, close the door behind it, and never remove it
322          * from the other end.
323          */
324
325         test = "test6-bundlecancel";
326         printf("beginning %s\n", test);
327
328         domainsocketpair(test, fd);
329         sendfd(test, fd[0], fd[0]);
330         sendfd(test, fd[1], fd[0]);
331         closesocketpair(fd);
332
333         printf("%s passed\n", test);
334
335         /*
336          * Test for PR 151758: Send an character device over the UNIX
337          * domain socket and then close both sockets to orphan the
338          * device.
339          */
340
341         test = "test7-devfsorphan";
342         printf("beginning %s\n", test);
343
344         domainsocketpair(test, fd);
345         devnull(test, &putfd_1);
346         sendfd(test, fd[0], putfd_1);
347         close(putfd_1);
348         closesocketpair(fd);
349
350         printf("%s passed\n", test);
351
352         /*
353          * Test for PR 181741. Receiver sets LOCAL_CREDS, and kernel
354          * prepends a control message to the data. Sender sends large
355          * payload. Payload + SCM_RIGHTS + LOCAL_CREDS hit socket buffer
356          * limit, and receiver receives truncated data.
357          */
358         test = "test8-rights+creds+payload";
359         printf("beginning %s\n", test);
360
361         {
362                 const int on = 1;
363                 u_long sendspace;
364                 size_t len;
365                 void *buf;
366
367                 len = sizeof(sendspace);
368                 if (sysctlbyname("net.local.stream.sendspace", &sendspace,
369                     &len, NULL, 0) < 0)
370                         err(-1, "%s: sysctlbyname(net.local.stream.sendspace)",
371                             test);
372
373                 if ((buf = calloc(1, sendspace)) == NULL)
374                         err(-1, "%s: calloc", test);
375
376                 domainsocketpair(test, fd);
377                 if (setsockopt(fd[1], 0, LOCAL_CREDS, &on, sizeof(on)) < 0)
378                         err(-1, "%s: setsockopt(LOCAL_CREDS)", test);
379                 tempfile(test, &putfd_1);
380                 sendfd_payload(test, fd[0], putfd_1, buf, sendspace);
381                 recvfd_payload(test, fd[1], &getfd_1, buf, sendspace);
382                 close(putfd_1);
383                 close(getfd_1);
384                 closesocketpair(fd);
385         }
386
387         printf("%s passed\n", test);
388
389         return (0);
390 }