1152251Srwatson/*- 2152251Srwatson * Copyright (c) 2005 Robert N. M. Watson 3152251Srwatson * All rights reserved. 4152251Srwatson * 5152251Srwatson * Redistribution and use in source and binary forms, with or without 6152251Srwatson * modification, are permitted provided that the following conditions 7152251Srwatson * are met: 8152251Srwatson * 1. Redistributions of source code must retain the above copyright 9152251Srwatson * notice, this list of conditions and the following disclaimer. 10152251Srwatson * 2. Redistributions in binary form must reproduce the above copyright 11152251Srwatson * notice, this list of conditions and the following disclaimer in the 12152251Srwatson * documentation and/or other materials provided with the distribution. 13152251Srwatson * 14152251Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15152251Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16152251Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17152251Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18152251Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19152251Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20152251Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21152251Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22152251Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23152251Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24152251Srwatson * SUCH DAMAGE. 25152251Srwatson * 26152251Srwatson * $FreeBSD$ 27152251Srwatson */ 28152251Srwatson 29152251Srwatson#include <sys/types.h> 30152251Srwatson#include <sys/socket.h> 31152251Srwatson#include <sys/stat.h> 32152251Srwatson 33152251Srwatson#include <err.h> 34152251Srwatson#include <limits.h> 35152251Srwatson#include <stdio.h> 36152251Srwatson#include <string.h> 37152251Srwatson#include <unistd.h> 38152251Srwatson 39152251Srwatson/* 40152251Srwatson * UNIX domain sockets allow file descriptors to be passed via "ancillary 41152251Srwatson * data", or control messages. This regression test is intended to exercise 42152251Srwatson * this facility, both performing some basic tests that it operates, and also 43152251Srwatson * causing some kernel edge cases to execute, such as garbage collection when 44152251Srwatson * there are cyclic file descriptor references. Right now we test only with 45152251Srwatson * stream sockets, but ideally we'd also test with datagram sockets. 46152251Srwatson */ 47152251Srwatson 48152251Srwatsonstatic void 49152251Srwatsondomainsocketpair(const char *test, int *fdp) 50152251Srwatson{ 51152251Srwatson 52152251Srwatson if (socketpair(PF_UNIX, SOCK_STREAM, 0, fdp) < 0) 53152251Srwatson err(-1, "%s: socketpair(PF_UNIX, SOCK_STREAM)", test); 54152251Srwatson} 55152251Srwatson 56152251Srwatsonstatic void 57152251Srwatsonclosesocketpair(int *fdp) 58152251Srwatson{ 59152251Srwatson 60152251Srwatson close(fdp[0]); 61152251Srwatson close(fdp[1]); 62152251Srwatson} 63152251Srwatson 64152251Srwatsonstatic void 65152251Srwatsontempfile(const char *test, int *fdp) 66152251Srwatson{ 67152251Srwatson char path[PATH_MAX]; 68152251Srwatson int fd; 69152251Srwatson 70152251Srwatson snprintf(path, PATH_MAX, "/tmp/unix_passfd.XXXXXXXXXXXXXXX"); 71152251Srwatson fd = mkstemp(path); 72152251Srwatson if (fd < 0) 73152251Srwatson err(-1, "%s: mkstemp(%s)", test, path); 74152251Srwatson (void)unlink(path); 75152251Srwatson *fdp = fd; 76152251Srwatson} 77152251Srwatson 78152251Srwatsonstatic void 79152251Srwatsondofstat(const char *test, int fd, struct stat *sb) 80152251Srwatson{ 81152251Srwatson 82152251Srwatson if (fstat(fd, sb) < 0) 83152251Srwatson err(-1, "%s: fstat", test); 84152251Srwatson} 85152251Srwatson 86152251Srwatsonstatic void 87152251Srwatsonsamefile(const char *test, struct stat *sb1, struct stat *sb2) 88152251Srwatson{ 89152251Srwatson 90152251Srwatson if (sb1->st_dev != sb2->st_dev) 91152251Srwatson err(-1, "%s: samefile: different device", test); 92152251Srwatson if (sb1->st_ino != sb2->st_ino) 93152251Srwatson err(-1, "%s: samefile: different inode", test); 94152251Srwatson} 95152251Srwatson 96152251Srwatsonstatic void 97152251Srwatsonsendfd(const char *test, int sockfd, int sendfd) 98152251Srwatson{ 99152251Srwatson struct iovec iovec; 100152251Srwatson char ch; 101152251Srwatson 102152251Srwatson struct { 103152251Srwatson struct cmsghdr cmsghdr; 104152251Srwatson int fd; 105152251Srwatson } message; 106152251Srwatson struct msghdr msghdr; 107152251Srwatson ssize_t len; 108152251Srwatson 109152251Srwatson bzero(&msghdr, sizeof(msghdr)); 110152251Srwatson bzero(&message, sizeof(message)); 111152251Srwatson ch = 0; 112152251Srwatson 113152251Srwatson msghdr.msg_control = &message; 114152251Srwatson msghdr.msg_controllen = sizeof(message); 115152251Srwatson 116152251Srwatson iovec.iov_base = &ch; 117152251Srwatson iovec.iov_len = sizeof(ch); 118152251Srwatson 119152251Srwatson msghdr.msg_iov = &iovec; 120152251Srwatson msghdr.msg_iovlen = 1; 121152251Srwatson 122152251Srwatson message.cmsghdr.cmsg_len = sizeof(message); 123152251Srwatson message.cmsghdr.cmsg_level = SOL_SOCKET; 124152251Srwatson message.cmsghdr.cmsg_type = SCM_RIGHTS; 125152251Srwatson message.fd = sendfd; 126152251Srwatson 127152251Srwatson len = sendmsg(sockfd, &msghdr, 0); 128152251Srwatson if (len < 0) 129152251Srwatson err(-1, "%s: sendmsg", test); 130152251Srwatson if (len != sizeof(ch)) 131152251Srwatson errx(-1, "%s: sendmsg: %d bytes sent", test, len); 132152251Srwatson} 133152251Srwatson 134152251Srwatsonstatic void 135152251Srwatsonrecvfd(const char *test, int sockfd, int *recvfd) 136152251Srwatson{ 137152251Srwatson struct { 138152251Srwatson struct cmsghdr cmsghdr; 139152251Srwatson int fd; 140152251Srwatson } message; 141152251Srwatson struct msghdr msghdr; 142152251Srwatson struct iovec iovec; 143152251Srwatson ssize_t len; 144152251Srwatson char ch; 145152251Srwatson 146152251Srwatson bzero(&msghdr, sizeof(msghdr)); 147152251Srwatson bzero(&message, sizeof(message)); 148152251Srwatson ch = 0; 149152251Srwatson 150152251Srwatson msghdr.msg_control = &message; 151152251Srwatson msghdr.msg_controllen = sizeof(message); 152152251Srwatson 153152251Srwatson iovec.iov_base = &ch; 154152251Srwatson iovec.iov_len = sizeof(ch); 155152251Srwatson 156152251Srwatson msghdr.msg_iov = &iovec; 157152251Srwatson msghdr.msg_iovlen = 1; 158152251Srwatson 159152251Srwatson iovec.iov_len = sizeof(ch); 160152251Srwatson 161152251Srwatson msghdr.msg_iov = &iovec; 162152251Srwatson msghdr.msg_iovlen = 1; 163152251Srwatson 164152251Srwatson message.cmsghdr.cmsg_len = sizeof(message); 165152251Srwatson message.cmsghdr.cmsg_level = SOL_SOCKET; 166152251Srwatson message.cmsghdr.cmsg_type = SCM_RIGHTS; 167152251Srwatson message.fd = -1; 168152251Srwatson 169152251Srwatson len = recvmsg(sockfd, &msghdr, 0); 170152251Srwatson if (len < 0) 171152251Srwatson err(-1, "%s: recvmsg", test); 172152251Srwatson if (len != sizeof(ch)) 173152251Srwatson errx(-1, "%s: recvmsg: %d bytes received", test, len); 174152251Srwatson if (message.fd == -1) 175152251Srwatson errx(-1, "%s: recvmsg: received fd -1", test); 176152251Srwatson *recvfd = message.fd; 177152251Srwatson} 178152251Srwatson 179152251Srwatsonint 180152251Srwatsonmain(int argc, char *argv[]) 181152251Srwatson{ 182152251Srwatson struct stat putfd_1_stat, putfd_2_stat, getfd_1_stat, getfd_2_stat; 183152251Srwatson int fd[2], putfd_1, putfd_2, getfd_1, getfd_2; 184152251Srwatson const char *test; 185152251Srwatson 186152251Srwatson /* 187152251Srwatson * First test: put a temporary file into a UNIX domain socket, then 188152251Srwatson * take it out and make sure it's the same file. First time around, 189152251Srwatson * don't close the reference after sending. 190152251Srwatson */ 191152251Srwatson test = "test1-simplesendfd"; 192152251Srwatson printf("beginning %s\n", test); 193152251Srwatson 194152251Srwatson domainsocketpair(test, fd); 195152251Srwatson tempfile(test, &putfd_1); 196152251Srwatson dofstat(test, putfd_1, &putfd_1_stat); 197152251Srwatson sendfd(test, fd[0], putfd_1); 198152251Srwatson recvfd(test, fd[1], &getfd_1); 199152251Srwatson dofstat(test, getfd_1, &getfd_1_stat); 200152251Srwatson samefile(test, &putfd_1_stat, &getfd_1_stat); 201152251Srwatson close(putfd_1); 202152251Srwatson close(getfd_1); 203152251Srwatson closesocketpair(fd); 204152251Srwatson 205152251Srwatson printf("%s passed\n", test); 206152251Srwatson 207152251Srwatson /* 208152251Srwatson * Second test: same as first, only close the file reference after 209152251Srwatson * sending, so that the only reference is the descriptor in the UNIX 210152251Srwatson * domain socket buffer. 211152251Srwatson */ 212152251Srwatson test = "test2-sendandclose"; 213152251Srwatson printf("beginning %s\n", test); 214152251Srwatson 215152251Srwatson domainsocketpair(test, fd); 216152251Srwatson tempfile(test, &putfd_1); 217152251Srwatson dofstat(test, putfd_1, &putfd_1_stat); 218152251Srwatson sendfd(test, fd[0], putfd_1); 219152251Srwatson close(putfd_1); 220152251Srwatson recvfd(test, fd[1], &getfd_1); 221152251Srwatson dofstat(test, getfd_1, &getfd_1_stat); 222152251Srwatson samefile(test, &putfd_1_stat, &getfd_1_stat); 223152251Srwatson close(getfd_1); 224152251Srwatson closesocketpair(fd); 225152251Srwatson 226152251Srwatson printf("%s passed\n", test); 227152251Srwatson 228152251Srwatson /* 229152251Srwatson * Third test: put a temporary file into a UNIX domain socket, then 230152251Srwatson * close both endpoints causing garbage collection to kick off. 231152251Srwatson */ 232152251Srwatson test = "test3-sendandcancel"; 233152251Srwatson printf("beginning %s\n", test); 234152251Srwatson 235152251Srwatson domainsocketpair(test, fd); 236152251Srwatson tempfile(test, &putfd_1); 237152251Srwatson sendfd(test, fd[0], putfd_1); 238152251Srwatson close(putfd_1); 239152251Srwatson closesocketpair(fd); 240152251Srwatson 241152251Srwatson printf("%s passed\n", test); 242152251Srwatson 243152251Srwatson /* 244152251Srwatson * Send two files. Then receive them. Make sure they are returned 245152251Srwatson * in the right order, and both get there. 246152251Srwatson */ 247152251Srwatson 248152251Srwatson test = "test4-twofile"; 249152251Srwatson printf("beginning %s\n", test); 250152251Srwatson 251152251Srwatson domainsocketpair(test, fd); 252152251Srwatson tempfile(test, &putfd_1); 253152251Srwatson tempfile(test, &putfd_2); 254152251Srwatson dofstat(test, putfd_1, &putfd_1_stat); 255152251Srwatson dofstat(test, putfd_2, &putfd_2_stat); 256152251Srwatson sendfd(test, fd[0], putfd_1); 257152251Srwatson sendfd(test, fd[0], putfd_2); 258152251Srwatson close(putfd_1); 259152251Srwatson close(putfd_2); 260152251Srwatson recvfd(test, fd[1], &getfd_1); 261152251Srwatson recvfd(test, fd[1], &getfd_2); 262152251Srwatson dofstat(test, getfd_1, &getfd_1_stat); 263152251Srwatson dofstat(test, getfd_2, &getfd_2_stat); 264152251Srwatson samefile(test, &putfd_1_stat, &getfd_1_stat); 265152251Srwatson samefile(test, &putfd_2_stat, &getfd_2_stat); 266152251Srwatson close(getfd_1); 267152251Srwatson close(getfd_2); 268152251Srwatson closesocketpair(fd); 269152251Srwatson 270152251Srwatson printf("%s passed\n", test); 271152251Srwatson 272152251Srwatson /* 273152251Srwatson * Big bundling test. Send an endpoint of the UNIX domain socket 274152251Srwatson * over itself, closing the door behind it. 275152251Srwatson */ 276152251Srwatson 277152251Srwatson test = "test5-bundle"; 278152251Srwatson printf("beginning %s\n", test); 279152251Srwatson 280152251Srwatson domainsocketpair(test, fd); 281152251Srwatson 282152251Srwatson sendfd(test, fd[0], fd[0]); 283152251Srwatson close(fd[0]); 284152251Srwatson recvfd(test, fd[1], &getfd_1); 285152251Srwatson close(getfd_1); 286152251Srwatson close(fd[1]); 287152251Srwatson 288152251Srwatson printf("%s passed\n", test); 289152251Srwatson 290152251Srwatson /* 291152251Srwatson * Big bundling test part two: Send an endpoint of the UNIX domain 292152251Srwatson * socket over itself, close the door behind it, and never remove it 293152251Srwatson * from the other end. 294152251Srwatson */ 295152251Srwatson 296152251Srwatson test = "test6-bundlecancel"; 297152251Srwatson printf("beginning %s\n", test); 298152251Srwatson 299152251Srwatson domainsocketpair(test, fd); 300152251Srwatson sendfd(test, fd[0], fd[0]); 301152251Srwatson sendfd(test, fd[1], fd[0]); 302152251Srwatson closesocketpair(fd); 303152251Srwatson 304152251Srwatson printf("%s passed\n", test); 305152251Srwatson 306152251Srwatson return (0); 307152251Srwatson} 308