]>
Commit | Line | Data |
---|---|---|
2b778ceb | 1 | /* Copyright (C) 2001-2021 Free Software Foundation, Inc. |
5301af2d MK |
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 | |
cc7375ce RM |
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 | |
5301af2d MK |
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 | |
cc7375ce | 12 | Lesser General Public License for more details. |
5301af2d | 13 | |
cc7375ce | 14 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 | 15 | License along with the GNU C Library; see the file COPYING.LIB. If |
5a82c748 | 16 | not, see <https://www.gnu.org/licenses/>. */ |
5301af2d MK |
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> | |
89edef7b | 25 | #include <sysdep-cancel.h> |
5301af2d MK |
26 | |
27 | /* Receive a message as described by MESSAGE from socket FD. | |
28 | Returns the number of bytes read or -1 for errors. */ | |
29 | ssize_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; | |
344e7552 | 36 | mach_port_t *ports, *newports = NULL; |
d5a0160b | 37 | mach_msg_type_number_t nports = 0; |
344e7552 | 38 | struct cmsghdr *cmsg; |
5301af2d MK |
39 | char *cdata = NULL; |
40 | mach_msg_type_number_t clen = 0; | |
41 | size_t amount; | |
42 | char *buf; | |
344e7552 EPM |
43 | int nfds, *opened_fds = NULL; |
44 | int i, ii, j; | |
45 | int newfds; | |
89edef7b | 46 | int cancel_oldtype; |
344e7552 EPM |
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 (); | |
89edef7b ST |
53 | int cancel_oldtype; |
54 | ||
55 | cancel_oldtype = LIBC_CANCEL_ASYNC(); | |
344e7552 EPM |
56 | do |
57 | err = __io_reauthenticate (port, ref, MACH_MSG_TYPE_MAKE_SEND); | |
58 | while (err == EINTR); | |
59 | if (!err) | |
60 | do | |
67a78072 | 61 | err = __USEPORT_CANCEL (AUTH, __auth_user_authenticate (port, |
344e7552 EPM |
62 | ref, MACH_MSG_TYPE_MAKE_SEND, |
63 | result)); | |
64 | while (err == EINTR); | |
89edef7b ST |
65 | LIBC_CANCEL_RESET (cancel_oldtype); |
66 | ||
344e7552 EPM |
67 | __mach_port_destroy (__mach_task_self (), ref); |
68 | return err; | |
69 | } | |
5301af2d MK |
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; | |
89edef7b | 88 | cancel_oldtype = LIBC_CANCEL_ASYNC(); |
67a78072 ST |
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)); | |
89edef7b ST |
94 | LIBC_CANCEL_RESET (cancel_oldtype); |
95 | if (err) | |
e66ecb22 | 96 | return __hurd_sockfail (fd, flags, err); |
5301af2d | 97 | |
a194625e | 98 | if (message->msg_name != NULL && aport != MACH_PORT_NULL) |
5301af2d MK |
99 | { |
100 | char *buf = message->msg_name; | |
101 | mach_msg_type_number_t buflen = message->msg_namelen; | |
102 | int type; | |
103 | ||
89edef7b | 104 | cancel_oldtype = LIBC_CANCEL_ASYNC(); |
5301af2d | 105 | err = __socket_whatis_address (aport, &type, &buf, &buflen); |
89edef7b ST |
106 | LIBC_CANCEL_RESET (cancel_oldtype); |
107 | ||
5301af2d MK |
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); | |
e66ecb22 | 120 | return __hurd_sockfail (fd, flags, err); |
5301af2d MK |
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 | } | |
a194625e ST |
135 | else if (message->msg_name != NULL) |
136 | message->msg_namelen = 0; | |
5301af2d MK |
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 | ||
344e7552 EPM |
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 | ||
5301af2d MK |
219 | __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen); |
220 | ||
221 | return (buf - data); | |
344e7552 EPM |
222 | |
223 | cleanup: | |
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); | |
5301af2d MK |
249 | } |
250 | ||
251 | weak_alias (__libc_recvmsg, recvmsg) | |
b2bffca2 | 252 | weak_alias (__libc_recvmsg, __recvmsg) |