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