1*a4ff2ce6Stb /* $OpenBSD: imsg.c,v 1.38 2024/11/29 04:35:13 tb Exp $ */ 2dfaf6462Snicm 3dfaf6462Snicm /* 44658a150Sclaudio * Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org> 5dfaf6462Snicm * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 6dfaf6462Snicm * 7dfaf6462Snicm * Permission to use, copy, modify, and distribute this software for any 8dfaf6462Snicm * purpose with or without fee is hereby granted, provided that the above 9dfaf6462Snicm * copyright notice and this permission notice appear in all copies. 10dfaf6462Snicm * 11dfaf6462Snicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12dfaf6462Snicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13dfaf6462Snicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14dfaf6462Snicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15dfaf6462Snicm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16dfaf6462Snicm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17dfaf6462Snicm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18dfaf6462Snicm */ 19dfaf6462Snicm 20429b3653Sderaadt #include <sys/types.h> 21dfaf6462Snicm #include <sys/queue.h> 22dfaf6462Snicm #include <sys/socket.h> 23dfaf6462Snicm #include <sys/uio.h> 24dfaf6462Snicm 25dfaf6462Snicm #include <errno.h> 26ff59764dSclaudio #include <stddef.h> 27dfaf6462Snicm #include <stdlib.h> 28dfaf6462Snicm #include <string.h> 29dfaf6462Snicm #include <unistd.h> 30dfaf6462Snicm 31dfaf6462Snicm #include "imsg.h" 32dfaf6462Snicm 3304d83f12Sclaudio #define IMSG_ALLOW_FDPASS 0x01 34ff59764dSclaudio #define IMSG_FD_MARK 0x80000000U 354658a150Sclaudio 36ff59764dSclaudio static struct ibuf *imsg_parse_hdr(struct ibuf *, void *, int *); 37dfaf6462Snicm 3804d83f12Sclaudio int 39d5c9e083Sclaudio imsgbuf_init(struct imsgbuf *imsgbuf, int fd) 40dfaf6462Snicm { 4104d83f12Sclaudio imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr, 4204d83f12Sclaudio imsgbuf); 4304d83f12Sclaudio if (imsgbuf->w == NULL) 4404d83f12Sclaudio return (-1); 4541d3eadeSclaudio imsgbuf->pid = getpid(); 4604d83f12Sclaudio imsgbuf->maxsize = MAX_IMSGSIZE; 4704d83f12Sclaudio imsgbuf->fd = fd; 4804d83f12Sclaudio imsgbuf->flags = 0; 4904d83f12Sclaudio return (0); 5004d83f12Sclaudio } 5104d83f12Sclaudio 5204d83f12Sclaudio void 5304d83f12Sclaudio imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf) 5404d83f12Sclaudio { 5504d83f12Sclaudio imsgbuf->flags |= IMSG_ALLOW_FDPASS; 5604d83f12Sclaudio } 5704d83f12Sclaudio 58ff59764dSclaudio int 5904d83f12Sclaudio imsgbuf_set_maxsize(struct imsgbuf *imsgbuf, uint32_t maxsize) 6004d83f12Sclaudio { 61ff59764dSclaudio if (maxsize < IMSG_HEADER_SIZE || maxsize & IMSG_FD_MARK) { 62ff59764dSclaudio errno = EINVAL; 63ff59764dSclaudio return (-1); 64ff59764dSclaudio } 6504d83f12Sclaudio imsgbuf->maxsize = maxsize; 66ff59764dSclaudio return (0); 67dfaf6462Snicm } 68dfaf6462Snicm 69c362c6deSclaudio int 70d5c9e083Sclaudio imsgbuf_read(struct imsgbuf *imsgbuf) 71dfaf6462Snicm { 7204d83f12Sclaudio if (imsgbuf->flags & IMSG_ALLOW_FDPASS) 7304d83f12Sclaudio return msgbuf_read(imsgbuf->fd, imsgbuf->w); 7404d83f12Sclaudio else 7504d83f12Sclaudio return ibuf_read(imsgbuf->fd, imsgbuf->w); 76dfaf6462Snicm } 77dfaf6462Snicm 7844c57f07Sclaudio int 79d5c9e083Sclaudio imsgbuf_write(struct imsgbuf *imsgbuf) 8044c57f07Sclaudio { 8104d83f12Sclaudio if (imsgbuf->flags & IMSG_ALLOW_FDPASS) 82156c3c53Sclaudio return msgbuf_write(imsgbuf->fd, imsgbuf->w); 8304d83f12Sclaudio else 8404d83f12Sclaudio return ibuf_write(imsgbuf->fd, imsgbuf->w); 8544c57f07Sclaudio } 8644c57f07Sclaudio 87d5c9e083Sclaudio int 88d5c9e083Sclaudio imsgbuf_flush(struct imsgbuf *imsgbuf) 89d5c9e083Sclaudio { 90156c3c53Sclaudio while (imsgbuf_queuelen(imsgbuf) > 0) { 91d5c9e083Sclaudio if (imsgbuf_write(imsgbuf) == -1) 92d5c9e083Sclaudio return (-1); 93156c3c53Sclaudio } 94d5c9e083Sclaudio return (0); 95d5c9e083Sclaudio } 96d5c9e083Sclaudio 97d5c9e083Sclaudio void 98d5c9e083Sclaudio imsgbuf_clear(struct imsgbuf *imsgbuf) 99d5c9e083Sclaudio { 100156c3c53Sclaudio msgbuf_free(imsgbuf->w); 101156c3c53Sclaudio imsgbuf->w = NULL; 102d5c9e083Sclaudio } 103d5c9e083Sclaudio 104a17071b2Sclaudio uint32_t 105a17071b2Sclaudio imsgbuf_queuelen(struct imsgbuf *imsgbuf) 106a17071b2Sclaudio { 107156c3c53Sclaudio return msgbuf_queuelen(imsgbuf->w); 108a17071b2Sclaudio } 109a17071b2Sclaudio 110dfaf6462Snicm ssize_t 11141d3eadeSclaudio imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg) 112dfaf6462Snicm { 1134658a150Sclaudio struct imsg m; 11404d83f12Sclaudio struct ibuf *buf; 115dfaf6462Snicm 11604d83f12Sclaudio if ((buf = msgbuf_get(imsgbuf->w)) == NULL) 117dfaf6462Snicm return (0); 118dfaf6462Snicm 11904d83f12Sclaudio if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1) 120dfaf6462Snicm return (-1); 1214658a150Sclaudio 12204d83f12Sclaudio if (ibuf_size(buf)) 12304d83f12Sclaudio m.data = ibuf_data(buf); 12404d83f12Sclaudio else 1254658a150Sclaudio m.data = NULL; 12604d83f12Sclaudio m.buf = buf; 127ff59764dSclaudio m.hdr.len &= ~IMSG_FD_MARK; 128dfaf6462Snicm 1294658a150Sclaudio *imsg = m; 13004d83f12Sclaudio return (ibuf_size(buf) + IMSG_HEADER_SIZE); 131dfaf6462Snicm } 132dfaf6462Snicm 133dfaf6462Snicm int 1344658a150Sclaudio imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf) 1354658a150Sclaudio { 13604d83f12Sclaudio if (ibuf_size(imsg->buf) == 0) { 1374658a150Sclaudio errno = EBADMSG; 1384658a150Sclaudio return (-1); 1394658a150Sclaudio } 1404658a150Sclaudio return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf); 1414658a150Sclaudio } 1424658a150Sclaudio 1434658a150Sclaudio int 1444658a150Sclaudio imsg_get_data(struct imsg *imsg, void *data, size_t len) 1454658a150Sclaudio { 1464658a150Sclaudio if (len == 0) { 1474658a150Sclaudio errno = EINVAL; 1484658a150Sclaudio return (-1); 1494658a150Sclaudio } 15004d83f12Sclaudio if (ibuf_size(imsg->buf) != len) { 1514658a150Sclaudio errno = EBADMSG; 1524658a150Sclaudio return (-1); 1534658a150Sclaudio } 1544658a150Sclaudio return ibuf_get(imsg->buf, data, len); 1554658a150Sclaudio } 1564658a150Sclaudio 1574658a150Sclaudio int 1584658a150Sclaudio imsg_get_fd(struct imsg *imsg) 1594658a150Sclaudio { 1608d7d8ae7Sclaudio return ibuf_fd_get(imsg->buf); 1614658a150Sclaudio } 1624658a150Sclaudio 1634658a150Sclaudio uint32_t 1644658a150Sclaudio imsg_get_id(struct imsg *imsg) 1654658a150Sclaudio { 1664658a150Sclaudio return (imsg->hdr.peerid); 1674658a150Sclaudio } 1684658a150Sclaudio 1694658a150Sclaudio size_t 1704658a150Sclaudio imsg_get_len(struct imsg *imsg) 1714658a150Sclaudio { 1724658a150Sclaudio return ibuf_size(imsg->buf); 1734658a150Sclaudio } 1744658a150Sclaudio 1754658a150Sclaudio pid_t 1764658a150Sclaudio imsg_get_pid(struct imsg *imsg) 1774658a150Sclaudio { 1784658a150Sclaudio return (imsg->hdr.pid); 1794658a150Sclaudio } 1804658a150Sclaudio 1814658a150Sclaudio uint32_t 1824658a150Sclaudio imsg_get_type(struct imsg *imsg) 1834658a150Sclaudio { 1844658a150Sclaudio return (imsg->hdr.type); 1854658a150Sclaudio } 1864658a150Sclaudio 1874658a150Sclaudio int 188c1465023Sclaudio imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, 1894658a150Sclaudio int fd, const void *data, size_t datalen) 190dfaf6462Snicm { 191dfaf6462Snicm struct ibuf *wbuf; 192dfaf6462Snicm 193c1465023Sclaudio if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL) 194dfaf6462Snicm return (-1); 195dfaf6462Snicm 196dfaf6462Snicm if (imsg_add(wbuf, data, datalen) == -1) 197dfaf6462Snicm return (-1); 198dfaf6462Snicm 19919778535Sclaudio ibuf_fd_set(wbuf, fd); 20041d3eadeSclaudio imsg_close(imsgbuf, wbuf); 201dfaf6462Snicm 202dfaf6462Snicm return (1); 203dfaf6462Snicm } 204dfaf6462Snicm 205dfaf6462Snicm int 206c1465023Sclaudio imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, 207c1465023Sclaudio int fd, const struct iovec *iov, int iovcnt) 208dfaf6462Snicm { 209dfaf6462Snicm struct ibuf *wbuf; 2104658a150Sclaudio int i; 2114658a150Sclaudio size_t datalen = 0; 212dfaf6462Snicm 213dfaf6462Snicm for (i = 0; i < iovcnt; i++) 214dfaf6462Snicm datalen += iov[i].iov_len; 215dfaf6462Snicm 216c1465023Sclaudio if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL) 217dfaf6462Snicm return (-1); 218dfaf6462Snicm 219dfaf6462Snicm for (i = 0; i < iovcnt; i++) 220dfaf6462Snicm if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) 221dfaf6462Snicm return (-1); 222dfaf6462Snicm 22319778535Sclaudio ibuf_fd_set(wbuf, fd); 22441d3eadeSclaudio imsg_close(imsgbuf, wbuf); 225dfaf6462Snicm 226dfaf6462Snicm return (1); 227dfaf6462Snicm } 228dfaf6462Snicm 2294658a150Sclaudio /* 2304658a150Sclaudio * Enqueue imsg with payload from ibuf buf. fd passing is not possible 2314658a150Sclaudio * with this function. 2324658a150Sclaudio */ 23319778535Sclaudio int 234c1465023Sclaudio imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, 23519778535Sclaudio pid_t pid, struct ibuf *buf) 23619778535Sclaudio { 2374658a150Sclaudio struct ibuf *hdrbuf = NULL; 23819778535Sclaudio struct imsg_hdr hdr; 23919778535Sclaudio int save_errno; 24019778535Sclaudio 24104d83f12Sclaudio if (ibuf_size(buf) + IMSG_HEADER_SIZE > imsgbuf->maxsize) { 24219778535Sclaudio errno = ERANGE; 24319778535Sclaudio goto fail; 24419778535Sclaudio } 24519778535Sclaudio 24619778535Sclaudio hdr.type = type; 24719778535Sclaudio hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE; 248c1465023Sclaudio hdr.peerid = id; 24919778535Sclaudio if ((hdr.pid = pid) == 0) 25041d3eadeSclaudio hdr.pid = imsgbuf->pid; 25119778535Sclaudio 2524658a150Sclaudio if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL) 25319778535Sclaudio goto fail; 2544658a150Sclaudio if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1) 25519778535Sclaudio goto fail; 25619778535Sclaudio 257156c3c53Sclaudio ibuf_close(imsgbuf->w, hdrbuf); 258156c3c53Sclaudio ibuf_close(imsgbuf->w, buf); 25919778535Sclaudio return (1); 26019778535Sclaudio 26119778535Sclaudio fail: 26219778535Sclaudio save_errno = errno; 26319778535Sclaudio ibuf_free(buf); 2644658a150Sclaudio ibuf_free(hdrbuf); 26519778535Sclaudio errno = save_errno; 26619778535Sclaudio return (-1); 26719778535Sclaudio } 26819778535Sclaudio 2694658a150Sclaudio /* 2704658a150Sclaudio * Forward imsg to another channel. Any attached fd is closed. 2714658a150Sclaudio */ 2724658a150Sclaudio int 2734658a150Sclaudio imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg) 2744658a150Sclaudio { 2754658a150Sclaudio struct ibuf *wbuf; 276ff59764dSclaudio size_t len; 2774658a150Sclaudio 2784658a150Sclaudio ibuf_rewind(msg->buf); 27904d83f12Sclaudio ibuf_skip(msg->buf, sizeof(msg->hdr)); 2804658a150Sclaudio len = ibuf_size(msg->buf); 2814658a150Sclaudio 2824658a150Sclaudio if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid, 2834658a150Sclaudio msg->hdr.pid, len)) == NULL) 2844658a150Sclaudio return (-1); 2854658a150Sclaudio 286ff59764dSclaudio if (len != 0) { 2873b84e804Sclaudio if (ibuf_add_ibuf(wbuf, msg->buf) == -1) { 2884658a150Sclaudio ibuf_free(wbuf); 2894658a150Sclaudio return (-1); 2904658a150Sclaudio } 2914658a150Sclaudio } 2924658a150Sclaudio 2934658a150Sclaudio imsg_close(imsgbuf, wbuf); 2944658a150Sclaudio return (1); 2954658a150Sclaudio } 2964658a150Sclaudio 297dfaf6462Snicm struct ibuf * 298c1465023Sclaudio imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, 2994658a150Sclaudio size_t datalen) 300dfaf6462Snicm { 301dfaf6462Snicm struct ibuf *wbuf; 302dfaf6462Snicm struct imsg_hdr hdr; 303dfaf6462Snicm 304dfaf6462Snicm datalen += IMSG_HEADER_SIZE; 30504d83f12Sclaudio if (datalen > imsgbuf->maxsize) { 306dfaf6462Snicm errno = ERANGE; 307dfaf6462Snicm return (NULL); 308dfaf6462Snicm } 309dfaf6462Snicm 310dfaf6462Snicm hdr.type = type; 311c1465023Sclaudio hdr.peerid = id; 312dfaf6462Snicm if ((hdr.pid = pid) == 0) 31341d3eadeSclaudio hdr.pid = imsgbuf->pid; 31404d83f12Sclaudio if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL) { 315dfaf6462Snicm return (NULL); 316dfaf6462Snicm } 317dfaf6462Snicm if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) 318dfaf6462Snicm return (NULL); 319dfaf6462Snicm 320dfaf6462Snicm return (wbuf); 321dfaf6462Snicm } 322dfaf6462Snicm 323dfaf6462Snicm int 3244658a150Sclaudio imsg_add(struct ibuf *msg, const void *data, size_t datalen) 325dfaf6462Snicm { 326dfaf6462Snicm if (datalen) 327dfaf6462Snicm if (ibuf_add(msg, data, datalen) == -1) { 328dfaf6462Snicm ibuf_free(msg); 329dfaf6462Snicm return (-1); 330dfaf6462Snicm } 331dfaf6462Snicm return (datalen); 332dfaf6462Snicm } 333dfaf6462Snicm 334dfaf6462Snicm void 33541d3eadeSclaudio imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg) 336dfaf6462Snicm { 337ff59764dSclaudio uint32_t len; 338dfaf6462Snicm 339ff59764dSclaudio len = ibuf_size(msg); 340ff59764dSclaudio if (ibuf_fd_avail(msg)) 341ff59764dSclaudio len |= IMSG_FD_MARK; 342ff59764dSclaudio (void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len); 343156c3c53Sclaudio ibuf_close(imsgbuf->w, msg); 344dfaf6462Snicm } 345dfaf6462Snicm 346dfaf6462Snicm void 347dfaf6462Snicm imsg_free(struct imsg *imsg) 348dfaf6462Snicm { 3494658a150Sclaudio ibuf_free(imsg->buf); 350dfaf6462Snicm } 351dfaf6462Snicm 352ff59764dSclaudio static struct ibuf * 353ff59764dSclaudio imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd) 354dfaf6462Snicm { 35504d83f12Sclaudio struct imsgbuf *imsgbuf = arg; 35604d83f12Sclaudio struct imsg_hdr hdr; 357ff59764dSclaudio struct ibuf *b; 358ff59764dSclaudio uint32_t len; 359dfaf6462Snicm 36004d83f12Sclaudio if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1) 361ff59764dSclaudio return (NULL); 362ff59764dSclaudio 363ff59764dSclaudio len = hdr.len & ~IMSG_FD_MARK; 364ff59764dSclaudio 365ff59764dSclaudio if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) { 36604d83f12Sclaudio errno = ERANGE; 367ff59764dSclaudio return (NULL); 36804d83f12Sclaudio } 369ff59764dSclaudio if ((b = ibuf_open(len)) == NULL) 370ff59764dSclaudio return (NULL); 371ff59764dSclaudio if (hdr.len & IMSG_FD_MARK) { 372ff59764dSclaudio ibuf_fd_set(b, *fd); 373ff59764dSclaudio *fd = -1; 374ff59764dSclaudio } 375ff59764dSclaudio 376ff59764dSclaudio return b; 377dfaf6462Snicm } 378