xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/unix_recv_fd.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1*e89934bbSchristos /*	$NetBSD: unix_recv_fd.c,v 1.8 2017/02/14 01:16:49 christos Exp $	*/
241fbaed0Stron 
341fbaed0Stron /*++
441fbaed0Stron /* NAME
541fbaed0Stron /*	unix_recv_fd 3
641fbaed0Stron /* SUMMARY
741fbaed0Stron /*	receive file descriptor
841fbaed0Stron /* SYNOPSIS
941fbaed0Stron /*	#include <iostuff.h>
1041fbaed0Stron /*
1141fbaed0Stron /*	int	unix_recv_fd(fd)
1241fbaed0Stron /*	int	fd;
1341fbaed0Stron /* DESCRIPTION
1441fbaed0Stron /*	unix_recv_fd() receives a file descriptor via the specified
1541fbaed0Stron /*	UNIX-domain socket. The result value is the received descriptor.
1641fbaed0Stron /*
1741fbaed0Stron /*	Arguments:
1841fbaed0Stron /* .IP fd
19d14acb8dStron /*	File descriptor that connects the sending and receiving processes.
2041fbaed0Stron /* DIAGNOSTICS
2141fbaed0Stron /*	unix_recv_fd() returns -1 upon failure.
2241fbaed0Stron /* LICENSE
2341fbaed0Stron /* .ad
2441fbaed0Stron /* .fi
2541fbaed0Stron /*	The Secure Mailer license must be distributed with this software.
2641fbaed0Stron /* AUTHOR(S)
2741fbaed0Stron /*	Wietse Venema
2841fbaed0Stron /*	IBM T.J. Watson Research
2941fbaed0Stron /*	P.O. Box 704
3041fbaed0Stron /*	Yorktown Heights, NY 10598, USA
3141fbaed0Stron /*--*/
3241fbaed0Stron 
3341fbaed0Stron /* System library. */
3441fbaed0Stron 
3541fbaed0Stron #include <sys_defs.h>			/* includes <sys/types.h> */
3641fbaed0Stron #include <sys/socket.h>
3741fbaed0Stron #include <sys/uio.h>
3841fbaed0Stron #include <string.h>
3941fbaed0Stron 
4041fbaed0Stron /* Utility library. */
4141fbaed0Stron 
4241fbaed0Stron #include <msg.h>
4341fbaed0Stron #include <iostuff.h>
4441fbaed0Stron 
4541fbaed0Stron /* unix_recv_fd - receive file descriptor */
4641fbaed0Stron 
unix_recv_fd(int fd)4741fbaed0Stron int     unix_recv_fd(int fd)
4841fbaed0Stron {
4941fbaed0Stron     const char *myname = "unix_recv_fd";
5041fbaed0Stron 
5141fbaed0Stron     /*
5241fbaed0Stron      * This code does not work with version <2.2 Linux kernels, and it does
5341fbaed0Stron      * not compile with version <2 Linux libraries.
5441fbaed0Stron      */
5541fbaed0Stron #ifdef CANT_USE_SEND_RECV_MSG
5641fbaed0Stron     msg_warn("%s: your system has no support for file descriptor passing",
5741fbaed0Stron 	     myname);
5841fbaed0Stron     return (-1);
5941fbaed0Stron #else
6041fbaed0Stron     struct msghdr msg;
6141fbaed0Stron     int     newfd;
6241fbaed0Stron     struct iovec iov[1];
6341fbaed0Stron     char    buf[1];
6441fbaed0Stron 
6541fbaed0Stron     /*
6641fbaed0Stron      * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1,
6741fbaed0Stron      * Second edition. Except that we use CMSG_LEN instead of CMSG_SPACE, for
686941d836Stron      * portability to some LP64 environments. See also unix_send_fd.c.
6941fbaed0Stron      */
7041fbaed0Stron #if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL)
7141fbaed0Stron     union {
7241fbaed0Stron 	struct cmsghdr just_for_alignment;
73ea5a6926Spgoyette 	char    control[CMSG_SPACE(sizeof(newfd))];
7441fbaed0Stron     }       control_un;
7541fbaed0Stron     struct cmsghdr *cmptr;
7641fbaed0Stron 
77*e89934bbSchristos     memset((void *) &msg, 0, sizeof(msg));	/* Fix 200512 */
7841fbaed0Stron     msg.msg_control = control_un.control;
796941d836Stron     if (unix_pass_fd_fix & UNIX_PASS_FD_FIX_CMSG_LEN) {
806941d836Stron 	msg.msg_controllen = CMSG_LEN(sizeof(newfd));	/* Fix 200506 */
816941d836Stron     } else {
82f7954747Sjoerg 	msg.msg_controllen = CMSG_SPACE(sizeof(newfd));	/* normal */
836941d836Stron     }
8441fbaed0Stron #else
8541fbaed0Stron     msg.msg_accrights = (char *) &newfd;
8641fbaed0Stron     msg.msg_accrightslen = sizeof(newfd);
8741fbaed0Stron #endif
8841fbaed0Stron 
8941fbaed0Stron     msg.msg_name = 0;
9041fbaed0Stron     msg.msg_namelen = 0;
9141fbaed0Stron 
9241fbaed0Stron     /*
9341fbaed0Stron      * XXX We don't want to pass any data, just a file descriptor. However,
9441fbaed0Stron      * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble: we need
9541fbaed0Stron      * to read_wait() before we can receive the descriptor, and the code
9641fbaed0Stron      * fails after the first descriptor when we attempt to receive a sequence
9741fbaed0Stron      * of descriptors.
9841fbaed0Stron      */
9941fbaed0Stron     iov->iov_base = buf;
10041fbaed0Stron     iov->iov_len = sizeof(buf);
10141fbaed0Stron     msg.msg_iov = iov;
10241fbaed0Stron     msg.msg_iovlen = 1;
10341fbaed0Stron 
10441fbaed0Stron     if (recvmsg(fd, &msg, 0) < 0)
10541fbaed0Stron 	return (-1);
10641fbaed0Stron 
10741fbaed0Stron #if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL)
10841fbaed0Stron     if ((cmptr = CMSG_FIRSTHDR(&msg)) != 0
10941fbaed0Stron 	&& cmptr->cmsg_len == CMSG_LEN(sizeof(newfd))) {
11041fbaed0Stron 	if (cmptr->cmsg_level != SOL_SOCKET)
11141fbaed0Stron 	    msg_fatal("%s: control level %d != SOL_SOCKET",
11241fbaed0Stron 		      myname, cmptr->cmsg_level);
11341fbaed0Stron 	if (cmptr->cmsg_type != SCM_RIGHTS)
11441fbaed0Stron 	    msg_fatal("%s: control type %d != SCM_RIGHTS",
11541fbaed0Stron 		      myname, cmptr->cmsg_type);
11641fbaed0Stron 	return (*(int *) CMSG_DATA(cmptr));
11741fbaed0Stron     } else
11841fbaed0Stron 	return (-1);
11941fbaed0Stron #else
12041fbaed0Stron     if (msg.msg_accrightslen == sizeof(newfd))
12141fbaed0Stron 	return (newfd);
12241fbaed0Stron     else
12341fbaed0Stron 	return (-1);
12441fbaed0Stron #endif
12541fbaed0Stron #endif
12641fbaed0Stron }
12741fbaed0Stron 
12841fbaed0Stron #ifdef TEST
12941fbaed0Stron 
13041fbaed0Stron  /*
13141fbaed0Stron   * Proof-of-concept program. Receive a descriptor (presumably from the
13241fbaed0Stron   * unix_send_fd test program) and copy its content until EOF.
13341fbaed0Stron   */
13441fbaed0Stron #include <unistd.h>
13541fbaed0Stron #include <string.h>
13641fbaed0Stron #include <stdlib.h>
13741fbaed0Stron #include <split_at.h>
13841fbaed0Stron #include <listen.h>
13941fbaed0Stron 
main(int argc,char ** argv)14041fbaed0Stron int     main(int argc, char **argv)
14141fbaed0Stron {
14241fbaed0Stron     char   *transport;
14341fbaed0Stron     char   *endpoint;
14441fbaed0Stron     int     listen_sock;
14541fbaed0Stron     int     client_sock;
14641fbaed0Stron     int     client_fd;
14741fbaed0Stron     ssize_t read_count;
14841fbaed0Stron     char    buf[1024];
14941fbaed0Stron 
1506941d836Stron     if (argc < 2 || argc > 3
15141fbaed0Stron 	|| (endpoint = split_at(transport = argv[1], ':')) == 0
15241fbaed0Stron 	|| *endpoint == 0 || *transport == 0)
1536941d836Stron 	msg_fatal("usage: %s transport:endpoint [workaround]", argv[0]);
15441fbaed0Stron 
15541fbaed0Stron     if (strcmp(transport, "unix") == 0) {
15641fbaed0Stron 	listen_sock = unix_listen(endpoint, 10, BLOCKING);
15741fbaed0Stron     } else {
15841fbaed0Stron 	msg_fatal("invalid transport name: %s", transport);
15941fbaed0Stron     }
16041fbaed0Stron     if (listen_sock < 0)
16141fbaed0Stron 	msg_fatal("listen %s:%s: %m", transport, endpoint);
16241fbaed0Stron 
16341fbaed0Stron     client_sock = accept(listen_sock, (struct sockaddr *) 0, (SOCKADDR_SIZE) 0);
16441fbaed0Stron     if (client_sock < 0)
16541fbaed0Stron 	msg_fatal("accept: %m");
16641fbaed0Stron 
1676941d836Stron     set_unix_pass_fd_fix(argv[2] ? argv[2] : "");
1686941d836Stron 
16941fbaed0Stron     while ((client_fd = unix_recv_fd(client_sock)) >= 0) {
1706941d836Stron 	msg_info("client_fd = %d, fix=%d", client_fd, unix_pass_fd_fix);
17141fbaed0Stron 	while ((read_count = read(client_fd, buf, sizeof(buf))) > 0)
17241fbaed0Stron 	    write(1, buf, read_count);
17341fbaed0Stron 	if (read_count < 0)
17441fbaed0Stron 	    msg_fatal("read: %m");
17541fbaed0Stron 	close(client_fd);
17641fbaed0Stron     }
17741fbaed0Stron     exit(0);
17841fbaed0Stron }
17941fbaed0Stron 
18041fbaed0Stron #endif
181