1 /* $NetBSD: unix_recv_fd.c,v 1.8 2017/02/14 01:16:49 christos 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 char control[CMSG_SPACE(sizeof(newfd))]; 74 } control_un; 75 struct cmsghdr *cmptr; 76 77 memset((void *) &msg, 0, sizeof(msg)); /* Fix 200512 */ 78 msg.msg_control = control_un.control; 79 if (unix_pass_fd_fix & UNIX_PASS_FD_FIX_CMSG_LEN) { 80 msg.msg_controllen = CMSG_LEN(sizeof(newfd)); /* Fix 200506 */ 81 } else { 82 msg.msg_controllen = CMSG_SPACE(sizeof(newfd)); /* normal */ 83 } 84 #else 85 msg.msg_accrights = (char *) &newfd; 86 msg.msg_accrightslen = sizeof(newfd); 87 #endif 88 89 msg.msg_name = 0; 90 msg.msg_namelen = 0; 91 92 /* 93 * XXX We don't want to pass any data, just a file descriptor. However, 94 * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble: we need 95 * to read_wait() before we can receive the descriptor, and the code 96 * fails after the first descriptor when we attempt to receive a sequence 97 * of descriptors. 98 */ 99 iov->iov_base = buf; 100 iov->iov_len = sizeof(buf); 101 msg.msg_iov = iov; 102 msg.msg_iovlen = 1; 103 104 if (recvmsg(fd, &msg, 0) < 0) 105 return (-1); 106 107 #if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL) 108 if ((cmptr = CMSG_FIRSTHDR(&msg)) != 0 109 && cmptr->cmsg_len == CMSG_LEN(sizeof(newfd))) { 110 if (cmptr->cmsg_level != SOL_SOCKET) 111 msg_fatal("%s: control level %d != SOL_SOCKET", 112 myname, cmptr->cmsg_level); 113 if (cmptr->cmsg_type != SCM_RIGHTS) 114 msg_fatal("%s: control type %d != SCM_RIGHTS", 115 myname, cmptr->cmsg_type); 116 return (*(int *) CMSG_DATA(cmptr)); 117 } else 118 return (-1); 119 #else 120 if (msg.msg_accrightslen == sizeof(newfd)) 121 return (newfd); 122 else 123 return (-1); 124 #endif 125 #endif 126 } 127 128 #ifdef TEST 129 130 /* 131 * Proof-of-concept program. Receive a descriptor (presumably from the 132 * unix_send_fd test program) and copy its content until EOF. 133 */ 134 #include <unistd.h> 135 #include <string.h> 136 #include <stdlib.h> 137 #include <split_at.h> 138 #include <listen.h> 139 140 int main(int argc, char **argv) 141 { 142 char *transport; 143 char *endpoint; 144 int listen_sock; 145 int client_sock; 146 int client_fd; 147 ssize_t read_count; 148 char buf[1024]; 149 150 if (argc < 2 || argc > 3 151 || (endpoint = split_at(transport = argv[1], ':')) == 0 152 || *endpoint == 0 || *transport == 0) 153 msg_fatal("usage: %s transport:endpoint [workaround]", argv[0]); 154 155 if (strcmp(transport, "unix") == 0) { 156 listen_sock = unix_listen(endpoint, 10, BLOCKING); 157 } else { 158 msg_fatal("invalid transport name: %s", transport); 159 } 160 if (listen_sock < 0) 161 msg_fatal("listen %s:%s: %m", transport, endpoint); 162 163 client_sock = accept(listen_sock, (struct sockaddr *) 0, (SOCKADDR_SIZE) 0); 164 if (client_sock < 0) 165 msg_fatal("accept: %m"); 166 167 set_unix_pass_fd_fix(argv[2] ? argv[2] : ""); 168 169 while ((client_fd = unix_recv_fd(client_sock)) >= 0) { 170 msg_info("client_fd = %d, fix=%d", client_fd, unix_pass_fd_fix); 171 while ((read_count = read(client_fd, buf, sizeof(buf))) > 0) 172 write(1, buf, read_count); 173 if (read_count < 0) 174 msg_fatal("read: %m"); 175 close(client_fd); 176 } 177 exit(0); 178 } 179 180 #endif 181