1/* Copyright (C) 2001-2024 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 if (MACH_PORT_VALID (aport))
139 __mach_port_deallocate (__mach_task_self (), aport);
140
141 if (buf == data)
142 buf += len;
143 else
144 {
145 /* Copy the data into MSG. */
146 if (len > amount)
147 message->msg_flags |= MSG_TRUNC;
148 else
149 amount = len;
150
151 buf = data;
152 for (i = 0; i < message->msg_iovlen; i++)
153 {
154#define min(a, b) ((a) > (b) ? (b) : (a))
155 size_t copy = min (message->msg_iov[i].iov_len, amount);
156
157 memcpy (message->msg_iov[i].iov_base, buf, copy);
158
159 buf += copy;
160 amount -= copy;
161 if (len == 0)
162 break;
163 }
164
165 __vm_deallocate (__mach_task_self (), (vm_address_t) data, len);
166 }
167
168 /* Copy the control message into MSG. */
169 if (clen > message->msg_controllen)
170 message->msg_flags |= MSG_CTRUNC;
171 else
172 message->msg_controllen = clen;
173 memcpy (message->msg_control, cdata, message->msg_controllen);
174
175 if (nports > 0)
176 {
177 newports = __alloca (nports * sizeof (mach_port_t));
178 opened_fds = __alloca (nports * sizeof (int));
179 }
180
181 /* This counts how many ports we processed completely. */
182 i = 0;
183 /* This counts how many new fds we create. */
184 newfds = 0;
185
186 for (cmsg = CMSG_FIRSTHDR (message);
187 cmsg;
188 cmsg = CMSG_NXTHDR (message, cmsg))
189 {
190 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
191 {
192 /* SCM_RIGHTS support. */
193 /* The fd's flags are passed in the control data. */
194 int *fds = (int *) CMSG_DATA (cmsg);
195 nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
196 / sizeof (int);
197
198 for (j = 0; j < nfds; j++)
199 {
200 int fd_flags = (flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0;
201 err = reauthenticate (ports[i], &newports[newfds]);
202 if (err)
203 goto cleanup;
204 /* We do not currently take any flag from the sender. */
205 fds[j] = opened_fds[newfds] = _hurd_intern_fd (newports[newfds],
206 (fds[j] & 0)
207 | fd_flags,
208 0);
209 if (fds[j] == -1)
210 {
211 err = errno;
212 __mach_port_deallocate (__mach_task_self (), newports[newfds]);
213 goto cleanup;
214 }
215 i++;
216 newfds++;
217 }
218 }
219 }
220
221 for (i = 0; i < nports; i++)
222 __mach_port_deallocate (mach_task_self (), ports[i]);
223
224 __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen);
225
226 return (buf - data);
227
228cleanup:
229 /* Clean up all the file descriptors from port 0 to i-1. */
230 if (nports > 0)
231 {
232 ii = 0;
233 newfds = 0;
234 for (cmsg = CMSG_FIRSTHDR (message);
235 cmsg;
236 cmsg = CMSG_NXTHDR (message, cmsg))
237 {
238 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
239 {
240 nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
241 / sizeof (int);
242 for (j = 0; j < nfds && ii < i; j++, ii++, newfds++)
243 {
244 _hurd_fd_close (_hurd_fd_get (opened_fds[newfds]));
245 __mach_port_deallocate (__mach_task_self (), newports[newfds]);
246 __mach_port_deallocate (__mach_task_self (), ports[ii]);
247 }
248 }
249 }
250 }
251
252 __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen);
253 return __hurd_fail (err);
254}
255
256weak_alias (__libc_recvmsg, recvmsg)
257weak_alias (__libc_recvmsg, __recvmsg)
258

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