xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/unix_recv_fd.c (revision 1b9578b8c2c1f848eeb16dabbfd7d1f0d9fdefbd)
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