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: releng/10.2/tools/regression/sockets/unix_passfd/unix_passfd.c 281974 2015-04-25 05:31:52Z ngie $ 27152251Srwatson */ 28152251Srwatson 29152251Srwatson#include <sys/types.h> 30152251Srwatson#include <sys/socket.h> 31152251Srwatson#include <sys/stat.h> 32281974Sngie#include <sys/sysctl.h> 33281974Sngie#include <sys/un.h> 34152251Srwatson 35152251Srwatson#include <err.h> 36228371Sjhb#include <fcntl.h> 37152251Srwatson#include <limits.h> 38152251Srwatson#include <stdio.h> 39281974Sngie#include <stdlib.h> 40152251Srwatson#include <string.h> 41152251Srwatson#include <unistd.h> 42152251Srwatson 43152251Srwatson/* 44152251Srwatson * UNIX domain sockets allow file descriptors to be passed via "ancillary 45152251Srwatson * data", or control messages. This regression test is intended to exercise 46152251Srwatson * this facility, both performing some basic tests that it operates, and also 47152251Srwatson * causing some kernel edge cases to execute, such as garbage collection when 48152251Srwatson * there are cyclic file descriptor references. Right now we test only with 49152251Srwatson * stream sockets, but ideally we'd also test with datagram sockets. 50152251Srwatson */ 51152251Srwatson 52152251Srwatsonstatic void 53152251Srwatsondomainsocketpair(const char *test, int *fdp) 54152251Srwatson{ 55152251Srwatson 56152251Srwatson if (socketpair(PF_UNIX, SOCK_STREAM, 0, fdp) < 0) 57152251Srwatson err(-1, "%s: socketpair(PF_UNIX, SOCK_STREAM)", test); 58152251Srwatson} 59152251Srwatson 60152251Srwatsonstatic void 61152251Srwatsonclosesocketpair(int *fdp) 62152251Srwatson{ 63152251Srwatson 64152251Srwatson close(fdp[0]); 65152251Srwatson close(fdp[1]); 66152251Srwatson} 67152251Srwatson 68152251Srwatsonstatic void 69228371Sjhbdevnull(const char *test, int *fdp) 70228371Sjhb{ 71228371Sjhb int fd; 72228371Sjhb 73228371Sjhb fd = open("/dev/null", O_RDONLY); 74228371Sjhb if (fd < 0) 75228371Sjhb err(-1, "%s: open(/dev/null)", test); 76228371Sjhb *fdp = fd; 77228371Sjhb} 78228371Sjhb 79228371Sjhbstatic void 80152251Srwatsontempfile(const char *test, int *fdp) 81152251Srwatson{ 82152251Srwatson char path[PATH_MAX]; 83152251Srwatson int fd; 84152251Srwatson 85152251Srwatson snprintf(path, PATH_MAX, "/tmp/unix_passfd.XXXXXXXXXXXXXXX"); 86152251Srwatson fd = mkstemp(path); 87152251Srwatson if (fd < 0) 88152251Srwatson err(-1, "%s: mkstemp(%s)", test, path); 89152251Srwatson (void)unlink(path); 90152251Srwatson *fdp = fd; 91152251Srwatson} 92152251Srwatson 93152251Srwatsonstatic void 94152251Srwatsondofstat(const char *test, int fd, struct stat *sb) 95152251Srwatson{ 96152251Srwatson 97152251Srwatson if (fstat(fd, sb) < 0) 98152251Srwatson err(-1, "%s: fstat", test); 99152251Srwatson} 100152251Srwatson 101152251Srwatsonstatic void 102152251Srwatsonsamefile(const char *test, struct stat *sb1, struct stat *sb2) 103152251Srwatson{ 104152251Srwatson 105152251Srwatson if (sb1->st_dev != sb2->st_dev) 106228371Sjhb errx(-1, "%s: samefile: different device", test); 107152251Srwatson if (sb1->st_ino != sb2->st_ino) 108228371Sjhb errx(-1, "%s: samefile: different inode", test); 109152251Srwatson} 110152251Srwatson 111152251Srwatsonstatic void 112281974Sngiesendfd_payload(const char *test, int sockfd, int sendfd, 113281974Sngie void *payload, size_t paylen) 114152251Srwatson{ 115152251Srwatson struct iovec iovec; 116228371Sjhb char message[CMSG_SPACE(sizeof(int))]; 117228371Sjhb struct cmsghdr *cmsghdr; 118152251Srwatson struct msghdr msghdr; 119152251Srwatson ssize_t len; 120152251Srwatson 121152251Srwatson bzero(&msghdr, sizeof(msghdr)); 122152251Srwatson bzero(&message, sizeof(message)); 123152251Srwatson 124228371Sjhb msghdr.msg_control = message; 125152251Srwatson msghdr.msg_controllen = sizeof(message); 126152251Srwatson 127281974Sngie iovec.iov_base = payload; 128281974Sngie iovec.iov_len = paylen; 129152251Srwatson 130152251Srwatson msghdr.msg_iov = &iovec; 131152251Srwatson msghdr.msg_iovlen = 1; 132152251Srwatson 133228371Sjhb cmsghdr = (struct cmsghdr *)message; 134228371Sjhb cmsghdr->cmsg_len = CMSG_LEN(sizeof(int)); 135228371Sjhb cmsghdr->cmsg_level = SOL_SOCKET; 136228371Sjhb cmsghdr->cmsg_type = SCM_RIGHTS; 137228371Sjhb *(int *)CMSG_DATA(cmsghdr) = sendfd; 138152251Srwatson 139152251Srwatson len = sendmsg(sockfd, &msghdr, 0); 140152251Srwatson if (len < 0) 141152251Srwatson err(-1, "%s: sendmsg", test); 142281974Sngie if ((size_t)len != paylen) 143228371Sjhb errx(-1, "%s: sendmsg: %zd bytes sent", test, len); 144152251Srwatson} 145152251Srwatson 146152251Srwatsonstatic void 147281974Sngiesendfd(const char *test, int sockfd, int sendfd) 148152251Srwatson{ 149281974Sngie char ch; 150281974Sngie 151281974Sngie return (sendfd_payload(test, sockfd, sendfd, &ch, sizeof(ch))); 152281974Sngie} 153281974Sngie 154281974Sngiestatic void 155281974Sngierecvfd_payload(const char *test, int sockfd, int *recvfd, 156281974Sngie void *buf, size_t buflen) 157281974Sngie{ 158228371Sjhb struct cmsghdr *cmsghdr; 159281974Sngie char message[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + sizeof(int)]; 160152251Srwatson struct msghdr msghdr; 161152251Srwatson struct iovec iovec; 162152251Srwatson ssize_t len; 163152251Srwatson 164152251Srwatson bzero(&msghdr, sizeof(msghdr)); 165152251Srwatson 166228371Sjhb msghdr.msg_control = message; 167152251Srwatson msghdr.msg_controllen = sizeof(message); 168152251Srwatson 169281974Sngie iovec.iov_base = buf; 170281974Sngie iovec.iov_len = buflen; 171152251Srwatson 172152251Srwatson msghdr.msg_iov = &iovec; 173152251Srwatson msghdr.msg_iovlen = 1; 174152251Srwatson 175152251Srwatson len = recvmsg(sockfd, &msghdr, 0); 176152251Srwatson if (len < 0) 177152251Srwatson err(-1, "%s: recvmsg", test); 178281974Sngie if ((size_t)len != buflen) 179228371Sjhb errx(-1, "%s: recvmsg: %zd bytes received", test, len); 180281974Sngie 181228371Sjhb cmsghdr = CMSG_FIRSTHDR(&msghdr); 182228371Sjhb if (cmsghdr == NULL) 183228371Sjhb errx(-1, "%s: recvmsg: did not receive control message", test); 184281974Sngie *recvfd = -1; 185281974Sngie for (; cmsghdr != NULL; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) { 186281974Sngie if (cmsghdr->cmsg_level == SOL_SOCKET && 187281974Sngie cmsghdr->cmsg_type == SCM_RIGHTS && 188281974Sngie cmsghdr->cmsg_len == CMSG_LEN(sizeof(int))) { 189281974Sngie *recvfd = *(int *)CMSG_DATA(cmsghdr); 190281974Sngie if (*recvfd == -1) 191281974Sngie errx(-1, "%s: recvmsg: received fd -1", test); 192281974Sngie } 193281974Sngie } 194281974Sngie if (*recvfd == -1) 195228371Sjhb errx(-1, "%s: recvmsg: did not receive single-fd message", 196228371Sjhb test); 197152251Srwatson} 198152251Srwatson 199281974Sngiestatic void 200281974Sngierecvfd(const char *test, int sockfd, int *recvfd) 201281974Sngie{ 202281974Sngie char ch; 203281974Sngie 204281974Sngie return (recvfd_payload(test, sockfd, recvfd, &ch, sizeof(ch))); 205281974Sngie} 206281974Sngie 207152251Srwatsonint 208281974Sngiemain(void) 209152251Srwatson{ 210152251Srwatson struct stat putfd_1_stat, putfd_2_stat, getfd_1_stat, getfd_2_stat; 211152251Srwatson int fd[2], putfd_1, putfd_2, getfd_1, getfd_2; 212152251Srwatson const char *test; 213152251Srwatson 214152251Srwatson /* 215152251Srwatson * First test: put a temporary file into a UNIX domain socket, then 216152251Srwatson * take it out and make sure it's the same file. First time around, 217152251Srwatson * don't close the reference after sending. 218152251Srwatson */ 219152251Srwatson test = "test1-simplesendfd"; 220152251Srwatson printf("beginning %s\n", test); 221152251Srwatson 222152251Srwatson domainsocketpair(test, fd); 223152251Srwatson tempfile(test, &putfd_1); 224152251Srwatson dofstat(test, putfd_1, &putfd_1_stat); 225152251Srwatson sendfd(test, fd[0], putfd_1); 226152251Srwatson recvfd(test, fd[1], &getfd_1); 227152251Srwatson dofstat(test, getfd_1, &getfd_1_stat); 228152251Srwatson samefile(test, &putfd_1_stat, &getfd_1_stat); 229152251Srwatson close(putfd_1); 230152251Srwatson close(getfd_1); 231152251Srwatson closesocketpair(fd); 232152251Srwatson 233152251Srwatson printf("%s passed\n", test); 234152251Srwatson 235152251Srwatson /* 236152251Srwatson * Second test: same as first, only close the file reference after 237152251Srwatson * sending, so that the only reference is the descriptor in the UNIX 238152251Srwatson * domain socket buffer. 239152251Srwatson */ 240152251Srwatson test = "test2-sendandclose"; 241152251Srwatson printf("beginning %s\n", test); 242152251Srwatson 243152251Srwatson domainsocketpair(test, fd); 244152251Srwatson tempfile(test, &putfd_1); 245152251Srwatson dofstat(test, putfd_1, &putfd_1_stat); 246152251Srwatson sendfd(test, fd[0], putfd_1); 247152251Srwatson close(putfd_1); 248152251Srwatson recvfd(test, fd[1], &getfd_1); 249152251Srwatson dofstat(test, getfd_1, &getfd_1_stat); 250152251Srwatson samefile(test, &putfd_1_stat, &getfd_1_stat); 251152251Srwatson close(getfd_1); 252152251Srwatson closesocketpair(fd); 253152251Srwatson 254152251Srwatson printf("%s passed\n", test); 255152251Srwatson 256152251Srwatson /* 257152251Srwatson * Third test: put a temporary file into a UNIX domain socket, then 258152251Srwatson * close both endpoints causing garbage collection to kick off. 259152251Srwatson */ 260152251Srwatson test = "test3-sendandcancel"; 261152251Srwatson printf("beginning %s\n", test); 262152251Srwatson 263152251Srwatson domainsocketpair(test, fd); 264152251Srwatson tempfile(test, &putfd_1); 265152251Srwatson sendfd(test, fd[0], putfd_1); 266152251Srwatson close(putfd_1); 267152251Srwatson closesocketpair(fd); 268152251Srwatson 269152251Srwatson printf("%s passed\n", test); 270152251Srwatson 271152251Srwatson /* 272152251Srwatson * Send two files. Then receive them. Make sure they are returned 273152251Srwatson * in the right order, and both get there. 274152251Srwatson */ 275152251Srwatson 276152251Srwatson test = "test4-twofile"; 277152251Srwatson printf("beginning %s\n", test); 278152251Srwatson 279152251Srwatson domainsocketpair(test, fd); 280152251Srwatson tempfile(test, &putfd_1); 281152251Srwatson tempfile(test, &putfd_2); 282152251Srwatson dofstat(test, putfd_1, &putfd_1_stat); 283152251Srwatson dofstat(test, putfd_2, &putfd_2_stat); 284152251Srwatson sendfd(test, fd[0], putfd_1); 285152251Srwatson sendfd(test, fd[0], putfd_2); 286152251Srwatson close(putfd_1); 287152251Srwatson close(putfd_2); 288152251Srwatson recvfd(test, fd[1], &getfd_1); 289152251Srwatson recvfd(test, fd[1], &getfd_2); 290152251Srwatson dofstat(test, getfd_1, &getfd_1_stat); 291152251Srwatson dofstat(test, getfd_2, &getfd_2_stat); 292152251Srwatson samefile(test, &putfd_1_stat, &getfd_1_stat); 293152251Srwatson samefile(test, &putfd_2_stat, &getfd_2_stat); 294152251Srwatson close(getfd_1); 295152251Srwatson close(getfd_2); 296152251Srwatson closesocketpair(fd); 297152251Srwatson 298152251Srwatson printf("%s passed\n", test); 299152251Srwatson 300152251Srwatson /* 301152251Srwatson * Big bundling test. Send an endpoint of the UNIX domain socket 302152251Srwatson * over itself, closing the door behind it. 303152251Srwatson */ 304152251Srwatson 305152251Srwatson test = "test5-bundle"; 306152251Srwatson printf("beginning %s\n", test); 307152251Srwatson 308152251Srwatson domainsocketpair(test, fd); 309152251Srwatson 310152251Srwatson sendfd(test, fd[0], fd[0]); 311152251Srwatson close(fd[0]); 312152251Srwatson recvfd(test, fd[1], &getfd_1); 313152251Srwatson close(getfd_1); 314152251Srwatson close(fd[1]); 315152251Srwatson 316152251Srwatson printf("%s passed\n", test); 317152251Srwatson 318152251Srwatson /* 319152251Srwatson * Big bundling test part two: Send an endpoint of the UNIX domain 320152251Srwatson * socket over itself, close the door behind it, and never remove it 321152251Srwatson * from the other end. 322152251Srwatson */ 323152251Srwatson 324152251Srwatson test = "test6-bundlecancel"; 325152251Srwatson printf("beginning %s\n", test); 326152251Srwatson 327152251Srwatson domainsocketpair(test, fd); 328152251Srwatson sendfd(test, fd[0], fd[0]); 329152251Srwatson sendfd(test, fd[1], fd[0]); 330152251Srwatson closesocketpair(fd); 331152251Srwatson 332152251Srwatson printf("%s passed\n", test); 333152251Srwatson 334228371Sjhb /* 335228371Sjhb * Test for PR 151758: Send an character device over the UNIX 336228371Sjhb * domain socket and then close both sockets to orphan the 337228371Sjhb * device. 338228371Sjhb */ 339228371Sjhb 340228371Sjhb test = "test7-devfsorphan"; 341228371Sjhb printf("beginning %s\n", test); 342228371Sjhb 343228371Sjhb domainsocketpair(test, fd); 344228371Sjhb devnull(test, &putfd_1); 345228371Sjhb sendfd(test, fd[0], putfd_1); 346228371Sjhb close(putfd_1); 347228371Sjhb closesocketpair(fd); 348228371Sjhb 349228371Sjhb printf("%s passed\n", test); 350281974Sngie 351281974Sngie /* 352281974Sngie * Test for PR 181741. Receiver sets LOCAL_CREDS, and kernel 353281974Sngie * prepends a control message to the data. Sender sends large 354281974Sngie * payload. Payload + SCM_RIGHTS + LOCAL_CREDS hit socket buffer 355281974Sngie * limit, and receiver receives truncated data. 356281974Sngie */ 357281974Sngie test = "test8-rights+creds+payload"; 358281974Sngie printf("beginning %s\n", test); 359281974Sngie 360281974Sngie { 361281974Sngie const int on = 1; 362281974Sngie u_long sendspace; 363281974Sngie size_t len; 364281974Sngie void *buf; 365281974Sngie 366281974Sngie len = sizeof(sendspace); 367281974Sngie if (sysctlbyname("net.local.stream.sendspace", &sendspace, 368281974Sngie &len, NULL, 0) < 0) 369281974Sngie err(-1, "%s: sysctlbyname(net.local.stream.sendspace)", 370281974Sngie test); 371281974Sngie 372281974Sngie if ((buf = malloc(sendspace)) == NULL) 373281974Sngie err(-1, "%s: malloc", test); 374281974Sngie 375281974Sngie domainsocketpair(test, fd); 376281974Sngie if (setsockopt(fd[1], 0, LOCAL_CREDS, &on, sizeof(on)) < 0) 377281974Sngie err(-1, "%s: setsockopt(LOCAL_CREDS)", test); 378281974Sngie tempfile(test, &putfd_1); 379281974Sngie sendfd_payload(test, fd[0], putfd_1, buf, sendspace); 380281974Sngie recvfd_payload(test, fd[1], &getfd_1, buf, sendspace); 381281974Sngie close(putfd_1); 382281974Sngie close(getfd_1); 383281974Sngie closesocketpair(fd); 384281974Sngie } 385281974Sngie 386281974Sngie printf("%s passed\n", test); 387228371Sjhb 388152251Srwatson return (0); 389152251Srwatson} 390