diff options
Diffstat (limited to '')
-rw-r--r-- | src/util/unix_recv_fd.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/util/unix_recv_fd.c b/src/util/unix_recv_fd.c new file mode 100644 index 0000000..3410cff --- /dev/null +++ b/src/util/unix_recv_fd.c @@ -0,0 +1,178 @@ +/*++ +/* NAME +/* unix_recv_fd 3 +/* SUMMARY +/* receive file descriptor +/* SYNOPSIS +/* #include <iostuff.h> +/* +/* int unix_recv_fd(fd) +/* int fd; +/* DESCRIPTION +/* unix_recv_fd() receives a file descriptor via the specified +/* UNIX-domain socket. The result value is the received descriptor. +/* +/* Arguments: +/* .IP fd +/* File descriptor that connects the sending and receiving processes. +/* DIAGNOSTICS +/* unix_recv_fd() returns -1 upon failure. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> /* includes <sys/types.h> */ +#include <sys/socket.h> +#include <sys/uio.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <iostuff.h> + +/* unix_recv_fd - receive file descriptor */ + +int unix_recv_fd(int fd) +{ + const char *myname = "unix_recv_fd"; + + /* + * This code does not work with version <2.2 Linux kernels, and it does + * not compile with version <2 Linux libraries. + */ +#ifdef CANT_USE_SEND_RECV_MSG + msg_warn("%s: your system has no support for file descriptor passing", + myname); + return (-1); +#else + struct msghdr msg; + int newfd; + struct iovec iov[1]; + char buf[1]; + + /* + * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1, + * Second edition. Except that we use CMSG_LEN instead of CMSG_SPACE, for + * portability to some LP64 environments. See also unix_send_fd.c. + */ +#if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL) + union { + struct cmsghdr just_for_alignment; + char control[CMSG_SPACE(sizeof(newfd))]; + } control_un; + struct cmsghdr *cmptr; + + memset((void *) &msg, 0, sizeof(msg)); /* Fix 200512 */ + msg.msg_control = control_un.control; + if (unix_pass_fd_fix & UNIX_PASS_FD_FIX_CMSG_LEN) { + msg.msg_controllen = CMSG_LEN(sizeof(newfd)); /* Fix 200506 */ + } else { + msg.msg_controllen = sizeof(control_un.control); /* normal */ + } +#else + msg.msg_accrights = (char *) &newfd; + msg.msg_accrightslen = sizeof(newfd); +#endif + + msg.msg_name = 0; + msg.msg_namelen = 0; + + /* + * XXX We don't want to pass any data, just a file descriptor. However, + * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble: we need + * to read_wait() before we can receive the descriptor, and the code + * fails after the first descriptor when we attempt to receive a sequence + * of descriptors. + */ + iov->iov_base = buf; + iov->iov_len = sizeof(buf); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if (recvmsg(fd, &msg, 0) < 0) + return (-1); + +#if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL) + if ((cmptr = CMSG_FIRSTHDR(&msg)) != 0 + && cmptr->cmsg_len == CMSG_LEN(sizeof(newfd))) { + if (cmptr->cmsg_level != SOL_SOCKET) + msg_fatal("%s: control level %d != SOL_SOCKET", + myname, cmptr->cmsg_level); + if (cmptr->cmsg_type != SCM_RIGHTS) + msg_fatal("%s: control type %d != SCM_RIGHTS", + myname, cmptr->cmsg_type); + return (*(int *) CMSG_DATA(cmptr)); + } else + return (-1); +#else + if (msg.msg_accrightslen == sizeof(newfd)) + return (newfd); + else + return (-1); +#endif +#endif +} + +#ifdef TEST + + /* + * Proof-of-concept program. Receive a descriptor (presumably from the + * unix_send_fd test program) and copy its content until EOF. + */ +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <split_at.h> +#include <listen.h> + +int main(int argc, char **argv) +{ + char *transport; + char *endpoint; + int listen_sock; + int client_sock; + int client_fd; + ssize_t read_count; + char buf[1024]; + + if (argc < 2 || argc > 3 + || (endpoint = split_at(transport = argv[1], ':')) == 0 + || *endpoint == 0 || *transport == 0) + msg_fatal("usage: %s transport:endpoint [workaround]", argv[0]); + + if (strcmp(transport, "unix") == 0) { + listen_sock = unix_listen(endpoint, 10, BLOCKING); + } else { + msg_fatal("invalid transport name: %s", transport); + } + if (listen_sock < 0) + msg_fatal("listen %s:%s: %m", transport, endpoint); + + client_sock = accept(listen_sock, (struct sockaddr *) 0, (SOCKADDR_SIZE) 0); + if (client_sock < 0) + msg_fatal("accept: %m"); + + set_unix_pass_fd_fix(argv[2] ? argv[2] : ""); + + while ((client_fd = unix_recv_fd(client_sock)) >= 0) { + msg_info("client_fd = %d, fix=%d", client_fd, unix_pass_fd_fix); + while ((read_count = read(client_fd, buf, sizeof(buf))) > 0) + write(1, buf, read_count); + if (read_count < 0) + msg_fatal("read: %m"); + close(client_fd); + } + exit(0); +} + +#endif |