Home | History | Annotate | Line # | Download | only in compat
imsg.c revision 1.11
      1 /*	$OpenBSD: imsg.c,v 1.23 2023/12/12 15:47:41 claudio Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2023 Claudio Jeker <claudio (at) openbsd.org>
      5  * Copyright (c) 2003, 2004 Henning Brauer <henning (at) openbsd.org>
      6  *
      7  * Permission to use, copy, modify, and distribute this software for any
      8  * purpose with or without fee is hereby granted, provided that the above
      9  * copyright notice and this permission notice appear in all copies.
     10  *
     11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 
     20 #include <sys/types.h>
     21 #include <sys/socket.h>
     22 #include <sys/uio.h>
     23 
     24 #include <errno.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <unistd.h>
     28 
     29 #include "compat.h"
     30 #include "imsg.h"
     31 
     32 struct imsg_fd {
     33 	TAILQ_ENTRY(imsg_fd)	entry;
     34 	int			fd;
     35 };
     36 
     37 int	 imsg_fd_overhead = 0;
     38 
     39 static int	 imsg_dequeue_fd(struct imsgbuf *);
     40 
     41 void
     42 imsg_init(struct imsgbuf *imsgbuf, int fd)
     43 {
     44 	msgbuf_init(&imsgbuf->w);
     45 	memset(&imsgbuf->r, 0, sizeof(imsgbuf->r));
     46 	imsgbuf->fd = fd;
     47 	imsgbuf->w.fd = fd;
     48 	imsgbuf->pid = getpid();
     49 	TAILQ_INIT(&imsgbuf->fds);
     50 }
     51 
     52 ssize_t
     53 imsg_read(struct imsgbuf *imsgbuf)
     54 {
     55 	struct msghdr		 msg;
     56 	struct cmsghdr		*cmsg;
     57 	union {
     58 		struct cmsghdr hdr;
     59 		char	buf[CMSG_SPACE(sizeof(int) * 1)];
     60 	} cmsgbuf;
     61 	struct iovec		 iov;
     62 	ssize_t			 n = -1;
     63 	int			 fd;
     64 	struct imsg_fd		*ifd;
     65 
     66 	memset(&msg, 0, sizeof(msg));
     67 	memset(&cmsgbuf, 0, sizeof(cmsgbuf));
     68 
     69 	iov.iov_base = imsgbuf->r.buf + imsgbuf->r.wpos;
     70 	iov.iov_len = sizeof(imsgbuf->r.buf) - imsgbuf->r.wpos;
     71 	msg.msg_iov = &iov;
     72 	msg.msg_iovlen = 1;
     73 	msg.msg_control = &cmsgbuf.buf;
     74 	msg.msg_controllen = CMSG_SPACE(sizeof(int) * 16);
     75 
     76 	if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
     77 		return (-1);
     78 
     79 again:
     80 	if (getdtablecount() + imsg_fd_overhead +
     81 	    (int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))
     82 	    >= getdtablesize()) {
     83 		errno = EAGAIN;
     84 		free(ifd);
     85 		return (-1);
     86 	}
     87 
     88 	if ((n = recvmsg(imsgbuf->fd, &msg, 0)) == -1) {
     89 		if (errno == EINTR)
     90 			goto again;
     91 		goto fail;
     92 	}
     93 
     94 	imsgbuf->r.wpos += n;
     95 
     96 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
     97 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
     98 		if (cmsg->cmsg_level == SOL_SOCKET &&
     99 		    cmsg->cmsg_type == SCM_RIGHTS) {
    100 			int i;
    101 			int j;
    102 
    103 			/*
    104 			 * We only accept one file descriptor.  Due to C
    105 			 * padding rules, our control buffer might contain
    106 			 * more than one fd, and we must close them.
    107 			 */
    108 			j = ((char *)cmsg + cmsg->cmsg_len -
    109 			    (char *)CMSG_DATA(cmsg)) / sizeof(int);
    110 			for (i = 0; i < j; i++) {
    111 				fd = ((int *)CMSG_DATA(cmsg))[i];
    112 				if (ifd != NULL) {
    113 					ifd->fd = fd;
    114 					TAILQ_INSERT_TAIL(&imsgbuf->fds, ifd,
    115 					    entry);
    116 					ifd = NULL;
    117 				} else
    118 					close(fd);
    119 			}
    120 		}
    121 		/* we do not handle other ctl data level */
    122 	}
    123 
    124 fail:
    125 	free(ifd);
    126 	return (n);
    127 }
    128 
    129 ssize_t
    130 imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
    131 {
    132 	struct imsg		 m;
    133 	size_t			 av, left, datalen;
    134 
    135 	av = imsgbuf->r.wpos;
    136 
    137 	if (IMSG_HEADER_SIZE > av)
    138 		return (0);
    139 
    140 	memcpy(&m.hdr, imsgbuf->r.buf, sizeof(m.hdr));
    141 	if (m.hdr.len < IMSG_HEADER_SIZE ||
    142 	    m.hdr.len > MAX_IMSGSIZE) {
    143 		errno = ERANGE;
    144 		return (-1);
    145 	}
    146 	if (m.hdr.len > av)
    147 		return (0);
    148 
    149 	m.fd = -1;
    150 	m.buf = NULL;
    151 	m.data = NULL;
    152 
    153 	datalen = m.hdr.len - IMSG_HEADER_SIZE;
    154 	imsgbuf->r.rptr = imsgbuf->r.buf + IMSG_HEADER_SIZE;
    155 	if (datalen != 0) {
    156 		if ((m.buf = ibuf_open(datalen)) == NULL)
    157 			return (-1);
    158 		if (ibuf_add(m.buf, imsgbuf->r.rptr, datalen) == -1) {
    159 			/* this should never fail */
    160 			ibuf_free(m.buf);
    161 			return (-1);
    162 		}
    163 		m.data = ibuf_data(m.buf);
    164 	}
    165 
    166 	if (m.hdr.flags & IMSGF_HASFD)
    167 		m.fd = imsg_dequeue_fd(imsgbuf);
    168 
    169 	if (m.hdr.len < av) {
    170 		left = av - m.hdr.len;
    171 		memmove(&imsgbuf->r.buf, imsgbuf->r.buf + m.hdr.len, left);
    172 		imsgbuf->r.wpos = left;
    173 	} else
    174 		imsgbuf->r.wpos = 0;
    175 
    176 	*imsg = m;
    177 	return (datalen + IMSG_HEADER_SIZE);
    178 }
    179 
    180 int
    181 imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
    182 {
    183 	if (imsg->buf == NULL) {
    184 		errno = EBADMSG;
    185 		return (-1);
    186 	}
    187 	return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf);
    188 }
    189 
    190 int
    191 imsg_get_data(struct imsg *imsg, void *data, size_t len)
    192 {
    193 	if (len == 0) {
    194 		errno = EINVAL;
    195 		return (-1);
    196 	}
    197 	if (imsg->buf == NULL || ibuf_size(imsg->buf) != len) {
    198 		errno = EBADMSG;
    199 		return (-1);
    200 	}
    201 	return ibuf_get(imsg->buf, data, len);
    202 }
    203 
    204 int
    205 imsg_get_fd(struct imsg *imsg)
    206 {
    207 	int fd = imsg->fd;
    208 
    209 	imsg->fd = -1;
    210 	return fd;
    211 }
    212 
    213 uint32_t
    214 imsg_get_id(struct imsg *imsg)
    215 {
    216 	return (imsg->hdr.peerid);
    217 }
    218 
    219 size_t
    220 imsg_get_len(struct imsg *imsg)
    221 {
    222 	if (imsg->buf == NULL)
    223 		return 0;
    224 	return ibuf_size(imsg->buf);
    225 }
    226 
    227 pid_t
    228 imsg_get_pid(struct imsg *imsg)
    229 {
    230 	return (imsg->hdr.pid);
    231 }
    232 
    233 uint32_t
    234 imsg_get_type(struct imsg *imsg)
    235 {
    236 	return (imsg->hdr.type);
    237 }
    238 
    239 int
    240 imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
    241     int fd, const void *data, size_t datalen)
    242 {
    243 	struct ibuf	*wbuf;
    244 
    245 	if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
    246 		return (-1);
    247 
    248 	if (imsg_add(wbuf, data, datalen) == -1)
    249 		return (-1);
    250 
    251 	ibuf_fd_set(wbuf, fd);
    252 	imsg_close(imsgbuf, wbuf);
    253 
    254 	return (1);
    255 }
    256 
    257 int
    258 imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
    259     int fd, const struct iovec *iov, int iovcnt)
    260 {
    261 	struct ibuf	*wbuf;
    262 	int		 i;
    263 	size_t		 datalen = 0;
    264 
    265 	for (i = 0; i < iovcnt; i++)
    266 		datalen += iov[i].iov_len;
    267 
    268 	if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
    269 		return (-1);
    270 
    271 	for (i = 0; i < iovcnt; i++)
    272 		if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
    273 			return (-1);
    274 
    275 	ibuf_fd_set(wbuf, fd);
    276 	imsg_close(imsgbuf, wbuf);
    277 
    278 	return (1);
    279 }
    280 
    281 /*
    282  * Enqueue imsg with payload from ibuf buf. fd passing is not possible
    283  * with this function.
    284  */
    285 int
    286 imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
    287     pid_t pid, struct ibuf *buf)
    288 {
    289 	struct ibuf	*hdrbuf = NULL;
    290 	struct imsg_hdr	 hdr;
    291 	int save_errno;
    292 
    293 	if (ibuf_size(buf) + IMSG_HEADER_SIZE > MAX_IMSGSIZE) {
    294 		errno = ERANGE;
    295 		goto fail;
    296 	}
    297 
    298 	hdr.type = type;
    299 	hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
    300 	hdr.flags = 0;
    301 	hdr.peerid = id;
    302 	if ((hdr.pid = pid) == 0)
    303 		hdr.pid = imsgbuf->pid;
    304 
    305 	if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
    306 		goto fail;
    307 	if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
    308 		goto fail;
    309 
    310 	ibuf_close(&imsgbuf->w, hdrbuf);
    311 	ibuf_close(&imsgbuf->w, buf);
    312 	return (1);
    313 
    314  fail:
    315 	save_errno = errno;
    316 	ibuf_free(buf);
    317 	errno = save_errno;
    318 	return (-1);
    319 }
    320 
    321 /*
    322  * Forward imsg to another channel. Any attached fd is closed.
    323  */
    324 int
    325 imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
    326 {
    327 	struct ibuf	*wbuf;
    328 	size_t		 len = 0;
    329 
    330 	if (msg->fd != -1) {
    331 		close(msg->fd);
    332 		msg->fd = -1;
    333 	}
    334 
    335 	if (msg->buf != NULL) {
    336 		ibuf_rewind(msg->buf);
    337 		len = ibuf_size(msg->buf);
    338 	}
    339 
    340 	if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
    341 	    msg->hdr.pid, len)) == NULL)
    342 		return (-1);
    343 
    344 	if (msg->buf != NULL) {
    345 		if (ibuf_add_buf(wbuf, msg->buf) == -1) {
    346 			ibuf_free(wbuf);
    347 			return (-1);
    348 		}
    349 	}
    350 
    351 	imsg_close(imsgbuf, wbuf);
    352 	return (1);
    353 }
    354 
    355 struct ibuf *
    356 imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
    357     size_t datalen)
    358 {
    359 	struct ibuf	*wbuf;
    360 	struct imsg_hdr	 hdr;
    361 
    362 	datalen += IMSG_HEADER_SIZE;
    363 	if (datalen > MAX_IMSGSIZE) {
    364 		errno = ERANGE;
    365 		return (NULL);
    366 	}
    367 
    368 	hdr.type = type;
    369 	hdr.flags = 0;
    370 	hdr.peerid = id;
    371 	if ((hdr.pid = pid) == 0)
    372 		hdr.pid = imsgbuf->pid;
    373 	if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
    374 		return (NULL);
    375 	}
    376 	if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
    377 		return (NULL);
    378 
    379 	return (wbuf);
    380 }
    381 
    382 int
    383 imsg_add(struct ibuf *msg, const void *data, size_t datalen)
    384 {
    385 	if (datalen)
    386 		if (ibuf_add(msg, data, datalen) == -1) {
    387 			ibuf_free(msg);
    388 			return (-1);
    389 		}
    390 	return (datalen);
    391 }
    392 
    393 void
    394 imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
    395 {
    396 	struct imsg_hdr	*hdr;
    397 
    398 	hdr = (struct imsg_hdr *)msg->buf;
    399 
    400 	hdr->flags &= ~IMSGF_HASFD;
    401 	if (ibuf_fd_avail(msg))
    402 		hdr->flags |= IMSGF_HASFD;
    403 	hdr->len = ibuf_size(msg);
    404 
    405 	ibuf_close(&imsgbuf->w, msg);
    406 }
    407 
    408 void
    409 imsg_free(struct imsg *imsg)
    410 {
    411 	ibuf_free(imsg->buf);
    412 }
    413 
    414 static int
    415 imsg_dequeue_fd(struct imsgbuf *imsgbuf)
    416 {
    417 	int		 fd;
    418 	struct imsg_fd	*ifd;
    419 
    420 	if ((ifd = TAILQ_FIRST(&imsgbuf->fds)) == NULL)
    421 		return (-1);
    422 
    423 	fd = ifd->fd;
    424 	TAILQ_REMOVE(&imsgbuf->fds, ifd, entry);
    425 	free(ifd);
    426 
    427 	return (fd);
    428 }
    429 
    430 int
    431 imsg_flush(struct imsgbuf *imsgbuf)
    432 {
    433 	while (imsgbuf->w.queued)
    434 		if (msgbuf_write(&imsgbuf->w) <= 0)
    435 			return (-1);
    436 	return (0);
    437 }
    438 
    439 void
    440 imsg_clear(struct imsgbuf *imsgbuf)
    441 {
    442 	int	fd;
    443 
    444 	msgbuf_clear(&imsgbuf->w);
    445 	while ((fd = imsg_dequeue_fd(imsgbuf)) != -1)
    446 		close(fd);
    447 }
    448