Home | History | Annotate | Line # | Download | only in compat
      1 /*	$OpenBSD: imsg.c,v 1.42 2025/06/16 13:56:11 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 <stddef.h>
     26 #include <stdlib.h>
     27 #include <string.h>
     28 #include <unistd.h>
     29 
     30 #include "compat.h"
     31 #include "imsg.h"
     32 
     33 #define IMSG_ALLOW_FDPASS	0x01
     34 #define IMSG_FD_MARK		0x80000000U
     35 
     36 static struct ibuf	*imsg_parse_hdr(struct ibuf *, void *, int *);
     37 
     38 int
     39 imsgbuf_init(struct imsgbuf *imsgbuf, int fd)
     40 {
     41 	imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr,
     42 	    imsgbuf);
     43 	if (imsgbuf->w == NULL)
     44 		return (-1);
     45 	imsgbuf->pid = getpid();
     46 	imsgbuf->maxsize = MAX_IMSGSIZE;
     47 	imsgbuf->fd = fd;
     48 	imsgbuf->flags = 0;
     49 	return (0);
     50 }
     51 
     52 void
     53 imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf)
     54 {
     55 	imsgbuf->flags |= IMSG_ALLOW_FDPASS;
     56 }
     57 
     58 int
     59 imsgbuf_set_maxsize(struct imsgbuf *imsgbuf, uint32_t max)
     60 {
     61 	if (max > UINT32_MAX - IMSG_HEADER_SIZE) {
     62 		errno = ERANGE;
     63 		return (-1);
     64 	}
     65 	max += IMSG_HEADER_SIZE;
     66 	if (max & IMSG_FD_MARK) {
     67 		errno = EINVAL;
     68 		return (-1);
     69 	}
     70 	imsgbuf->maxsize = max;
     71 	return (0);
     72 }
     73 
     74 int
     75 imsgbuf_read(struct imsgbuf *imsgbuf)
     76 {
     77 	if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
     78 		return msgbuf_read(imsgbuf->fd, imsgbuf->w);
     79 	else
     80 		return ibuf_read(imsgbuf->fd, imsgbuf->w);
     81 }
     82 
     83 int
     84 imsgbuf_write(struct imsgbuf *imsgbuf)
     85 {
     86 	if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
     87 		return msgbuf_write(imsgbuf->fd, imsgbuf->w);
     88 	else
     89 		return ibuf_write(imsgbuf->fd, imsgbuf->w);
     90 }
     91 
     92 int
     93 imsgbuf_flush(struct imsgbuf *imsgbuf)
     94 {
     95 	while (imsgbuf_queuelen(imsgbuf) > 0) {
     96 		if (imsgbuf_write(imsgbuf) == -1)
     97 			return (-1);
     98 	}
     99 	return (0);
    100 }
    101 
    102 void
    103 imsgbuf_clear(struct imsgbuf *imsgbuf)
    104 {
    105 	msgbuf_free(imsgbuf->w);
    106 	imsgbuf->w = NULL;
    107 }
    108 
    109 uint32_t
    110 imsgbuf_queuelen(struct imsgbuf *imsgbuf)
    111 {
    112 	return msgbuf_queuelen(imsgbuf->w);
    113 }
    114 
    115 int
    116 imsgbuf_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
    117 {
    118 	struct imsg	 m;
    119 	struct ibuf	*buf;
    120 
    121 	if ((buf = msgbuf_get(imsgbuf->w)) == NULL)
    122 		return (0);
    123 
    124 	if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
    125 		return (-1);
    126 
    127 	if (ibuf_size(buf))
    128 		m.data = ibuf_data(buf);
    129 	else
    130 		m.data = NULL;
    131 	m.buf = buf;
    132 	m.hdr.len &= ~IMSG_FD_MARK;
    133 
    134 	*imsg = m;
    135 	return (1);
    136 }
    137 
    138 ssize_t
    139 imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
    140 {
    141 	int rv;
    142 
    143 	if ((rv = imsgbuf_get(imsgbuf, imsg)) != 1)
    144 		return rv;
    145 	return (imsg_get_len(imsg) + IMSG_HEADER_SIZE);
    146 }
    147 
    148 int
    149 imsg_ibufq_pop(struct ibufqueue *bufq, struct imsg *imsg)
    150 {
    151 	struct imsg	 m;
    152 	struct ibuf	*buf;
    153 
    154 	if ((buf = ibufq_pop(bufq)) == NULL)
    155 		return (0);
    156 
    157 	if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
    158 		return (-1);
    159 
    160 	if (ibuf_size(buf))
    161 		m.data = ibuf_data(buf);
    162 	else
    163 		m.data = NULL;
    164 	m.buf = buf;
    165 	m.hdr.len &= ~IMSG_FD_MARK;
    166 
    167 	*imsg = m;
    168 	return (1);
    169 }
    170 
    171 void
    172 imsg_ibufq_push(struct ibufqueue *bufq, struct imsg *imsg)
    173 {
    174 	ibuf_rewind(imsg->buf);
    175 	ibufq_push(bufq, imsg->buf);
    176 	memset(imsg, 0, sizeof(*imsg));
    177 }
    178 
    179 int
    180 imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
    181 {
    182 	if (ibuf_size(imsg->buf) == 0) {
    183 		errno = EBADMSG;
    184 		return (-1);
    185 	}
    186 	return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf);
    187 }
    188 
    189 int
    190 imsg_get_data(struct imsg *imsg, void *data, size_t len)
    191 {
    192 	if (len == 0) {
    193 		errno = EINVAL;
    194 		return (-1);
    195 	}
    196 	if (ibuf_size(imsg->buf) != len) {
    197 		errno = EBADMSG;
    198 		return (-1);
    199 	}
    200 	return ibuf_get(imsg->buf, data, len);
    201 }
    202 
    203 int
    204 imsg_get_buf(struct imsg *imsg, void *data, size_t len)
    205 {
    206 	return ibuf_get(imsg->buf, data, len);
    207 }
    208 
    209 int
    210 imsg_get_strbuf(struct imsg *imsg, char *str, size_t len)
    211 {
    212 	return ibuf_get_strbuf(imsg->buf, str, len);
    213 }
    214 
    215 int
    216 imsg_get_fd(struct imsg *imsg)
    217 {
    218 	return ibuf_fd_get(imsg->buf);
    219 }
    220 
    221 uint32_t
    222 imsg_get_id(struct imsg *imsg)
    223 {
    224 	return (imsg->hdr.peerid);
    225 }
    226 
    227 size_t
    228 imsg_get_len(struct imsg *imsg)
    229 {
    230 	return ibuf_size(imsg->buf);
    231 }
    232 
    233 pid_t
    234 imsg_get_pid(struct imsg *imsg)
    235 {
    236 	return (imsg->hdr.pid);
    237 }
    238 
    239 uint32_t
    240 imsg_get_type(struct imsg *imsg)
    241 {
    242 	return (imsg->hdr.type);
    243 }
    244 
    245 int
    246 imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
    247     int fd, const void *data, size_t datalen)
    248 {
    249 	struct ibuf	*wbuf;
    250 
    251 	if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
    252 		goto fail;
    253 
    254 	if (ibuf_add(wbuf, data, datalen) == -1)
    255 		goto fail;
    256 
    257 	ibuf_fd_set(wbuf, fd);
    258 	imsg_close(imsgbuf, wbuf);
    259 
    260 	return (1);
    261 
    262  fail:
    263 	ibuf_free(wbuf);
    264 	return (-1);
    265 }
    266 
    267 int
    268 imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
    269     int fd, const struct iovec *iov, int iovcnt)
    270 {
    271 	struct ibuf	*wbuf;
    272 	int		 i;
    273 	size_t		 datalen = 0;
    274 
    275 	for (i = 0; i < iovcnt; i++)
    276 		datalen += iov[i].iov_len;
    277 
    278 	if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
    279 		goto fail;
    280 
    281 	for (i = 0; i < iovcnt; i++)
    282 		if (ibuf_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
    283 			goto fail;
    284 
    285 	ibuf_fd_set(wbuf, fd);
    286 	imsg_close(imsgbuf, wbuf);
    287 
    288 	return (1);
    289 
    290  fail:
    291 	ibuf_free(wbuf);
    292 	return (-1);
    293 }
    294 
    295 /*
    296  * Enqueue imsg with payload from ibuf buf. fd passing is not possible
    297  * with this function.
    298  */
    299 int
    300 imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
    301     pid_t pid, struct ibuf *buf)
    302 {
    303 	struct ibuf	*hdrbuf = NULL;
    304 	struct imsg_hdr	 hdr;
    305 
    306 	if (ibuf_size(buf) + IMSG_HEADER_SIZE > imsgbuf->maxsize) {
    307 		errno = ERANGE;
    308 		goto fail;
    309 	}
    310 
    311 	hdr.type = type;
    312 	hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
    313 	hdr.peerid = id;
    314 	if ((hdr.pid = pid) == 0)
    315 		hdr.pid = imsgbuf->pid;
    316 
    317 	if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
    318 		goto fail;
    319 	if (ibuf_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
    320 		goto fail;
    321 
    322 	ibuf_close(imsgbuf->w, hdrbuf);
    323 	ibuf_close(imsgbuf->w, buf);
    324 	return (1);
    325 
    326  fail:
    327 	ibuf_free(buf);
    328 	return (-1);
    329 }
    330 
    331 /*
    332  * Forward imsg to another channel. Any attached fd is closed.
    333  */
    334 int
    335 imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
    336 {
    337 	struct ibuf	*wbuf;
    338 	size_t		 len;
    339 
    340 	ibuf_rewind(msg->buf);
    341 	ibuf_skip(msg->buf, sizeof(msg->hdr));
    342 	len = ibuf_size(msg->buf);
    343 
    344 	if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
    345 	    msg->hdr.pid, len)) == NULL)
    346 		return (-1);
    347 
    348 	if (len != 0) {
    349 		if (ibuf_add_ibuf(wbuf, msg->buf) == -1) {
    350 			ibuf_free(wbuf);
    351 			return (-1);
    352 		}
    353 	}
    354 
    355 	imsg_close(imsgbuf, wbuf);
    356 	return (1);
    357 }
    358 
    359 struct ibuf *
    360 imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
    361     size_t datalen)
    362 {
    363 	struct ibuf	*wbuf;
    364 	struct imsg_hdr	 hdr;
    365 
    366 	datalen += IMSG_HEADER_SIZE;
    367 	if (datalen > imsgbuf->maxsize) {
    368 		errno = ERANGE;
    369 		return (NULL);
    370 	}
    371 
    372 	hdr.len = 0;
    373 	hdr.type = type;
    374 	hdr.peerid = id;
    375 	if ((hdr.pid = pid) == 0)
    376 		hdr.pid = imsgbuf->pid;
    377 	if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL)
    378 		goto fail;
    379 	if (ibuf_add(wbuf, &hdr, sizeof(hdr)) == -1)
    380 		goto fail;
    381 
    382 	return (wbuf);
    383 
    384  fail:
    385 	ibuf_free(wbuf);
    386 	return (NULL);
    387 }
    388 
    389 int
    390 imsg_add(struct ibuf *msg, const void *data, size_t datalen)
    391 {
    392 	if (datalen)
    393 		if (ibuf_add(msg, data, datalen) == -1) {
    394 			ibuf_free(msg);
    395 			return (-1);
    396 		}
    397 	return (datalen);
    398 }
    399 
    400 void
    401 imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
    402 {
    403 	uint32_t len;
    404 
    405 	len = ibuf_size(msg);
    406 	if (ibuf_fd_avail(msg))
    407 		len |= IMSG_FD_MARK;
    408 	(void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len);
    409 	ibuf_close(imsgbuf->w, msg);
    410 }
    411 
    412 void
    413 imsg_free(struct imsg *imsg)
    414 {
    415 	ibuf_free(imsg->buf);
    416 }
    417 
    418 int
    419 imsg_set_maxsize(struct ibuf *msg, size_t max)
    420 {
    421 	if (max > UINT32_MAX - IMSG_HEADER_SIZE) {
    422 		errno = ERANGE;
    423 		return (-1);
    424 	}
    425 	return ibuf_set_maxsize(msg, max + IMSG_HEADER_SIZE);
    426 }
    427 
    428 static struct ibuf *
    429 imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd)
    430 {
    431 	struct imsgbuf *imsgbuf = arg;
    432 	struct imsg_hdr hdr;
    433 	struct ibuf *b;
    434 	uint32_t len;
    435 
    436 	if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1)
    437 		return (NULL);
    438 
    439 	len = hdr.len & ~IMSG_FD_MARK;
    440 
    441 	if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) {
    442 		errno = ERANGE;
    443 		return (NULL);
    444 	}
    445 	if ((b = ibuf_open(len)) == NULL)
    446 		return (NULL);
    447 	if (hdr.len & IMSG_FD_MARK) {
    448 		ibuf_fd_set(b, *fd);
    449 		*fd = -1;
    450 	}
    451 
    452 	return b;
    453 }
    454