1/* $OpenBSD: imsg-buffer.c,v 1.18 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 <limits.h> 26#include <errno.h> 27#include <endian.h> 28#include <stdint.h> 29#include <stdlib.h> 30#include <string.h> 31#include <unistd.h> 32 33#include "imsg.h" 34 35static int ibuf_realloc(struct ibuf *, size_t); 36static void ibuf_enqueue(struct msgbuf *, struct ibuf *); 37static void ibuf_dequeue(struct msgbuf *, struct ibuf *); 38static void msgbuf_drain(struct msgbuf *, size_t); 39 40struct ibuf * 41ibuf_open(size_t len) 42{ 43 struct ibuf *buf; 44 45 if (len == 0) { 46 errno = EINVAL; 47 return (NULL); 48 } 49 if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) 50 return (NULL); 51 if ((buf->buf = calloc(len, 1)) == NULL) { 52 free(buf); 53 return (NULL); 54 } 55 buf->size = buf->max = len; 56 buf->fd = -1; 57 58 return (buf); 59} 60 61struct ibuf * 62ibuf_dynamic(size_t len, size_t max) 63{ 64 struct ibuf *buf; 65 66 if (max == 0 || max < len) { 67 errno = EINVAL; 68 return (NULL); 69 } 70 71 if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) 72 return (NULL); 73 if (len > 0) { 74 if ((buf->buf = calloc(len, 1)) == NULL) { 75 free(buf); 76 return (NULL); 77 } 78 } 79 buf->size = len; 80 buf->max = max; 81 buf->fd = -1; 82 83 return (buf); 84} 85 86static int 87ibuf_realloc(struct ibuf *buf, size_t len) 88{ 89 unsigned char *b; 90 91 /* on static buffers max is eq size and so the following fails */ 92 if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) { 93 errno = ERANGE; 94 return (-1); 95 } 96 97 b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1); 98 if (b == NULL) 99 return (-1); 100 buf->buf = b; 101 buf->size = buf->wpos + len; 102 103 return (0); 104} 105 106void * 107ibuf_reserve(struct ibuf *buf, size_t len) 108{ 109 void *b; 110 111 if (len > SIZE_MAX - buf->wpos || buf->max == 0) { 112 errno = ERANGE; 113 return (NULL); 114 } 115 116 if (buf->wpos + len > buf->size) 117 if (ibuf_realloc(buf, len) == -1) 118 return (NULL); 119 120 b = buf->buf + buf->wpos; 121 buf->wpos += len; 122 return (b); 123} 124 125int 126ibuf_add(struct ibuf *buf, const void *data, size_t len) 127{ 128 void *b; 129 130 if ((b = ibuf_reserve(buf, len)) == NULL) 131 return (-1); 132 133 memcpy(b, data, len); 134 return (0); 135} 136 137int 138ibuf_add_ibuf(struct ibuf *buf, const struct ibuf *from) 139{ 140 return ibuf_add(buf, ibuf_data(from), ibuf_size(from)); 141} 142 143/* remove after tree is converted */ 144int 145ibuf_add_buf(struct ibuf *buf, const struct ibuf *from) 146{ 147 return ibuf_add_ibuf(buf, from); 148} 149 150int 151ibuf_add_n8(struct ibuf *buf, uint64_t value) 152{ 153 uint8_t v; 154 155 if (value > UINT8_MAX) { 156 errno = EINVAL; 157 return (-1); 158 } 159 v = value; 160 return ibuf_add(buf, &v, sizeof(v)); 161} 162 163int 164ibuf_add_n16(struct ibuf *buf, uint64_t value) 165{ 166 uint16_t v; 167 168 if (value > UINT16_MAX) { 169 errno = EINVAL; 170 return (-1); 171 } 172 v = htobe16(value); 173 return ibuf_add(buf, &v, sizeof(v)); 174} 175 176int 177ibuf_add_n32(struct ibuf *buf, uint64_t value) 178{ 179 uint32_t v; 180 181 if (value > UINT32_MAX) { 182 errno = EINVAL; 183 return (-1); 184 } 185 v = htobe32(value); 186 return ibuf_add(buf, &v, sizeof(v)); 187} 188 189int 190ibuf_add_n64(struct ibuf *buf, uint64_t value) 191{ 192 value = htobe64(value); 193 return ibuf_add(buf, &value, sizeof(value)); 194} 195 196int 197ibuf_add_h16(struct ibuf *buf, uint64_t value) 198{ 199 uint16_t v; 200 201 if (value > UINT16_MAX) { 202 errno = EINVAL; 203 return (-1); 204 } 205 v = value; 206 return ibuf_add(buf, &v, sizeof(v)); 207} 208 209int 210ibuf_add_h32(struct ibuf *buf, uint64_t value) 211{ 212 uint32_t v; 213 214 if (value > UINT32_MAX) { 215 errno = EINVAL; 216 return (-1); 217 } 218 v = value; 219 return ibuf_add(buf, &v, sizeof(v)); 220} 221 222int 223ibuf_add_h64(struct ibuf *buf, uint64_t value) 224{ 225 return ibuf_add(buf, &value, sizeof(value)); 226} 227 228int 229ibuf_add_zero(struct ibuf *buf, size_t len) 230{ 231 void *b; 232 233 if ((b = ibuf_reserve(buf, len)) == NULL) 234 return (-1); 235 memset(b, 0, len); 236 return (0); 237} 238 239void * 240ibuf_seek(struct ibuf *buf, size_t pos, size_t len) 241{ 242 /* only allow seeking between rpos and wpos */ 243 if (ibuf_size(buf) < pos || SIZE_MAX - pos < len || 244 ibuf_size(buf) < pos + len) { 245 errno = ERANGE; 246 return (NULL); 247 } 248 249 return (buf->buf + buf->rpos + pos); 250} 251 252int 253ibuf_set(struct ibuf *buf, size_t pos, const void *data, size_t len) 254{ 255 void *b; 256 257 if ((b = ibuf_seek(buf, pos, len)) == NULL) 258 return (-1); 259 260 memcpy(b, data, len); 261 return (0); 262} 263 264int 265ibuf_set_n8(struct ibuf *buf, size_t pos, uint64_t value) 266{ 267 uint8_t v; 268 269 if (value > UINT8_MAX) { 270 errno = EINVAL; 271 return (-1); 272 } 273 v = value; 274 return (ibuf_set(buf, pos, &v, sizeof(v))); 275} 276 277int 278ibuf_set_n16(struct ibuf *buf, size_t pos, uint64_t value) 279{ 280 uint16_t v; 281 282 if (value > UINT16_MAX) { 283 errno = EINVAL; 284 return (-1); 285 } 286 v = htobe16(value); 287 return (ibuf_set(buf, pos, &v, sizeof(v))); 288} 289 290int 291ibuf_set_n32(struct ibuf *buf, size_t pos, uint64_t value) 292{ 293 uint32_t v; 294 295 if (value > UINT32_MAX) { 296 errno = EINVAL; 297 return (-1); 298 } 299 v = htobe32(value); 300 return (ibuf_set(buf, pos, &v, sizeof(v))); 301} 302 303int 304ibuf_set_n64(struct ibuf *buf, size_t pos, uint64_t value) 305{ 306 value = htobe64(value); 307 return (ibuf_set(buf, pos, &value, sizeof(value))); 308} 309 310int 311ibuf_set_h16(struct ibuf *buf, size_t pos, uint64_t value) 312{ 313 uint16_t v; 314 315 if (value > UINT16_MAX) { 316 errno = EINVAL; 317 return (-1); 318 } 319 v = value; 320 return (ibuf_set(buf, pos, &v, sizeof(v))); 321} 322 323int 324ibuf_set_h32(struct ibuf *buf, size_t pos, uint64_t value) 325{ 326 uint32_t v; 327 328 if (value > UINT32_MAX) { 329 errno = EINVAL; 330 return (-1); 331 } 332 v = value; 333 return (ibuf_set(buf, pos, &v, sizeof(v))); 334} 335 336int 337ibuf_set_h64(struct ibuf *buf, size_t pos, uint64_t value) 338{ 339 return (ibuf_set(buf, pos, &value, sizeof(value))); 340} 341 342void * 343ibuf_data(const struct ibuf *buf) 344{ 345 return (buf->buf + buf->rpos); 346} 347 348size_t 349ibuf_size(const struct ibuf *buf) 350{ 351 return (buf->wpos - buf->rpos); 352} 353 354size_t 355ibuf_left(const struct ibuf *buf) 356{ 357 if (buf->max == 0) 358 return (0); 359 return (buf->max - buf->wpos); 360} 361 362int 363ibuf_truncate(struct ibuf *buf, size_t len) 364{ 365 if (ibuf_size(buf) >= len) { 366 buf->wpos = buf->rpos + len; 367 return (0); 368 } 369 if (buf->max == 0) { 370 /* only allow to truncate down */ 371 errno = ERANGE; 372 return (-1); 373 } 374 return ibuf_add_zero(buf, len - ibuf_size(buf)); 375} 376 377void 378ibuf_rewind(struct ibuf *buf) 379{ 380 buf->rpos = 0; 381} 382 383void 384ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) 385{ 386 ibuf_enqueue(msgbuf, buf); 387} 388 389void 390ibuf_from_buffer(struct ibuf *buf, void *data, size_t len) 391{ 392 memset(buf, 0, sizeof(*buf)); 393 buf->buf = data; 394 buf->size = buf->wpos = len; 395 buf->fd = -1; 396} 397 398void 399ibuf_from_ibuf(struct ibuf *buf, const struct ibuf *from) 400{ 401 ibuf_from_buffer(buf, ibuf_data(from), ibuf_size(from)); 402} 403 404int 405ibuf_get(struct ibuf *buf, void *data, size_t len) 406{ 407 if (ibuf_size(buf) < len) { 408 errno = EBADMSG; 409 return (-1); 410 } 411 412 memcpy(data, ibuf_data(buf), len); 413 buf->rpos += len; 414 return (0); 415} 416 417int 418ibuf_get_ibuf(struct ibuf *buf, size_t len, struct ibuf *new) 419{ 420 if (ibuf_size(buf) < len) { 421 errno = EBADMSG; 422 return (-1); 423 } 424 425 ibuf_from_buffer(new, ibuf_data(buf), len); 426 buf->rpos += len; 427 return (0); 428} 429 430int 431ibuf_get_n8(struct ibuf *buf, uint8_t *value) 432{ 433 return ibuf_get(buf, value, sizeof(*value)); 434} 435 436int 437ibuf_get_n16(struct ibuf *buf, uint16_t *value) 438{ 439 int rv; 440 441 rv = ibuf_get(buf, value, sizeof(*value)); 442 *value = be16toh(*value); 443 return (rv); 444} 445 446int 447ibuf_get_n32(struct ibuf *buf, uint32_t *value) 448{ 449 int rv; 450 451 rv = ibuf_get(buf, value, sizeof(*value)); 452 *value = be32toh(*value); 453 return (rv); 454} 455 456int 457ibuf_get_n64(struct ibuf *buf, uint64_t *value) 458{ 459 int rv; 460 461 rv = ibuf_get(buf, value, sizeof(*value)); 462 *value = be64toh(*value); 463 return (rv); 464} 465 466int 467ibuf_get_h16(struct ibuf *buf, uint16_t *value) 468{ 469 return ibuf_get(buf, value, sizeof(*value)); 470} 471 472int 473ibuf_get_h32(struct ibuf *buf, uint32_t *value) 474{ 475 return ibuf_get(buf, value, sizeof(*value)); 476} 477 478int 479ibuf_get_h64(struct ibuf *buf, uint64_t *value) 480{ 481 return ibuf_get(buf, value, sizeof(*value)); 482} 483 484int 485ibuf_skip(struct ibuf *buf, size_t len) 486{ 487 if (ibuf_size(buf) < len) { 488 errno = EBADMSG; 489 return (-1); 490 } 491 492 buf->rpos += len; 493 return (0); 494} 495 496void 497ibuf_free(struct ibuf *buf) 498{ 499 if (buf == NULL) 500 return; 501 if (buf->max == 0) /* if buf lives on the stack */ 502 abort(); /* abort before causing more harm */ 503 if (buf->fd != -1) 504 close(buf->fd); 505 freezero(buf->buf, buf->size); 506 free(buf); 507} 508 509int 510ibuf_fd_avail(struct ibuf *buf) 511{ 512 return (buf->fd != -1); 513} 514 515int 516ibuf_fd_get(struct ibuf *buf) 517{ 518 int fd; 519 520 fd = buf->fd; 521 buf->fd = -1; 522 return (fd); 523} 524 525void 526ibuf_fd_set(struct ibuf *buf, int fd) 527{ 528 if (buf->max == 0) /* if buf lives on the stack */ 529 abort(); /* abort before causing more harm */ 530 if (buf->fd != -1) 531 close(buf->fd); 532 buf->fd = fd; 533} 534 535int 536ibuf_write(struct msgbuf *msgbuf) 537{ 538 struct iovec iov[IOV_MAX]; 539 struct ibuf *buf; 540 unsigned int i = 0; 541 ssize_t n; 542 543 memset(&iov, 0, sizeof(iov)); 544 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { 545 if (i >= IOV_MAX) 546 break; 547 iov[i].iov_base = ibuf_data(buf); 548 iov[i].iov_len = ibuf_size(buf); 549 i++; 550 } 551 552again: 553 if ((n = writev(msgbuf->fd, iov, i)) == -1) { 554 if (errno == EINTR) 555 goto again; 556 if (errno == ENOBUFS) 557 errno = EAGAIN; 558 return (-1); 559 } 560 561 if (n == 0) { /* connection closed */ 562 errno = 0; 563 return (0); 564 } 565 566 msgbuf_drain(msgbuf, n); 567 568 return (1); 569} 570 571void 572msgbuf_init(struct msgbuf *msgbuf) 573{ 574 msgbuf->queued = 0; 575 msgbuf->fd = -1; 576 TAILQ_INIT(&msgbuf->bufs); 577} 578 579static void 580msgbuf_drain(struct msgbuf *msgbuf, size_t n) 581{ 582 struct ibuf *buf, *next; 583 584 for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; 585 buf = next) { 586 next = TAILQ_NEXT(buf, entry); 587 if (n >= ibuf_size(buf)) { 588 n -= ibuf_size(buf); 589 ibuf_dequeue(msgbuf, buf); 590 } else { 591 buf->rpos += n; 592 n = 0; 593 } 594 } 595} 596 597void 598msgbuf_clear(struct msgbuf *msgbuf) 599{ 600 struct ibuf *buf; 601 602 while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) 603 ibuf_dequeue(msgbuf, buf); 604} 605 606int 607msgbuf_write(struct msgbuf *msgbuf) 608{ 609 struct iovec iov[IOV_MAX]; 610 struct ibuf *buf, *buf0 = NULL; 611 unsigned int i = 0; 612 ssize_t n; 613 struct msghdr msg; 614 struct cmsghdr *cmsg; 615 union { 616 struct cmsghdr hdr; 617 char buf[CMSG_SPACE(sizeof(int))]; 618 } cmsgbuf; 619 620 memset(&iov, 0, sizeof(iov)); 621 memset(&msg, 0, sizeof(msg)); 622 memset(&cmsgbuf, 0, sizeof(cmsgbuf)); 623 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { 624 if (i >= IOV_MAX) 625 break; 626 if (i > 0 && buf->fd != -1) 627 break; 628 iov[i].iov_base = ibuf_data(buf); 629 iov[i].iov_len = ibuf_size(buf); 630 i++; 631 if (buf->fd != -1) 632 buf0 = buf; 633 } 634 635 msg.msg_iov = iov; 636 msg.msg_iovlen = i; 637 638 if (buf0 != NULL) { 639 msg.msg_control = (caddr_t)&cmsgbuf.buf; 640 msg.msg_controllen = sizeof(cmsgbuf.buf); 641 cmsg = CMSG_FIRSTHDR(&msg); 642 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 643 cmsg->cmsg_level = SOL_SOCKET; 644 cmsg->cmsg_type = SCM_RIGHTS; 645 *(int *)CMSG_DATA(cmsg) = buf0->fd; 646 } 647 648again: 649 if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { 650 if (errno == EINTR) 651 goto again; 652 if (errno == ENOBUFS) 653 errno = EAGAIN; 654 return (-1); 655 } 656 657 if (n == 0) { /* connection closed */ 658 errno = 0; 659 return (0); 660 } 661 662 /* 663 * assumption: fd got sent if sendmsg sent anything 664 * this works because fds are passed one at a time 665 */ 666 if (buf0 != NULL) { 667 close(buf0->fd); 668 buf0->fd = -1; 669 } 670 671 msgbuf_drain(msgbuf, n); 672 673 return (1); 674} 675 676uint32_t 677msgbuf_queuelen(struct msgbuf *msgbuf) 678{ 679 return (msgbuf->queued); 680} 681 682static void 683ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) 684{ 685 if (buf->max == 0) /* if buf lives on the stack */ 686 abort(); /* abort before causing more harm */ 687 TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); 688 msgbuf->queued++; 689} 690 691static void 692ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) 693{ 694 TAILQ_REMOVE(&msgbuf->bufs, buf, entry); 695 msgbuf->queued--; 696 ibuf_free(buf); 697} 698