]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libnv/msgio.c
unbound: Vendor import 1.19.0
[FreeBSD/FreeBSD.git] / lib / libnv / msgio.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2013 The FreeBSD Foundation
5  * Copyright (c) 2013 Mariusz Zaborski <oshogbo@FreeBSD.org>
6  * All rights reserved.
7  *
8  * This software was developed by Pawel Jakub Dawidek under sponsorship from
9  * the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #include <sys/cdefs.h>
34 #include <sys/param.h>
35 #include <sys/socket.h>
36 #include <sys/select.h>
37
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <stdbool.h>
41 #include <stdint.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45
46 #ifdef HAVE_PJDLOG
47 #include <pjdlog.h>
48 #endif
49
50 #include "common_impl.h"
51 #include "msgio.h"
52
53 #ifndef HAVE_PJDLOG
54 #include <assert.h>
55 #define PJDLOG_ASSERT(...)              assert(__VA_ARGS__)
56 #define PJDLOG_RASSERT(expr, ...)       assert(expr)
57 #define PJDLOG_ABORT(...)               abort()
58 #endif
59
60 #ifdef __linux__
61 /* Linux: arbitrary size, but must be lower than SCM_MAX_FD. */
62 #define PKG_MAX_SIZE    ((64U - 1) * CMSG_SPACE(sizeof(int)))
63 #else
64 /*
65  * To work around limitations in 32-bit emulation on 64-bit kernels, use a
66  * machine-independent limit on the number of FDs per message.  Each control
67  * message contains 1 FD and requires 12 bytes for the header, 4 pad bytes,
68  * 4 bytes for the descriptor, and another 4 pad bytes.
69  */
70 #define PKG_MAX_SIZE    (MCLBYTES / 24)
71 #endif
72
73 static int
74 msghdr_add_fd(struct cmsghdr *cmsg, int fd)
75 {
76
77         PJDLOG_ASSERT(fd >= 0);
78
79         cmsg->cmsg_level = SOL_SOCKET;
80         cmsg->cmsg_type = SCM_RIGHTS;
81         cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
82         bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
83
84         return (0);
85 }
86
87 static void
88 fd_wait(int fd, bool doread)
89 {
90         fd_set fds;
91
92         PJDLOG_ASSERT(fd >= 0);
93
94         FD_ZERO(&fds);
95         FD_SET(fd, &fds);
96         (void)select(fd + 1, doread ? &fds : NULL, doread ? NULL : &fds,
97             NULL, NULL);
98 }
99
100 static int
101 msg_recv(int sock, struct msghdr *msg)
102 {
103         int flags;
104
105         PJDLOG_ASSERT(sock >= 0);
106
107 #ifdef MSG_CMSG_CLOEXEC
108         flags = MSG_CMSG_CLOEXEC;
109 #else
110         flags = 0;
111 #endif
112
113         for (;;) {
114                 fd_wait(sock, true);
115                 if (recvmsg(sock, msg, flags) == -1) {
116                         if (errno == EINTR)
117                                 continue;
118                         return (-1);
119                 }
120                 break;
121         }
122
123         return (0);
124 }
125
126 static int
127 msg_send(int sock, const struct msghdr *msg)
128 {
129
130         PJDLOG_ASSERT(sock >= 0);
131
132         for (;;) {
133                 fd_wait(sock, false);
134                 if (sendmsg(sock, msg, 0) == -1) {
135                         if (errno == EINTR)
136                                 continue;
137                         return (-1);
138                 }
139                 break;
140         }
141
142         return (0);
143 }
144
145 #ifdef __FreeBSD__
146 int
147 cred_send(int sock)
148 {
149         unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))];
150         struct msghdr msg;
151         struct cmsghdr *cmsg;
152         struct iovec iov;
153         uint8_t dummy;
154
155         bzero(credbuf, sizeof(credbuf));
156         bzero(&msg, sizeof(msg));
157         bzero(&iov, sizeof(iov));
158
159         /*
160          * XXX: We send one byte along with the control message, because
161          *      setting msg_iov to NULL only works if this is the first
162          *      packet send over the socket. Once we send some data we
163          *      won't be able to send credentials anymore. This is most
164          *      likely a kernel bug.
165          */
166         dummy = 0;
167         iov.iov_base = &dummy;
168         iov.iov_len = sizeof(dummy);
169
170         msg.msg_iov = &iov;
171         msg.msg_iovlen = 1;
172         msg.msg_control = credbuf;
173         msg.msg_controllen = sizeof(credbuf);
174
175         cmsg = CMSG_FIRSTHDR(&msg);
176         cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
177         cmsg->cmsg_level = SOL_SOCKET;
178         cmsg->cmsg_type = SCM_CREDS;
179
180         if (msg_send(sock, &msg) == -1)
181                 return (-1);
182
183         return (0);
184 }
185
186 int
187 cred_recv(int sock, struct cmsgcred *cred)
188 {
189         unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))];
190         struct msghdr msg;
191         struct cmsghdr *cmsg;
192         struct iovec iov;
193         uint8_t dummy;
194
195         bzero(credbuf, sizeof(credbuf));
196         bzero(&msg, sizeof(msg));
197         bzero(&iov, sizeof(iov));
198
199         iov.iov_base = &dummy;
200         iov.iov_len = sizeof(dummy);
201
202         msg.msg_iov = &iov;
203         msg.msg_iovlen = 1;
204         msg.msg_control = credbuf;
205         msg.msg_controllen = sizeof(credbuf);
206
207         if (msg_recv(sock, &msg) == -1)
208                 return (-1);
209
210         cmsg = CMSG_FIRSTHDR(&msg);
211         if (cmsg == NULL ||
212             cmsg->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred)) ||
213             cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDS) {
214                 errno = EINVAL;
215                 return (-1);
216         }
217         bcopy(CMSG_DATA(cmsg), cred, sizeof(*cred));
218
219         return (0);
220 }
221 #endif
222
223 static int
224 fd_package_send(int sock, const int *fds, size_t nfds)
225 {
226         struct msghdr msg;
227         struct cmsghdr *cmsg;
228         struct iovec iov;
229         unsigned int i;
230         int serrno, ret;
231         uint8_t dummy;
232
233         PJDLOG_ASSERT(sock >= 0);
234         PJDLOG_ASSERT(fds != NULL);
235         PJDLOG_ASSERT(nfds > 0);
236
237         bzero(&msg, sizeof(msg));
238
239         /*
240          * XXX: Look into cred_send function for more details.
241          */
242         dummy = 0;
243         iov.iov_base = &dummy;
244         iov.iov_len = sizeof(dummy);
245
246         msg.msg_iov = &iov;
247         msg.msg_iovlen = 1;
248         msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int));
249         msg.msg_control = calloc(1, msg.msg_controllen);
250         if (msg.msg_control == NULL)
251                 return (-1);
252
253         ret = -1;
254
255         for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL;
256             i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) {
257                 if (msghdr_add_fd(cmsg, fds[i]) == -1)
258                         goto end;
259         }
260
261         if (msg_send(sock, &msg) == -1)
262                 goto end;
263
264         ret = 0;
265 end:
266         serrno = errno;
267         free(msg.msg_control);
268         errno = serrno;
269         return (ret);
270 }
271
272 static int
273 fd_package_recv(int sock, int *fds, size_t nfds)
274 {
275         struct msghdr msg;
276         struct cmsghdr *cmsg;
277         unsigned int i;
278         int serrno, ret;
279         struct iovec iov;
280         uint8_t dummy;
281
282         PJDLOG_ASSERT(sock >= 0);
283         PJDLOG_ASSERT(nfds > 0);
284         PJDLOG_ASSERT(fds != NULL);
285
286         bzero(&msg, sizeof(msg));
287         bzero(&iov, sizeof(iov));
288
289         /*
290          * XXX: Look into cred_send function for more details.
291          */
292         iov.iov_base = &dummy;
293         iov.iov_len = sizeof(dummy);
294
295         msg.msg_iov = &iov;
296         msg.msg_iovlen = 1;
297         msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int));
298         msg.msg_control = calloc(1, msg.msg_controllen);
299         if (msg.msg_control == NULL)
300                 return (-1);
301
302         ret = -1;
303
304         if (msg_recv(sock, &msg) == -1)
305                 goto end;
306
307         i = 0;
308         cmsg = CMSG_FIRSTHDR(&msg);
309         while (cmsg && i < nfds) {
310                 unsigned int n;
311
312                 if (cmsg->cmsg_level != SOL_SOCKET ||
313                     cmsg->cmsg_type != SCM_RIGHTS) {
314                         errno = EINVAL;
315                         break;
316                 }
317                 n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
318                 if (i + n > nfds) {
319                         errno = EINVAL;
320                         break;
321                 }
322                 bcopy(CMSG_DATA(cmsg), fds + i, sizeof(int) * n);
323                 cmsg = CMSG_NXTHDR(&msg, cmsg);
324                 i += n;
325         }
326
327         if (cmsg != NULL || i < nfds) {
328                 unsigned int last;
329
330                 /*
331                  * We need to close all received descriptors, even if we have
332                  * different control message (eg. SCM_CREDS) in between.
333                  */
334                 last = i;
335                 for (i = 0; i < last; i++) {
336                         if (fds[i] >= 0) {
337                                 close(fds[i]);
338                         }
339                 }
340                 errno = EINVAL;
341                 goto end;
342         }
343
344 #ifndef MSG_CMSG_CLOEXEC
345         /*
346          * If the MSG_CMSG_CLOEXEC flag is not available we cannot set the
347          * close-on-exec flag atomically, but we still want to set it for
348          * consistency.
349          */
350         for (i = 0; i < nfds; i++) {
351                 (void) fcntl(fds[i], F_SETFD, FD_CLOEXEC);
352         }
353 #endif
354
355         ret = 0;
356 end:
357         serrno = errno;
358         free(msg.msg_control);
359         errno = serrno;
360         return (ret);
361 }
362
363 int
364 fd_recv(int sock, int *fds, size_t nfds)
365 {
366         unsigned int i, step, j;
367         int ret, serrno;
368
369         if (nfds == 0 || fds == NULL) {
370                 errno = EINVAL;
371                 return (-1);
372         }
373
374         ret = i = step = 0;
375         while (i < nfds) {
376                 if (PKG_MAX_SIZE < nfds - i)
377                         step = PKG_MAX_SIZE;
378                 else
379                         step = nfds - i;
380                 ret = fd_package_recv(sock, fds + i, step);
381                 if (ret != 0) {
382                         /* Close all received descriptors. */
383                         serrno = errno;
384                         for (j = 0; j < i; j++)
385                                 close(fds[j]);
386                         errno = serrno;
387                         break;
388                 }
389                 i += step;
390         }
391
392         return (ret);
393 }
394
395 int
396 fd_send(int sock, const int *fds, size_t nfds)
397 {
398         unsigned int i, step;
399         int ret;
400
401         if (nfds == 0 || fds == NULL) {
402                 errno = EINVAL;
403                 return (-1);
404         }
405
406         ret = i = step = 0;
407         while (i < nfds) {
408                 if (PKG_MAX_SIZE < nfds - i)
409                         step = PKG_MAX_SIZE;
410                 else
411                         step = nfds - i;
412                 ret = fd_package_send(sock, fds + i, step);
413                 if (ret != 0)
414                         break;
415                 i += step;
416         }
417
418         return (ret);
419 }
420
421 int
422 buf_send(int sock, void *buf, size_t size)
423 {
424         ssize_t done;
425         unsigned char *ptr;
426
427         PJDLOG_ASSERT(sock >= 0);
428         PJDLOG_ASSERT(size > 0);
429         PJDLOG_ASSERT(buf != NULL);
430
431         ptr = buf;
432         do {
433                 fd_wait(sock, false);
434                 done = send(sock, ptr, size, 0);
435                 if (done == -1) {
436                         if (errno == EINTR)
437                                 continue;
438                         return (-1);
439                 } else if (done == 0) {
440                         errno = ENOTCONN;
441                         return (-1);
442                 }
443                 size -= done;
444                 ptr += done;
445         } while (size > 0);
446
447         return (0);
448 }
449
450 int
451 buf_recv(int sock, void *buf, size_t size, int flags)
452 {
453         ssize_t done;
454         unsigned char *ptr;
455
456         PJDLOG_ASSERT(sock >= 0);
457         PJDLOG_ASSERT(buf != NULL);
458
459         ptr = buf;
460         while (size > 0) {
461                 fd_wait(sock, true);
462                 done = recv(sock, ptr, size, flags);
463                 if (done == -1) {
464                         if (errno == EINTR)
465                                 continue;
466                         return (-1);
467                 } else if (done == 0) {
468                         errno = ENOTCONN;
469                         return (-1);
470                 }
471                 size -= done;
472                 ptr += done;
473         }
474
475         return (0);
476 }