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