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