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
unix_recv_fd(int fd)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
main(int argc,char ** argv)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