1 /* $OpenBSD: imsg.c,v 1.23 2023/12/12 15:47:41 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 <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 #include "imsg.h" 31 32 struct imsg_fd { 33 TAILQ_ENTRY(imsg_fd) entry; 34 int fd; 35 }; 36 37 int imsg_fd_overhead = 0; 38 39 static int imsg_dequeue_fd(struct imsgbuf *); 40 41 void 42 imsg_init(struct imsgbuf *imsgbuf, int fd) 43 { 44 msgbuf_init(&imsgbuf->w); 45 memset(&imsgbuf->r, 0, sizeof(imsgbuf->r)); 46 imsgbuf->fd = fd; 47 imsgbuf->w.fd = fd; 48 imsgbuf->pid = getpid(); 49 TAILQ_INIT(&imsgbuf->fds); 50 } 51 52 ssize_t 53 imsg_read(struct imsgbuf *imsgbuf) 54 { 55 struct msghdr msg; 56 struct cmsghdr *cmsg; 57 union { 58 struct cmsghdr hdr; 59 char buf[CMSG_SPACE(sizeof(int) * 1)]; 60 } cmsgbuf; 61 struct iovec iov; 62 ssize_t n = -1; 63 int fd; 64 struct imsg_fd *ifd; 65 66 memset(&msg, 0, sizeof(msg)); 67 memset(&cmsgbuf, 0, sizeof(cmsgbuf)); 68 69 iov.iov_base = imsgbuf->r.buf + imsgbuf->r.wpos; 70 iov.iov_len = sizeof(imsgbuf->r.buf) - imsgbuf->r.wpos; 71 msg.msg_iov = &iov; 72 msg.msg_iovlen = 1; 73 msg.msg_control = &cmsgbuf.buf; 74 msg.msg_controllen = sizeof(cmsgbuf.buf); 75 76 if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) 77 return (-1); 78 79 again: 80 if (getdtablecount() + imsg_fd_overhead + 81 (int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int)) 82 >= getdtablesize()) { 83 errno = EAGAIN; 84 free(ifd); 85 return (-1); 86 } 87 88 if ((n = recvmsg(imsgbuf->fd, &msg, 0)) == -1) { 89 if (errno == EINTR) 90 goto again; 91 goto fail; 92 } 93 94 imsgbuf->r.wpos += n; 95 96 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 97 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 98 if (cmsg->cmsg_level == SOL_SOCKET && 99 cmsg->cmsg_type == SCM_RIGHTS) { 100 int i; 101 int j; 102 103 /* 104 * We only accept one file descriptor. Due to C 105 * padding rules, our control buffer might contain 106 * more than one fd, and we must close them. 107 */ 108 j = ((char *)cmsg + cmsg->cmsg_len - 109 (char *)CMSG_DATA(cmsg)) / sizeof(int); 110 for (i = 0; i < j; i++) { 111 fd = ((int *)CMSG_DATA(cmsg))[i]; 112 if (ifd != NULL) { 113 ifd->fd = fd; 114 TAILQ_INSERT_TAIL(&imsgbuf->fds, ifd, 115 entry); 116 ifd = NULL; 117 } else 118 close(fd); 119 } 120 } 121 /* we do not handle other ctl data level */ 122 } 123 124 fail: 125 free(ifd); 126 return (n); 127 } 128 129 ssize_t 130 imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg) 131 { 132 struct imsg m; 133 size_t av, left, datalen; 134 135 av = imsgbuf->r.wpos; 136 137 if (IMSG_HEADER_SIZE > av) 138 return (0); 139 140 memcpy(&m.hdr, imsgbuf->r.buf, sizeof(m.hdr)); 141 if (m.hdr.len < IMSG_HEADER_SIZE || 142 m.hdr.len > MAX_IMSGSIZE) { 143 errno = ERANGE; 144 return (-1); 145 } 146 if (m.hdr.len > av) 147 return (0); 148 149 m.fd = -1; 150 m.buf = NULL; 151 m.data = NULL; 152 153 datalen = m.hdr.len - IMSG_HEADER_SIZE; 154 imsgbuf->r.rptr = imsgbuf->r.buf + IMSG_HEADER_SIZE; 155 if (datalen != 0) { 156 if ((m.buf = ibuf_open(datalen)) == NULL) 157 return (-1); 158 if (ibuf_add(m.buf, imsgbuf->r.rptr, datalen) == -1) { 159 /* this should never fail */ 160 ibuf_free(m.buf); 161 return (-1); 162 } 163 m.data = ibuf_data(m.buf); 164 } 165 166 if (m.hdr.flags & IMSGF_HASFD) 167 m.fd = imsg_dequeue_fd(imsgbuf); 168 169 if (m.hdr.len < av) { 170 left = av - m.hdr.len; 171 memmove(&imsgbuf->r.buf, imsgbuf->r.buf + m.hdr.len, left); 172 imsgbuf->r.wpos = left; 173 } else 174 imsgbuf->r.wpos = 0; 175 176 *imsg = m; 177 return (datalen + IMSG_HEADER_SIZE); 178 } 179 180 int 181 imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf) 182 { 183 if (imsg->buf == NULL) { 184 errno = EBADMSG; 185 return (-1); 186 } 187 return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf); 188 } 189 190 int 191 imsg_get_data(struct imsg *imsg, void *data, size_t len) 192 { 193 if (len == 0) { 194 errno = EINVAL; 195 return (-1); 196 } 197 if (imsg->buf == NULL || ibuf_size(imsg->buf) != len) { 198 errno = EBADMSG; 199 return (-1); 200 } 201 return ibuf_get(imsg->buf, data, len); 202 } 203 204 int 205 imsg_get_fd(struct imsg *imsg) 206 { 207 int fd = imsg->fd; 208 209 imsg->fd = -1; 210 return fd; 211 } 212 213 uint32_t 214 imsg_get_id(struct imsg *imsg) 215 { 216 return (imsg->hdr.peerid); 217 } 218 219 size_t 220 imsg_get_len(struct imsg *imsg) 221 { 222 if (imsg->buf == NULL) 223 return 0; 224 return ibuf_size(imsg->buf); 225 } 226 227 pid_t 228 imsg_get_pid(struct imsg *imsg) 229 { 230 return (imsg->hdr.pid); 231 } 232 233 uint32_t 234 imsg_get_type(struct imsg *imsg) 235 { 236 return (imsg->hdr.type); 237 } 238 239 int 240 imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, 241 int fd, const void *data, size_t datalen) 242 { 243 struct ibuf *wbuf; 244 245 if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL) 246 return (-1); 247 248 if (imsg_add(wbuf, data, datalen) == -1) 249 return (-1); 250 251 ibuf_fd_set(wbuf, fd); 252 imsg_close(imsgbuf, wbuf); 253 254 return (1); 255 } 256 257 int 258 imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, 259 int fd, const struct iovec *iov, int iovcnt) 260 { 261 struct ibuf *wbuf; 262 int i; 263 size_t datalen = 0; 264 265 for (i = 0; i < iovcnt; i++) 266 datalen += iov[i].iov_len; 267 268 if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL) 269 return (-1); 270 271 for (i = 0; i < iovcnt; i++) 272 if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) 273 return (-1); 274 275 ibuf_fd_set(wbuf, fd); 276 imsg_close(imsgbuf, wbuf); 277 278 return (1); 279 } 280 281 /* 282 * Enqueue imsg with payload from ibuf buf. fd passing is not possible 283 * with this function. 284 */ 285 int 286 imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, 287 pid_t pid, struct ibuf *buf) 288 { 289 struct ibuf *hdrbuf = NULL; 290 struct imsg_hdr hdr; 291 int save_errno; 292 293 if (ibuf_size(buf) + IMSG_HEADER_SIZE > MAX_IMSGSIZE) { 294 errno = ERANGE; 295 goto fail; 296 } 297 298 hdr.type = type; 299 hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE; 300 hdr.flags = 0; 301 hdr.peerid = id; 302 if ((hdr.pid = pid) == 0) 303 hdr.pid = imsgbuf->pid; 304 305 if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL) 306 goto fail; 307 if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1) 308 goto fail; 309 310 ibuf_close(&imsgbuf->w, hdrbuf); 311 ibuf_close(&imsgbuf->w, buf); 312 return (1); 313 314 fail: 315 save_errno = errno; 316 ibuf_free(buf); 317 ibuf_free(hdrbuf); 318 errno = save_errno; 319 return (-1); 320 } 321 322 /* 323 * Forward imsg to another channel. Any attached fd is closed. 324 */ 325 int 326 imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg) 327 { 328 struct ibuf *wbuf; 329 size_t len = 0; 330 331 if (msg->fd != -1) { 332 close(msg->fd); 333 msg->fd = -1; 334 } 335 336 if (msg->buf != NULL) { 337 ibuf_rewind(msg->buf); 338 len = ibuf_size(msg->buf); 339 } 340 341 if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid, 342 msg->hdr.pid, len)) == NULL) 343 return (-1); 344 345 if (msg->buf != NULL) { 346 if (ibuf_add_buf(wbuf, msg->buf) == -1) { 347 ibuf_free(wbuf); 348 return (-1); 349 } 350 } 351 352 imsg_close(imsgbuf, wbuf); 353 return (1); 354 } 355 356 struct ibuf * 357 imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, 358 size_t datalen) 359 { 360 struct ibuf *wbuf; 361 struct imsg_hdr hdr; 362 363 datalen += IMSG_HEADER_SIZE; 364 if (datalen > MAX_IMSGSIZE) { 365 errno = ERANGE; 366 return (NULL); 367 } 368 369 hdr.type = type; 370 hdr.flags = 0; 371 hdr.peerid = id; 372 if ((hdr.pid = pid) == 0) 373 hdr.pid = imsgbuf->pid; 374 if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { 375 return (NULL); 376 } 377 if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) 378 return (NULL); 379 380 return (wbuf); 381 } 382 383 int 384 imsg_add(struct ibuf *msg, const void *data, size_t datalen) 385 { 386 if (datalen) 387 if (ibuf_add(msg, data, datalen) == -1) { 388 ibuf_free(msg); 389 return (-1); 390 } 391 return (datalen); 392 } 393 394 void 395 imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg) 396 { 397 struct imsg_hdr *hdr; 398 399 hdr = (struct imsg_hdr *)msg->buf; 400 401 hdr->flags &= ~IMSGF_HASFD; 402 if (ibuf_fd_avail(msg)) 403 hdr->flags |= IMSGF_HASFD; 404 hdr->len = ibuf_size(msg); 405 406 ibuf_close(&imsgbuf->w, msg); 407 } 408 409 void 410 imsg_free(struct imsg *imsg) 411 { 412 ibuf_free(imsg->buf); 413 } 414 415 static int 416 imsg_dequeue_fd(struct imsgbuf *imsgbuf) 417 { 418 int fd; 419 struct imsg_fd *ifd; 420 421 if ((ifd = TAILQ_FIRST(&imsgbuf->fds)) == NULL) 422 return (-1); 423 424 fd = ifd->fd; 425 TAILQ_REMOVE(&imsgbuf->fds, ifd, entry); 426 free(ifd); 427 428 return (fd); 429 } 430 431 int 432 imsg_flush(struct imsgbuf *imsgbuf) 433 { 434 while (imsgbuf->w.queued) 435 if (msgbuf_write(&imsgbuf->w) <= 0) 436 return (-1); 437 return (0); 438 } 439 440 void 441 imsg_clear(struct imsgbuf *imsgbuf) 442 { 443 int fd; 444 445 msgbuf_clear(&imsgbuf->w); 446 while ((fd = imsg_dequeue_fd(imsgbuf)) != -1) 447 close(fd); 448 } 449