1 /* $OpenBSD: imsg.c,v 1.38 2024/11/29 04:35:13 tb 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 <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 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 maxsize) 60 { 61 if (maxsize < IMSG_HEADER_SIZE || maxsize & IMSG_FD_MARK) { 62 errno = EINVAL; 63 return (-1); 64 } 65 imsgbuf->maxsize = maxsize; 66 return (0); 67 } 68 69 int 70 imsgbuf_read(struct imsgbuf *imsgbuf) 71 { 72 if (imsgbuf->flags & IMSG_ALLOW_FDPASS) 73 return msgbuf_read(imsgbuf->fd, imsgbuf->w); 74 else 75 return ibuf_read(imsgbuf->fd, imsgbuf->w); 76 } 77 78 int 79 imsgbuf_write(struct imsgbuf *imsgbuf) 80 { 81 if (imsgbuf->flags & IMSG_ALLOW_FDPASS) 82 return msgbuf_write(imsgbuf->fd, imsgbuf->w); 83 else 84 return ibuf_write(imsgbuf->fd, imsgbuf->w); 85 } 86 87 int 88 imsgbuf_flush(struct imsgbuf *imsgbuf) 89 { 90 while (imsgbuf_queuelen(imsgbuf) > 0) { 91 if (imsgbuf_write(imsgbuf) == -1) 92 return (-1); 93 } 94 return (0); 95 } 96 97 void 98 imsgbuf_clear(struct imsgbuf *imsgbuf) 99 { 100 msgbuf_free(imsgbuf->w); 101 imsgbuf->w = NULL; 102 } 103 104 uint32_t 105 imsgbuf_queuelen(struct imsgbuf *imsgbuf) 106 { 107 return msgbuf_queuelen(imsgbuf->w); 108 } 109 110 ssize_t 111 imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg) 112 { 113 struct imsg m; 114 struct ibuf *buf; 115 116 if ((buf = msgbuf_get(imsgbuf->w)) == NULL) 117 return (0); 118 119 if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1) 120 return (-1); 121 122 if (ibuf_size(buf)) 123 m.data = ibuf_data(buf); 124 else 125 m.data = NULL; 126 m.buf = buf; 127 m.hdr.len &= ~IMSG_FD_MARK; 128 129 *imsg = m; 130 return (ibuf_size(buf) + IMSG_HEADER_SIZE); 131 } 132 133 int 134 imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf) 135 { 136 if (ibuf_size(imsg->buf) == 0) { 137 errno = EBADMSG; 138 return (-1); 139 } 140 return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf); 141 } 142 143 int 144 imsg_get_data(struct imsg *imsg, void *data, size_t len) 145 { 146 if (len == 0) { 147 errno = EINVAL; 148 return (-1); 149 } 150 if (ibuf_size(imsg->buf) != len) { 151 errno = EBADMSG; 152 return (-1); 153 } 154 return ibuf_get(imsg->buf, data, len); 155 } 156 157 int 158 imsg_get_fd(struct imsg *imsg) 159 { 160 return ibuf_fd_get(imsg->buf); 161 } 162 163 uint32_t 164 imsg_get_id(struct imsg *imsg) 165 { 166 return (imsg->hdr.peerid); 167 } 168 169 size_t 170 imsg_get_len(struct imsg *imsg) 171 { 172 return ibuf_size(imsg->buf); 173 } 174 175 pid_t 176 imsg_get_pid(struct imsg *imsg) 177 { 178 return (imsg->hdr.pid); 179 } 180 181 uint32_t 182 imsg_get_type(struct imsg *imsg) 183 { 184 return (imsg->hdr.type); 185 } 186 187 int 188 imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, 189 int fd, const void *data, size_t datalen) 190 { 191 struct ibuf *wbuf; 192 193 if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL) 194 return (-1); 195 196 if (imsg_add(wbuf, data, datalen) == -1) 197 return (-1); 198 199 ibuf_fd_set(wbuf, fd); 200 imsg_close(imsgbuf, wbuf); 201 202 return (1); 203 } 204 205 int 206 imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, 207 int fd, const struct iovec *iov, int iovcnt) 208 { 209 struct ibuf *wbuf; 210 int i; 211 size_t datalen = 0; 212 213 for (i = 0; i < iovcnt; i++) 214 datalen += iov[i].iov_len; 215 216 if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL) 217 return (-1); 218 219 for (i = 0; i < iovcnt; i++) 220 if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) 221 return (-1); 222 223 ibuf_fd_set(wbuf, fd); 224 imsg_close(imsgbuf, wbuf); 225 226 return (1); 227 } 228 229 /* 230 * Enqueue imsg with payload from ibuf buf. fd passing is not possible 231 * with this function. 232 */ 233 int 234 imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, 235 pid_t pid, struct ibuf *buf) 236 { 237 struct ibuf *hdrbuf = NULL; 238 struct imsg_hdr hdr; 239 int save_errno; 240 241 if (ibuf_size(buf) + IMSG_HEADER_SIZE > imsgbuf->maxsize) { 242 errno = ERANGE; 243 goto fail; 244 } 245 246 hdr.type = type; 247 hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE; 248 hdr.peerid = id; 249 if ((hdr.pid = pid) == 0) 250 hdr.pid = imsgbuf->pid; 251 252 if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL) 253 goto fail; 254 if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1) 255 goto fail; 256 257 ibuf_close(imsgbuf->w, hdrbuf); 258 ibuf_close(imsgbuf->w, buf); 259 return (1); 260 261 fail: 262 save_errno = errno; 263 ibuf_free(buf); 264 ibuf_free(hdrbuf); 265 errno = save_errno; 266 return (-1); 267 } 268 269 /* 270 * Forward imsg to another channel. Any attached fd is closed. 271 */ 272 int 273 imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg) 274 { 275 struct ibuf *wbuf; 276 size_t len; 277 278 ibuf_rewind(msg->buf); 279 ibuf_skip(msg->buf, sizeof(msg->hdr)); 280 len = ibuf_size(msg->buf); 281 282 if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid, 283 msg->hdr.pid, len)) == NULL) 284 return (-1); 285 286 if (len != 0) { 287 if (ibuf_add_ibuf(wbuf, msg->buf) == -1) { 288 ibuf_free(wbuf); 289 return (-1); 290 } 291 } 292 293 imsg_close(imsgbuf, wbuf); 294 return (1); 295 } 296 297 struct ibuf * 298 imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, 299 size_t datalen) 300 { 301 struct ibuf *wbuf; 302 struct imsg_hdr hdr; 303 304 datalen += IMSG_HEADER_SIZE; 305 if (datalen > imsgbuf->maxsize) { 306 errno = ERANGE; 307 return (NULL); 308 } 309 310 hdr.type = type; 311 hdr.peerid = id; 312 if ((hdr.pid = pid) == 0) 313 hdr.pid = imsgbuf->pid; 314 if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL) { 315 return (NULL); 316 } 317 if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) 318 return (NULL); 319 320 return (wbuf); 321 } 322 323 int 324 imsg_add(struct ibuf *msg, const void *data, size_t datalen) 325 { 326 if (datalen) 327 if (ibuf_add(msg, data, datalen) == -1) { 328 ibuf_free(msg); 329 return (-1); 330 } 331 return (datalen); 332 } 333 334 void 335 imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg) 336 { 337 uint32_t len; 338 339 len = ibuf_size(msg); 340 if (ibuf_fd_avail(msg)) 341 len |= IMSG_FD_MARK; 342 (void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len); 343 ibuf_close(imsgbuf->w, msg); 344 } 345 346 void 347 imsg_free(struct imsg *imsg) 348 { 349 ibuf_free(imsg->buf); 350 } 351 352 static struct ibuf * 353 imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd) 354 { 355 struct imsgbuf *imsgbuf = arg; 356 struct imsg_hdr hdr; 357 struct ibuf *b; 358 uint32_t len; 359 360 if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1) 361 return (NULL); 362 363 len = hdr.len & ~IMSG_FD_MARK; 364 365 if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) { 366 errno = ERANGE; 367 return (NULL); 368 } 369 if ((b = ibuf_open(len)) == NULL) 370 return (NULL); 371 if (hdr.len & IMSG_FD_MARK) { 372 ibuf_fd_set(b, *fd); 373 *fd = -1; 374 } 375 376 return b; 377 } 378