1/* Copyright (C) 2001-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If
16 not, see <https://www.gnu.org/licenses/>. */
17
18#include <errno.h>
19#include <string.h>
20#include <sys/socket.h>
21
22#include <hurd.h>
23#include <hurd/fd.h>
24#include <hurd/socket.h>
25#include <sysdep-cancel.h>
26
27/* Receive a message as described by MESSAGE from socket FD.
28 Returns the number of bytes read or -1 for errors. */
29ssize_t
30__libc_recvmsg (int fd, struct msghdr *message, int flags)
31{
32 error_t err;
33 addr_port_t aport;
34 char *data = NULL;
35 mach_msg_type_number_t len = 0;
36 mach_port_t *ports, *newports = NULL;
37 mach_msg_type_number_t nports = 0;
38 struct cmsghdr *cmsg;
39 char *cdata = NULL;
40 mach_msg_type_number_t clen = 0;
41 size_t amount;
42 char *buf;
43 int nfds, *opened_fds = NULL;
44 int i, ii, j;
45 int newfds;
46 int cancel_oldtype;
47
48 error_t reauthenticate (mach_port_t port, mach_port_t *result)
49 {
50 error_t err;
51 mach_port_t ref;
52 ref = __mach_reply_port ();
53 int cancel_oldtype;
54
55 cancel_oldtype = LIBC_CANCEL_ASYNC();
56 do
57 err = __io_reauthenticate (port, ref, MACH_MSG_TYPE_MAKE_SEND);
58 while (err == EINTR);
59 if (!err)
60 do
61 err = __USEPORT_CANCEL (AUTH, __auth_user_authenticate (port,
62 ref, MACH_MSG_TYPE_MAKE_SEND,
63 result));
64 while (err == EINTR);
65 LIBC_CANCEL_RESET (cancel_oldtype);
66
67 __mach_port_destroy (__mach_task_self (), ref);
68 return err;
69 }
70
71 /* Find the total number of bytes to be read. */
72 amount = 0;
73 for (i = 0; i < message->msg_iovlen; i++)
74 {
75 amount += message->msg_iov[i].iov_len;
76
77 /* As an optimization, we set the initial values of DATA and LEN
78 from the first non-empty iovec. This kicks-in in the case
79 where the whole packet fits into that iovec buffer. */
80 if (data == NULL && message->msg_iov[i].iov_len > 0)
81 {
82 data = message->msg_iov[i].iov_base;
83 len = message->msg_iov[i].iov_len;
84 }
85 }
86
87 buf = data;
88 cancel_oldtype = LIBC_CANCEL_ASYNC();
89 err = HURD_DPORT_USE_CANCEL (fd, __socket_recv (port, &aport,
90 flags, &data, &len,
91 &ports, &nports,
92 &cdata, &clen,
93 &message->msg_flags, amount));
94 LIBC_CANCEL_RESET (cancel_oldtype);
95 if (err)
96 return __hurd_sockfail (fd, flags, err);
97
98 if (message->msg_name != NULL && aport != MACH_PORT_NULL)
99 {
100 char *buf = message->msg_name;
101 mach_msg_type_number_t buflen = message->msg_namelen;
102 int type;
103
104 cancel_oldtype = LIBC_CANCEL_ASYNC();
105 err = __socket_whatis_address (aport, &type, &buf, &buflen);
106 LIBC_CANCEL_RESET (cancel_oldtype);
107
108 if (err == EOPNOTSUPP)
109 /* If the protocol server can't tell us the address, just return a
110 zero-length one. */
111 {
112 buf = message->msg_name;
113 buflen = 0;
114 err = 0;
115 }
116
117 if (err)
118 {
119 __mach_port_deallocate (__mach_task_self (), aport);
120 return __hurd_sockfail (fd, flags, err);
121 }
122
123 if (message->msg_namelen > buflen)
124 message->msg_namelen = buflen;
125
126 if (buf != message->msg_name)
127 {
128 memcpy (message->msg_name, buf, message->msg_namelen);
129 __vm_deallocate (__mach_task_self (), (vm_address_t) buf, buflen);
130 }
131
132 if (buflen > 0)
133 ((struct sockaddr *) message->msg_name)->sa_family = type;
134 }
135 else if (message->msg_name != NULL)
136 message->msg_namelen = 0;
137
138 __mach_port_deallocate (__mach_task_self (), aport);
139
140 if (buf == data)
141 buf += len;
142 else
143 {
144 /* Copy the data into MSG. */
145 if (len > amount)
146 message->msg_flags |= MSG_TRUNC;
147 else
148 amount = len;
149
150 buf = data;
151 for (i = 0; i < message->msg_iovlen; i++)
152 {
153#define min(a, b) ((a) > (b) ? (b) : (a))
154 size_t copy = min (message->msg_iov[i].iov_len, amount);
155
156 memcpy (message->msg_iov[i].iov_base, buf, copy);
157
158 buf += copy;
159 amount -= copy;
160 if (len == 0)
161 break;
162 }
163
164 __vm_deallocate (__mach_task_self (), (vm_address_t) data, len);
165 }
166
167 /* Copy the control message into MSG. */
168 if (clen > message->msg_controllen)
169 message->msg_flags |= MSG_CTRUNC;
170 else
171 message->msg_controllen = clen;
172 memcpy (message->msg_control, cdata, message->msg_controllen);
173
174 if (nports > 0)
175 {
176 newports = __alloca (nports * sizeof (mach_port_t));
177 opened_fds = __alloca (nports * sizeof (int));
178 }
179
180 /* This counts how many ports we processed completely. */
181 i = 0;
182 /* This counts how many new fds we create. */
183 newfds = 0;
184
185 for (cmsg = CMSG_FIRSTHDR (message);
186 cmsg;
187 cmsg = CMSG_NXTHDR (message, cmsg))
188 {
189 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
190 {
191 /* SCM_RIGHTS support. */
192 /* The fd's flags are passed in the control data. */
193 int *fds = (int *) CMSG_DATA (cmsg);
194 nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
195 / sizeof (int);
196
197 for (j = 0; j < nfds; j++)
198 {
199 err = reauthenticate (ports[i], &newports[newfds]);
200 if (err)
201 goto cleanup;
202 fds[j] = opened_fds[newfds] = _hurd_intern_fd (newports[newfds],
203 fds[j], 0);
204 if (fds[j] == -1)
205 {
206 err = errno;
207 __mach_port_deallocate (__mach_task_self (), newports[newfds]);
208 goto cleanup;
209 }
210 i++;
211 newfds++;
212 }
213 }
214 }
215
216 for (i = 0; i < nports; i++)
217 __mach_port_deallocate (mach_task_self (), ports[i]);
218
219 __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen);
220
221 return (buf - data);
222
223cleanup:
224 /* Clean up all the file descriptors from port 0 to i-1. */
225 if (nports > 0)
226 {
227 ii = 0;
228 newfds = 0;
229 for (cmsg = CMSG_FIRSTHDR (message);
230 cmsg;
231 cmsg = CMSG_NXTHDR (message, cmsg))
232 {
233 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
234 {
235 nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
236 / sizeof (int);
237 for (j = 0; j < nfds && ii < i; j++, ii++, newfds++)
238 {
239 _hurd_fd_close (_hurd_fd_get (opened_fds[newfds]));
240 __mach_port_deallocate (__mach_task_self (), newports[newfds]);
241 __mach_port_deallocate (__mach_task_self (), ports[ii]);
242 }
243 }
244 }
245 }
246
247 __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen);
248 return __hurd_fail (err);
249}
250
251weak_alias (__libc_recvmsg, recvmsg)
252weak_alias (__libc_recvmsg, __recvmsg)
253

source code of glibc/sysdeps/mach/hurd/recvmsg.c