1*32302d25Schristos /* $NetBSD: msgio.c,v 1.2 2018/09/08 14:02:15 christos Exp $ */
2*32302d25Schristos
384193a29Schristos /*-
484193a29Schristos * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
584193a29Schristos *
684193a29Schristos * Copyright (c) 2013 The FreeBSD Foundation
784193a29Schristos * Copyright (c) 2013 Mariusz Zaborski <oshogbo@FreeBSD.org>
884193a29Schristos * All rights reserved.
984193a29Schristos *
1084193a29Schristos * This software was developed by Pawel Jakub Dawidek under sponsorship from
1184193a29Schristos * the FreeBSD Foundation.
1284193a29Schristos *
1384193a29Schristos * Redistribution and use in source and binary forms, with or without
1484193a29Schristos * modification, are permitted provided that the following conditions
1584193a29Schristos * are met:
1684193a29Schristos * 1. Redistributions of source code must retain the above copyright
1784193a29Schristos * notice, this list of conditions and the following disclaimer.
1884193a29Schristos * 2. Redistributions in binary form must reproduce the above copyright
1984193a29Schristos * notice, this list of conditions and the following disclaimer in the
2084193a29Schristos * documentation and/or other materials provided with the distribution.
2184193a29Schristos *
2284193a29Schristos * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
2384193a29Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2484193a29Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2584193a29Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2684193a29Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2784193a29Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2884193a29Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2984193a29Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3084193a29Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3184193a29Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3284193a29Schristos * SUCH DAMAGE.
3384193a29Schristos */
3484193a29Schristos
3584193a29Schristos #include <sys/cdefs.h>
36*32302d25Schristos #ifdef __FreeBSD__
3784193a29Schristos __FBSDID("$FreeBSD: head/lib/libnv/msgio.c 326219 2017-11-26 02:00:33Z pfg $");
38*32302d25Schristos #else
39*32302d25Schristos __RCSID("$NetBSD: msgio.c,v 1.2 2018/09/08 14:02:15 christos Exp $");
40*32302d25Schristos #endif
4184193a29Schristos
4284193a29Schristos #include <sys/param.h>
4384193a29Schristos #include <sys/socket.h>
44*32302d25Schristos #include <sys/select.h>
4584193a29Schristos
4684193a29Schristos #include <errno.h>
4784193a29Schristos #include <fcntl.h>
4884193a29Schristos #include <stdbool.h>
4984193a29Schristos #include <stdint.h>
5084193a29Schristos #include <stdlib.h>
5184193a29Schristos #include <string.h>
5284193a29Schristos #include <unistd.h>
5384193a29Schristos
5484193a29Schristos #ifdef HAVE_PJDLOG
5584193a29Schristos #include <pjdlog.h>
5684193a29Schristos #endif
5784193a29Schristos
5884193a29Schristos #include "common_impl.h"
5984193a29Schristos #include "msgio.h"
6084193a29Schristos
6184193a29Schristos #ifndef HAVE_PJDLOG
6284193a29Schristos #include <assert.h>
6384193a29Schristos #define PJDLOG_ASSERT(...) assert(__VA_ARGS__)
6484193a29Schristos #define PJDLOG_RASSERT(expr, ...) assert(expr)
6584193a29Schristos #define PJDLOG_ABORT(...) abort()
6684193a29Schristos #endif
6784193a29Schristos
68*32302d25Schristos #ifdef __linux__
69*32302d25Schristos /* Linux: arbitrary size, but must be lower than SCM_MAX_FD. */
70*32302d25Schristos #define PKG_MAX_SIZE ((64U - 1) * CMSG_SPACE(sizeof(int)))
71*32302d25Schristos #else
7284193a29Schristos #define PKG_MAX_SIZE (MCLBYTES / CMSG_SPACE(sizeof(int)) - 1)
73*32302d25Schristos #endif
7484193a29Schristos
7584193a29Schristos static int
msghdr_add_fd(struct cmsghdr * cmsg,int fd)7684193a29Schristos msghdr_add_fd(struct cmsghdr *cmsg, int fd)
7784193a29Schristos {
7884193a29Schristos
7984193a29Schristos PJDLOG_ASSERT(fd >= 0);
8084193a29Schristos
8184193a29Schristos if (!fd_is_valid(fd)) {
8284193a29Schristos errno = EBADF;
8384193a29Schristos return (-1);
8484193a29Schristos }
8584193a29Schristos
8684193a29Schristos cmsg->cmsg_level = SOL_SOCKET;
8784193a29Schristos cmsg->cmsg_type = SCM_RIGHTS;
8884193a29Schristos cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
8984193a29Schristos bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
9084193a29Schristos
9184193a29Schristos return (0);
9284193a29Schristos }
9384193a29Schristos
9484193a29Schristos static int
msghdr_get_fd(struct cmsghdr * cmsg)9584193a29Schristos msghdr_get_fd(struct cmsghdr *cmsg)
9684193a29Schristos {
9784193a29Schristos int fd;
9884193a29Schristos
9984193a29Schristos if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET ||
10084193a29Schristos cmsg->cmsg_type != SCM_RIGHTS ||
10184193a29Schristos cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) {
10284193a29Schristos errno = EINVAL;
10384193a29Schristos return (-1);
10484193a29Schristos }
10584193a29Schristos
10684193a29Schristos bcopy(CMSG_DATA(cmsg), &fd, sizeof(fd));
10784193a29Schristos #ifndef MSG_CMSG_CLOEXEC
10884193a29Schristos /*
10984193a29Schristos * If the MSG_CMSG_CLOEXEC flag is not available we cannot set the
11084193a29Schristos * close-on-exec flag atomically, but we still want to set it for
11184193a29Schristos * consistency.
11284193a29Schristos */
11384193a29Schristos (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
11484193a29Schristos #endif
11584193a29Schristos
11684193a29Schristos return (fd);
11784193a29Schristos }
11884193a29Schristos
11984193a29Schristos static void
fd_wait(int fd,bool doread)12084193a29Schristos fd_wait(int fd, bool doread)
12184193a29Schristos {
12284193a29Schristos fd_set fds;
12384193a29Schristos
12484193a29Schristos PJDLOG_ASSERT(fd >= 0);
12584193a29Schristos
12684193a29Schristos FD_ZERO(&fds);
12784193a29Schristos FD_SET(fd, &fds);
12884193a29Schristos (void)select(fd + 1, doread ? &fds : NULL, doread ? NULL : &fds,
12984193a29Schristos NULL, NULL);
13084193a29Schristos }
13184193a29Schristos
13284193a29Schristos static int
msg_recv(int sock,struct msghdr * msg)13384193a29Schristos msg_recv(int sock, struct msghdr *msg)
13484193a29Schristos {
13584193a29Schristos int flags;
13684193a29Schristos
13784193a29Schristos PJDLOG_ASSERT(sock >= 0);
13884193a29Schristos
13984193a29Schristos #ifdef MSG_CMSG_CLOEXEC
14084193a29Schristos flags = MSG_CMSG_CLOEXEC;
14184193a29Schristos #else
14284193a29Schristos flags = 0;
14384193a29Schristos #endif
14484193a29Schristos
14584193a29Schristos for (;;) {
14684193a29Schristos fd_wait(sock, true);
14784193a29Schristos if (recvmsg(sock, msg, flags) == -1) {
14884193a29Schristos if (errno == EINTR)
14984193a29Schristos continue;
15084193a29Schristos return (-1);
15184193a29Schristos }
15284193a29Schristos break;
15384193a29Schristos }
15484193a29Schristos
15584193a29Schristos return (0);
15684193a29Schristos }
15784193a29Schristos
15884193a29Schristos static int
msg_send(int sock,const struct msghdr * msg)15984193a29Schristos msg_send(int sock, const struct msghdr *msg)
16084193a29Schristos {
16184193a29Schristos
16284193a29Schristos PJDLOG_ASSERT(sock >= 0);
16384193a29Schristos
16484193a29Schristos for (;;) {
16584193a29Schristos fd_wait(sock, false);
16684193a29Schristos if (sendmsg(sock, msg, 0) == -1) {
16784193a29Schristos if (errno == EINTR)
16884193a29Schristos continue;
16984193a29Schristos return (-1);
17084193a29Schristos }
17184193a29Schristos break;
17284193a29Schristos }
17384193a29Schristos
17484193a29Schristos return (0);
17584193a29Schristos }
17684193a29Schristos
177*32302d25Schristos #ifdef __FreeBSD__
17884193a29Schristos int
cred_send(int sock)17984193a29Schristos cred_send(int sock)
18084193a29Schristos {
18184193a29Schristos unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))];
18284193a29Schristos struct msghdr msg;
18384193a29Schristos struct cmsghdr *cmsg;
18484193a29Schristos struct iovec iov;
18584193a29Schristos uint8_t dummy;
18684193a29Schristos
18784193a29Schristos bzero(credbuf, sizeof(credbuf));
18884193a29Schristos bzero(&msg, sizeof(msg));
18984193a29Schristos bzero(&iov, sizeof(iov));
19084193a29Schristos
19184193a29Schristos /*
19284193a29Schristos * XXX: We send one byte along with the control message, because
19384193a29Schristos * setting msg_iov to NULL only works if this is the first
19484193a29Schristos * packet send over the socket. Once we send some data we
19584193a29Schristos * won't be able to send credentials anymore. This is most
19684193a29Schristos * likely a kernel bug.
19784193a29Schristos */
19884193a29Schristos dummy = 0;
19984193a29Schristos iov.iov_base = &dummy;
20084193a29Schristos iov.iov_len = sizeof(dummy);
20184193a29Schristos
20284193a29Schristos msg.msg_iov = &iov;
20384193a29Schristos msg.msg_iovlen = 1;
20484193a29Schristos msg.msg_control = credbuf;
20584193a29Schristos msg.msg_controllen = sizeof(credbuf);
20684193a29Schristos
20784193a29Schristos cmsg = CMSG_FIRSTHDR(&msg);
20884193a29Schristos cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
20984193a29Schristos cmsg->cmsg_level = SOL_SOCKET;
21084193a29Schristos cmsg->cmsg_type = SCM_CREDS;
21184193a29Schristos
21284193a29Schristos if (msg_send(sock, &msg) == -1)
21384193a29Schristos return (-1);
21484193a29Schristos
21584193a29Schristos return (0);
21684193a29Schristos }
21784193a29Schristos
21884193a29Schristos int
cred_recv(int sock,struct cmsgcred * cred)21984193a29Schristos cred_recv(int sock, struct cmsgcred *cred)
22084193a29Schristos {
22184193a29Schristos unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))];
22284193a29Schristos struct msghdr msg;
22384193a29Schristos struct cmsghdr *cmsg;
22484193a29Schristos struct iovec iov;
22584193a29Schristos uint8_t dummy;
22684193a29Schristos
22784193a29Schristos bzero(credbuf, sizeof(credbuf));
22884193a29Schristos bzero(&msg, sizeof(msg));
22984193a29Schristos bzero(&iov, sizeof(iov));
23084193a29Schristos
23184193a29Schristos iov.iov_base = &dummy;
23284193a29Schristos iov.iov_len = sizeof(dummy);
23384193a29Schristos
23484193a29Schristos msg.msg_iov = &iov;
23584193a29Schristos msg.msg_iovlen = 1;
23684193a29Schristos msg.msg_control = credbuf;
23784193a29Schristos msg.msg_controllen = sizeof(credbuf);
23884193a29Schristos
23984193a29Schristos if (msg_recv(sock, &msg) == -1)
24084193a29Schristos return (-1);
24184193a29Schristos
24284193a29Schristos cmsg = CMSG_FIRSTHDR(&msg);
24384193a29Schristos if (cmsg == NULL ||
24484193a29Schristos cmsg->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred)) ||
24584193a29Schristos cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDS) {
24684193a29Schristos errno = EINVAL;
24784193a29Schristos return (-1);
24884193a29Schristos }
24984193a29Schristos bcopy(CMSG_DATA(cmsg), cred, sizeof(*cred));
25084193a29Schristos
25184193a29Schristos return (0);
25284193a29Schristos }
253*32302d25Schristos #endif
25484193a29Schristos
25584193a29Schristos static int
fd_package_send(int sock,const int * fds,size_t nfds)25684193a29Schristos fd_package_send(int sock, const int *fds, size_t nfds)
25784193a29Schristos {
25884193a29Schristos struct msghdr msg;
25984193a29Schristos struct cmsghdr *cmsg;
26084193a29Schristos struct iovec iov;
26184193a29Schristos unsigned int i;
26284193a29Schristos int serrno, ret;
26384193a29Schristos uint8_t dummy;
26484193a29Schristos
26584193a29Schristos PJDLOG_ASSERT(sock >= 0);
26684193a29Schristos PJDLOG_ASSERT(fds != NULL);
26784193a29Schristos PJDLOG_ASSERT(nfds > 0);
26884193a29Schristos
26984193a29Schristos bzero(&msg, sizeof(msg));
27084193a29Schristos
27184193a29Schristos /*
27284193a29Schristos * XXX: Look into cred_send function for more details.
27384193a29Schristos */
27484193a29Schristos dummy = 0;
27584193a29Schristos iov.iov_base = &dummy;
27684193a29Schristos iov.iov_len = sizeof(dummy);
27784193a29Schristos
27884193a29Schristos msg.msg_iov = &iov;
27984193a29Schristos msg.msg_iovlen = 1;
28084193a29Schristos msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int));
28184193a29Schristos msg.msg_control = calloc(1, msg.msg_controllen);
28284193a29Schristos if (msg.msg_control == NULL)
28384193a29Schristos return (-1);
28484193a29Schristos
28584193a29Schristos ret = -1;
28684193a29Schristos
28784193a29Schristos for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL;
28884193a29Schristos i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) {
28984193a29Schristos if (msghdr_add_fd(cmsg, fds[i]) == -1)
29084193a29Schristos goto end;
29184193a29Schristos }
29284193a29Schristos
29384193a29Schristos if (msg_send(sock, &msg) == -1)
29484193a29Schristos goto end;
29584193a29Schristos
29684193a29Schristos ret = 0;
29784193a29Schristos end:
29884193a29Schristos serrno = errno;
29984193a29Schristos free(msg.msg_control);
30084193a29Schristos errno = serrno;
30184193a29Schristos return (ret);
30284193a29Schristos }
30384193a29Schristos
30484193a29Schristos static int
fd_package_recv(int sock,int * fds,size_t nfds)30584193a29Schristos fd_package_recv(int sock, int *fds, size_t nfds)
30684193a29Schristos {
30784193a29Schristos struct msghdr msg;
30884193a29Schristos struct cmsghdr *cmsg;
30984193a29Schristos unsigned int i;
31084193a29Schristos int serrno, ret;
31184193a29Schristos struct iovec iov;
31284193a29Schristos uint8_t dummy;
31384193a29Schristos
31484193a29Schristos PJDLOG_ASSERT(sock >= 0);
31584193a29Schristos PJDLOG_ASSERT(nfds > 0);
31684193a29Schristos PJDLOG_ASSERT(fds != NULL);
31784193a29Schristos
31884193a29Schristos bzero(&msg, sizeof(msg));
31984193a29Schristos bzero(&iov, sizeof(iov));
32084193a29Schristos
32184193a29Schristos /*
32284193a29Schristos * XXX: Look into cred_send function for more details.
32384193a29Schristos */
32484193a29Schristos iov.iov_base = &dummy;
32584193a29Schristos iov.iov_len = sizeof(dummy);
32684193a29Schristos
32784193a29Schristos msg.msg_iov = &iov;
32884193a29Schristos msg.msg_iovlen = 1;
32984193a29Schristos msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int));
33084193a29Schristos msg.msg_control = calloc(1, msg.msg_controllen);
33184193a29Schristos if (msg.msg_control == NULL)
33284193a29Schristos return (-1);
33384193a29Schristos
33484193a29Schristos ret = -1;
33584193a29Schristos
33684193a29Schristos if (msg_recv(sock, &msg) == -1)
33784193a29Schristos goto end;
33884193a29Schristos
33984193a29Schristos for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL;
34084193a29Schristos i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) {
34184193a29Schristos fds[i] = msghdr_get_fd(cmsg);
34284193a29Schristos if (fds[i] < 0)
34384193a29Schristos break;
34484193a29Schristos }
34584193a29Schristos
34684193a29Schristos if (cmsg != NULL || i < nfds) {
34784193a29Schristos int fd;
34884193a29Schristos
34984193a29Schristos /*
35084193a29Schristos * We need to close all received descriptors, even if we have
35184193a29Schristos * different control message (eg. SCM_CREDS) in between.
35284193a29Schristos */
35384193a29Schristos for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
35484193a29Schristos cmsg = CMSG_NXTHDR(&msg, cmsg)) {
35584193a29Schristos fd = msghdr_get_fd(cmsg);
35684193a29Schristos if (fd >= 0)
35784193a29Schristos close(fd);
35884193a29Schristos }
35984193a29Schristos errno = EINVAL;
36084193a29Schristos goto end;
36184193a29Schristos }
36284193a29Schristos
36384193a29Schristos ret = 0;
36484193a29Schristos end:
36584193a29Schristos serrno = errno;
36684193a29Schristos free(msg.msg_control);
36784193a29Schristos errno = serrno;
36884193a29Schristos return (ret);
36984193a29Schristos }
37084193a29Schristos
37184193a29Schristos int
fd_recv(int sock,int * fds,size_t nfds)37284193a29Schristos fd_recv(int sock, int *fds, size_t nfds)
37384193a29Schristos {
37484193a29Schristos unsigned int i, step, j;
37584193a29Schristos int ret, serrno;
37684193a29Schristos
37784193a29Schristos if (nfds == 0 || fds == NULL) {
37884193a29Schristos errno = EINVAL;
37984193a29Schristos return (-1);
38084193a29Schristos }
38184193a29Schristos
38284193a29Schristos ret = i = step = 0;
38384193a29Schristos while (i < nfds) {
38484193a29Schristos if (PKG_MAX_SIZE < nfds - i)
38584193a29Schristos step = PKG_MAX_SIZE;
38684193a29Schristos else
38784193a29Schristos step = nfds - i;
38884193a29Schristos ret = fd_package_recv(sock, fds + i, step);
38984193a29Schristos if (ret != 0) {
39084193a29Schristos /* Close all received descriptors. */
39184193a29Schristos serrno = errno;
39284193a29Schristos for (j = 0; j < i; j++)
39384193a29Schristos close(fds[j]);
39484193a29Schristos errno = serrno;
39584193a29Schristos break;
39684193a29Schristos }
39784193a29Schristos i += step;
39884193a29Schristos }
39984193a29Schristos
40084193a29Schristos return (ret);
40184193a29Schristos }
40284193a29Schristos
40384193a29Schristos int
fd_send(int sock,const int * fds,size_t nfds)40484193a29Schristos fd_send(int sock, const int *fds, size_t nfds)
40584193a29Schristos {
40684193a29Schristos unsigned int i, step;
40784193a29Schristos int ret;
40884193a29Schristos
40984193a29Schristos if (nfds == 0 || fds == NULL) {
41084193a29Schristos errno = EINVAL;
41184193a29Schristos return (-1);
41284193a29Schristos }
41384193a29Schristos
41484193a29Schristos ret = i = step = 0;
41584193a29Schristos while (i < nfds) {
41684193a29Schristos if (PKG_MAX_SIZE < nfds - i)
41784193a29Schristos step = PKG_MAX_SIZE;
41884193a29Schristos else
41984193a29Schristos step = nfds - i;
42084193a29Schristos ret = fd_package_send(sock, fds + i, step);
42184193a29Schristos if (ret != 0)
42284193a29Schristos break;
42384193a29Schristos i += step;
42484193a29Schristos }
42584193a29Schristos
42684193a29Schristos return (ret);
42784193a29Schristos }
42884193a29Schristos
42984193a29Schristos int
buf_send(int sock,void * buf,size_t size)43084193a29Schristos buf_send(int sock, void *buf, size_t size)
43184193a29Schristos {
43284193a29Schristos ssize_t done;
43384193a29Schristos unsigned char *ptr;
43484193a29Schristos
43584193a29Schristos PJDLOG_ASSERT(sock >= 0);
43684193a29Schristos PJDLOG_ASSERT(size > 0);
43784193a29Schristos PJDLOG_ASSERT(buf != NULL);
43884193a29Schristos
43984193a29Schristos ptr = buf;
44084193a29Schristos do {
44184193a29Schristos fd_wait(sock, false);
44284193a29Schristos done = send(sock, ptr, size, 0);
44384193a29Schristos if (done == -1) {
44484193a29Schristos if (errno == EINTR)
44584193a29Schristos continue;
44684193a29Schristos return (-1);
44784193a29Schristos } else if (done == 0) {
44884193a29Schristos errno = ENOTCONN;
44984193a29Schristos return (-1);
45084193a29Schristos }
45184193a29Schristos size -= done;
45284193a29Schristos ptr += done;
45384193a29Schristos } while (size > 0);
45484193a29Schristos
45584193a29Schristos return (0);
45684193a29Schristos }
45784193a29Schristos
45884193a29Schristos int
buf_recv(int sock,void * buf,size_t size)45984193a29Schristos buf_recv(int sock, void *buf, size_t size)
46084193a29Schristos {
46184193a29Schristos ssize_t done;
46284193a29Schristos unsigned char *ptr;
46384193a29Schristos
46484193a29Schristos PJDLOG_ASSERT(sock >= 0);
46584193a29Schristos PJDLOG_ASSERT(buf != NULL);
46684193a29Schristos
46784193a29Schristos ptr = buf;
46884193a29Schristos while (size > 0) {
46984193a29Schristos fd_wait(sock, true);
47084193a29Schristos done = recv(sock, ptr, size, 0);
47184193a29Schristos if (done == -1) {
47284193a29Schristos if (errno == EINTR)
47384193a29Schristos continue;
47484193a29Schristos return (-1);
47584193a29Schristos } else if (done == 0) {
47684193a29Schristos errno = ENOTCONN;
47784193a29Schristos return (-1);
47884193a29Schristos }
47984193a29Schristos size -= done;
48084193a29Schristos ptr += done;
48184193a29Schristos }
48284193a29Schristos
48384193a29Schristos return (0);
48484193a29Schristos }
485