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