unix_passfd_test.c revision 152251
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: head/tools/regression/sockets/unix_passfd/unix_passfd.c 152251 2005-11-09 21:41:20Z rwatson $ 27 */ 28 29#include <sys/types.h> 30#include <sys/socket.h> 31#include <sys/stat.h> 32 33#include <err.h> 34#include <limits.h> 35#include <stdio.h> 36#include <string.h> 37#include <unistd.h> 38 39/* 40 * UNIX domain sockets allow file descriptors to be passed via "ancillary 41 * data", or control messages. This regression test is intended to exercise 42 * this facility, both performing some basic tests that it operates, and also 43 * causing some kernel edge cases to execute, such as garbage collection when 44 * there are cyclic file descriptor references. Right now we test only with 45 * stream sockets, but ideally we'd also test with datagram sockets. 46 */ 47 48static void 49domainsocketpair(const char *test, int *fdp) 50{ 51 52 if (socketpair(PF_UNIX, SOCK_STREAM, 0, fdp) < 0) 53 err(-1, "%s: socketpair(PF_UNIX, SOCK_STREAM)", test); 54} 55 56static void 57closesocketpair(int *fdp) 58{ 59 60 close(fdp[0]); 61 close(fdp[1]); 62} 63 64static void 65tempfile(const char *test, int *fdp) 66{ 67 char path[PATH_MAX]; 68 int fd; 69 70 snprintf(path, PATH_MAX, "/tmp/unix_passfd.XXXXXXXXXXXXXXX"); 71 fd = mkstemp(path); 72 if (fd < 0) 73 err(-1, "%s: mkstemp(%s)", test, path); 74 (void)unlink(path); 75 *fdp = fd; 76} 77 78static void 79dofstat(const char *test, int fd, struct stat *sb) 80{ 81 82 if (fstat(fd, sb) < 0) 83 err(-1, "%s: fstat", test); 84} 85 86static void 87samefile(const char *test, struct stat *sb1, struct stat *sb2) 88{ 89 90 if (sb1->st_dev != sb2->st_dev) 91 err(-1, "%s: samefile: different device", test); 92 if (sb1->st_ino != sb2->st_ino) 93 err(-1, "%s: samefile: different inode", test); 94} 95 96static void 97sendfd(const char *test, int sockfd, int sendfd) 98{ 99 struct iovec iovec; 100 char ch; 101 102 struct { 103 struct cmsghdr cmsghdr; 104 int fd; 105 } message; 106 struct msghdr msghdr; 107 ssize_t len; 108 109 bzero(&msghdr, sizeof(msghdr)); 110 bzero(&message, sizeof(message)); 111 ch = 0; 112 113 msghdr.msg_control = &message; 114 msghdr.msg_controllen = sizeof(message); 115 116 iovec.iov_base = &ch; 117 iovec.iov_len = sizeof(ch); 118 119 msghdr.msg_iov = &iovec; 120 msghdr.msg_iovlen = 1; 121 122 message.cmsghdr.cmsg_len = sizeof(message); 123 message.cmsghdr.cmsg_level = SOL_SOCKET; 124 message.cmsghdr.cmsg_type = SCM_RIGHTS; 125 message.fd = sendfd; 126 127 len = sendmsg(sockfd, &msghdr, 0); 128 if (len < 0) 129 err(-1, "%s: sendmsg", test); 130 if (len != sizeof(ch)) 131 errx(-1, "%s: sendmsg: %d bytes sent", test, len); 132} 133 134static void 135recvfd(const char *test, int sockfd, int *recvfd) 136{ 137 struct { 138 struct cmsghdr cmsghdr; 139 int fd; 140 } message; 141 struct msghdr msghdr; 142 struct iovec iovec; 143 ssize_t len; 144 char ch; 145 146 bzero(&msghdr, sizeof(msghdr)); 147 bzero(&message, sizeof(message)); 148 ch = 0; 149 150 msghdr.msg_control = &message; 151 msghdr.msg_controllen = sizeof(message); 152 153 iovec.iov_base = &ch; 154 iovec.iov_len = sizeof(ch); 155 156 msghdr.msg_iov = &iovec; 157 msghdr.msg_iovlen = 1; 158 159 iovec.iov_len = sizeof(ch); 160 161 msghdr.msg_iov = &iovec; 162 msghdr.msg_iovlen = 1; 163 164 message.cmsghdr.cmsg_len = sizeof(message); 165 message.cmsghdr.cmsg_level = SOL_SOCKET; 166 message.cmsghdr.cmsg_type = SCM_RIGHTS; 167 message.fd = -1; 168 169 len = recvmsg(sockfd, &msghdr, 0); 170 if (len < 0) 171 err(-1, "%s: recvmsg", test); 172 if (len != sizeof(ch)) 173 errx(-1, "%s: recvmsg: %d bytes received", test, len); 174 if (message.fd == -1) 175 errx(-1, "%s: recvmsg: received fd -1", test); 176 *recvfd = message.fd; 177} 178 179int 180main(int argc, char *argv[]) 181{ 182 struct stat putfd_1_stat, putfd_2_stat, getfd_1_stat, getfd_2_stat; 183 int fd[2], putfd_1, putfd_2, getfd_1, getfd_2; 184 const char *test; 185 186 /* 187 * First test: put a temporary file into a UNIX domain socket, then 188 * take it out and make sure it's the same file. First time around, 189 * don't close the reference after sending. 190 */ 191 test = "test1-simplesendfd"; 192 printf("beginning %s\n", test); 193 194 domainsocketpair(test, fd); 195 tempfile(test, &putfd_1); 196 dofstat(test, putfd_1, &putfd_1_stat); 197 sendfd(test, fd[0], putfd_1); 198 recvfd(test, fd[1], &getfd_1); 199 dofstat(test, getfd_1, &getfd_1_stat); 200 samefile(test, &putfd_1_stat, &getfd_1_stat); 201 close(putfd_1); 202 close(getfd_1); 203 closesocketpair(fd); 204 205 printf("%s passed\n", test); 206 207 /* 208 * Second test: same as first, only close the file reference after 209 * sending, so that the only reference is the descriptor in the UNIX 210 * domain socket buffer. 211 */ 212 test = "test2-sendandclose"; 213 printf("beginning %s\n", test); 214 215 domainsocketpair(test, fd); 216 tempfile(test, &putfd_1); 217 dofstat(test, putfd_1, &putfd_1_stat); 218 sendfd(test, fd[0], putfd_1); 219 close(putfd_1); 220 recvfd(test, fd[1], &getfd_1); 221 dofstat(test, getfd_1, &getfd_1_stat); 222 samefile(test, &putfd_1_stat, &getfd_1_stat); 223 close(getfd_1); 224 closesocketpair(fd); 225 226 printf("%s passed\n", test); 227 228 /* 229 * Third test: put a temporary file into a UNIX domain socket, then 230 * close both endpoints causing garbage collection to kick off. 231 */ 232 test = "test3-sendandcancel"; 233 printf("beginning %s\n", test); 234 235 domainsocketpair(test, fd); 236 tempfile(test, &putfd_1); 237 sendfd(test, fd[0], putfd_1); 238 close(putfd_1); 239 closesocketpair(fd); 240 241 printf("%s passed\n", test); 242 243 /* 244 * Send two files. Then receive them. Make sure they are returned 245 * in the right order, and both get there. 246 */ 247 248 test = "test4-twofile"; 249 printf("beginning %s\n", test); 250 251 domainsocketpair(test, fd); 252 tempfile(test, &putfd_1); 253 tempfile(test, &putfd_2); 254 dofstat(test, putfd_1, &putfd_1_stat); 255 dofstat(test, putfd_2, &putfd_2_stat); 256 sendfd(test, fd[0], putfd_1); 257 sendfd(test, fd[0], putfd_2); 258 close(putfd_1); 259 close(putfd_2); 260 recvfd(test, fd[1], &getfd_1); 261 recvfd(test, fd[1], &getfd_2); 262 dofstat(test, getfd_1, &getfd_1_stat); 263 dofstat(test, getfd_2, &getfd_2_stat); 264 samefile(test, &putfd_1_stat, &getfd_1_stat); 265 samefile(test, &putfd_2_stat, &getfd_2_stat); 266 close(getfd_1); 267 close(getfd_2); 268 closesocketpair(fd); 269 270 printf("%s passed\n", test); 271 272 /* 273 * Big bundling test. Send an endpoint of the UNIX domain socket 274 * over itself, closing the door behind it. 275 */ 276 277 test = "test5-bundle"; 278 printf("beginning %s\n", test); 279 280 domainsocketpair(test, fd); 281 282 sendfd(test, fd[0], fd[0]); 283 close(fd[0]); 284 recvfd(test, fd[1], &getfd_1); 285 close(getfd_1); 286 close(fd[1]); 287 288 printf("%s passed\n", test); 289 290 /* 291 * Big bundling test part two: Send an endpoint of the UNIX domain 292 * socket over itself, close the door behind it, and never remove it 293 * from the other end. 294 */ 295 296 test = "test6-bundlecancel"; 297 printf("beginning %s\n", test); 298 299 domainsocketpair(test, fd); 300 sendfd(test, fd[0], fd[0]); 301 sendfd(test, fd[1], fd[0]); 302 closesocketpair(fd); 303 304 printf("%s passed\n", test); 305 306 return (0); 307} 308