1/*++ 2/* NAME 3/* unix_recv_fd 3 4/* SUMMARY 5/* receive file descriptor 6/* SYNOPSIS 7/* #include <iostuff.h> 8/* 9/* int unix_recv_fd(fd) 10/* int fd; 11/* DESCRIPTION 12/* unix_recv_fd() receives a file descriptor via the specified 13/* UNIX-domain socket. The result value is the received descriptor. 14/* 15/* Arguments: 16/* .IP fd 17/* File descriptor that connects the sending and receiving processes. 18/* DIAGNOSTICS 19/* unix_recv_fd() returns -1 upon failure. 20/* LICENSE 21/* .ad 22/* .fi 23/* The Secure Mailer license must be distributed with this software. 24/* AUTHOR(S) 25/* Wietse Venema 26/* IBM T.J. Watson Research 27/* P.O. Box 704 28/* Yorktown Heights, NY 10598, USA 29/*--*/ 30 31/* System library. */ 32 33#include <sys_defs.h> /* includes <sys/types.h> */ 34#include <sys/socket.h> 35#include <sys/uio.h> 36#include <string.h> 37 38/* Utility library. */ 39 40#include <msg.h> 41#include <iostuff.h> 42 43/* unix_recv_fd - receive file descriptor */ 44 45int unix_recv_fd(int fd) 46{ 47 const char *myname = "unix_recv_fd"; 48 49 /* 50 * This code does not work with version <2.2 Linux kernels, and it does 51 * not compile with version <2 Linux libraries. 52 */ 53#ifdef CANT_USE_SEND_RECV_MSG 54 msg_warn("%s: your system has no support for file descriptor passing", 55 myname); 56 return (-1); 57#else 58 struct msghdr msg; 59 int newfd; 60 struct iovec iov[1]; 61 char buf[1]; 62 63 /* 64 * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1, 65 * Second edition. Except that we use CMSG_LEN instead of CMSG_SPACE, for 66 * portability to some LP64 environments. See also unix_send_fd.c. 67 */ 68#if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL) 69 union { 70 struct cmsghdr just_for_alignment; 71 char control[CMSG_SPACE(sizeof(newfd))]; 72 } control_un; 73 struct cmsghdr *cmptr; 74 75 memset((char *) &msg, 0, sizeof(msg)); /* Fix 200512 */ 76 msg.msg_control = control_un.control; 77 if (unix_pass_fd_fix & UNIX_PASS_FD_FIX_CMSG_LEN) { 78 msg.msg_controllen = CMSG_LEN(sizeof(newfd)); /* Fix 200506 */ 79 } else { 80 msg.msg_controllen = sizeof(control_un.control); /* normal */ 81 } 82#else 83 msg.msg_accrights = (char *) &newfd; 84 msg.msg_accrightslen = sizeof(newfd); 85#endif 86 87 msg.msg_name = 0; 88 msg.msg_namelen = 0; 89 90 /* 91 * XXX We don't want to pass any data, just a file descriptor. However, 92 * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble: we need 93 * to read_wait() before we can receive the descriptor, and the code 94 * fails after the first descriptor when we attempt to receive a sequence 95 * of descriptors. 96 */ 97 iov->iov_base = buf; 98 iov->iov_len = sizeof(buf); 99 msg.msg_iov = iov; 100 msg.msg_iovlen = 1; 101 102 if (recvmsg(fd, &msg, 0) < 0) 103 return (-1); 104 105#if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL) 106 if ((cmptr = CMSG_FIRSTHDR(&msg)) != 0 107 && cmptr->cmsg_len == CMSG_LEN(sizeof(newfd))) { 108 if (cmptr->cmsg_level != SOL_SOCKET) 109 msg_fatal("%s: control level %d != SOL_SOCKET", 110 myname, cmptr->cmsg_level); 111 if (cmptr->cmsg_type != SCM_RIGHTS) 112 msg_fatal("%s: control type %d != SCM_RIGHTS", 113 myname, cmptr->cmsg_type); 114 return (*(int *) CMSG_DATA(cmptr)); 115 } else 116 return (-1); 117#else 118 if (msg.msg_accrightslen == sizeof(newfd)) 119 return (newfd); 120 else 121 return (-1); 122#endif 123#endif 124} 125 126#ifdef TEST 127 128 /* 129 * Proof-of-concept program. Receive a descriptor (presumably from the 130 * unix_send_fd test program) and copy its content until EOF. 131 */ 132#include <unistd.h> 133#include <string.h> 134#include <stdlib.h> 135#include <split_at.h> 136#include <listen.h> 137 138int main(int argc, char **argv) 139{ 140 char *transport; 141 char *endpoint; 142 int listen_sock; 143 int client_sock; 144 int client_fd; 145 ssize_t read_count; 146 char buf[1024]; 147 148 if (argc < 2 || argc > 3 149 || (endpoint = split_at(transport = argv[1], ':')) == 0 150 || *endpoint == 0 || *transport == 0) 151 msg_fatal("usage: %s transport:endpoint [workaround]", argv[0]); 152 153 if (strcmp(transport, "unix") == 0) { 154 listen_sock = unix_listen(endpoint, 10, BLOCKING); 155 } else { 156 msg_fatal("invalid transport name: %s", transport); 157 } 158 if (listen_sock < 0) 159 msg_fatal("listen %s:%s: %m", transport, endpoint); 160 161 client_sock = accept(listen_sock, (struct sockaddr *) 0, (SOCKADDR_SIZE) 0); 162 if (client_sock < 0) 163 msg_fatal("accept: %m"); 164 165 set_unix_pass_fd_fix(argv[2] ? argv[2] : ""); 166 167 while ((client_fd = unix_recv_fd(client_sock)) >= 0) { 168 msg_info("client_fd = %d, fix=%d", client_fd, unix_pass_fd_fix); 169 while ((read_count = read(client_fd, buf, sizeof(buf))) > 0) 170 write(1, buf, read_count); 171 if (read_count < 0) 172 msg_fatal("read: %m"); 173 close(client_fd); 174 } 175 exit(0); 176} 177 178#endif 179