1 /* $OpenBSD: imsg-buffer.c,v 1.16 2023/06/19 17:19:50 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/socket.h> 22 #include <sys/uio.h> 23 24 #include <limits.h> 25 #include <errno.h> 26 #include <endian.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "imsg.h" 32 33 static int ibuf_realloc(struct ibuf *, size_t); 34 static void ibuf_enqueue(struct msgbuf *, struct ibuf *); 35 static void ibuf_dequeue(struct msgbuf *, struct ibuf *); 36 static void msgbuf_drain(struct msgbuf *, size_t); 37 38 struct ibuf * 39 ibuf_open(size_t len) 40 { 41 struct ibuf *buf; 42 43 if (len == 0) { 44 errno = EINVAL; 45 return (NULL); 46 } 47 if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) 48 return (NULL); 49 if ((buf->buf = calloc(len, 1)) == NULL) { 50 free(buf); 51 return (NULL); 52 } 53 buf->size = buf->max = len; 54 buf->fd = -1; 55 56 return (buf); 57 } 58 59 struct ibuf * 60 ibuf_dynamic(size_t len, size_t max) 61 { 62 struct ibuf *buf; 63 64 if (max < len) { 65 errno = EINVAL; 66 return (NULL); 67 } 68 69 if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) 70 return (NULL); 71 if (len > 0) { 72 if ((buf->buf = calloc(len, 1)) == NULL) { 73 free(buf); 74 return (NULL); 75 } 76 } 77 buf->size = len; 78 buf->max = max; 79 buf->fd = -1; 80 81 return (buf); 82 } 83 84 static int 85 ibuf_realloc(struct ibuf *buf, size_t len) 86 { 87 unsigned char *b; 88 89 /* on static buffers max is eq size and so the following fails */ 90 if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) { 91 errno = ERANGE; 92 return (-1); 93 } 94 95 b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1); 96 if (b == NULL) 97 return (-1); 98 buf->buf = b; 99 buf->size = buf->wpos + len; 100 101 return (0); 102 } 103 104 void * 105 ibuf_reserve(struct ibuf *buf, size_t len) 106 { 107 void *b; 108 109 if (len > SIZE_MAX - buf->wpos) { 110 errno = ERANGE; 111 return (NULL); 112 } 113 114 if (buf->wpos + len > buf->size) 115 if (ibuf_realloc(buf, len) == -1) 116 return (NULL); 117 118 b = buf->buf + buf->wpos; 119 buf->wpos += len; 120 memset(b, 0, len); 121 return (b); 122 } 123 124 int 125 ibuf_add(struct ibuf *buf, const void *data, size_t len) 126 { 127 void *b; 128 129 if ((b = ibuf_reserve(buf, len)) == NULL) 130 return (-1); 131 132 memcpy(b, data, len); 133 return (0); 134 } 135 136 int 137 ibuf_add_buf(struct ibuf *buf, const struct ibuf *from) 138 { 139 return ibuf_add(buf, from->buf, from->wpos); 140 } 141 142 int 143 ibuf_add_n8(struct ibuf *buf, uint64_t value) 144 { 145 uint8_t v; 146 147 if (value > UINT8_MAX) { 148 errno = EINVAL; 149 return (-1); 150 } 151 v = value; 152 return ibuf_add(buf, &v, sizeof(v)); 153 } 154 155 int 156 ibuf_add_n16(struct ibuf *buf, uint64_t value) 157 { 158 uint16_t v; 159 160 if (value > UINT16_MAX) { 161 errno = EINVAL; 162 return (-1); 163 } 164 v = htobe16(value); 165 return ibuf_add(buf, &v, sizeof(v)); 166 } 167 168 int 169 ibuf_add_n32(struct ibuf *buf, uint64_t value) 170 { 171 uint32_t v; 172 173 if (value > UINT32_MAX) { 174 errno = EINVAL; 175 return (-1); 176 } 177 v = htobe32(value); 178 return ibuf_add(buf, &v, sizeof(v)); 179 } 180 181 int 182 ibuf_add_n64(struct ibuf *buf, uint64_t value) 183 { 184 value = htobe64(value); 185 return ibuf_add(buf, &value, sizeof(value)); 186 } 187 188 int 189 ibuf_add_zero(struct ibuf *buf, size_t len) 190 { 191 void *b; 192 193 if ((b = ibuf_reserve(buf, len)) == NULL) 194 return (-1); 195 return (0); 196 } 197 198 void * 199 ibuf_seek(struct ibuf *buf, size_t pos, size_t len) 200 { 201 /* only allowed to seek in already written parts */ 202 if (len > SIZE_MAX - pos || pos + len > buf->wpos) { 203 errno = ERANGE; 204 return (NULL); 205 } 206 207 return (buf->buf + pos); 208 } 209 210 int 211 ibuf_set(struct ibuf *buf, size_t pos, const void *data, size_t len) 212 { 213 void *b; 214 215 if ((b = ibuf_seek(buf, pos, len)) == NULL) 216 return (-1); 217 218 memcpy(b, data, len); 219 return (0); 220 } 221 222 int 223 ibuf_set_n8(struct ibuf *buf, size_t pos, uint64_t value) 224 { 225 uint8_t v; 226 227 if (value > UINT8_MAX) { 228 errno = EINVAL; 229 return (-1); 230 } 231 v = value; 232 return (ibuf_set(buf, pos, &v, sizeof(v))); 233 } 234 235 int 236 ibuf_set_n16(struct ibuf *buf, size_t pos, uint64_t value) 237 { 238 uint16_t v; 239 240 if (value > UINT16_MAX) { 241 errno = EINVAL; 242 return (-1); 243 } 244 v = htobe16(value); 245 return (ibuf_set(buf, pos, &v, sizeof(v))); 246 } 247 248 int 249 ibuf_set_n32(struct ibuf *buf, size_t pos, uint64_t value) 250 { 251 uint32_t v; 252 253 if (value > UINT32_MAX) { 254 errno = EINVAL; 255 return (-1); 256 } 257 v = htobe32(value); 258 return (ibuf_set(buf, pos, &v, sizeof(v))); 259 } 260 261 int 262 ibuf_set_n64(struct ibuf *buf, size_t pos, uint64_t value) 263 { 264 value = htobe64(value); 265 return (ibuf_set(buf, pos, &value, sizeof(value))); 266 } 267 268 void * 269 ibuf_data(struct ibuf *buf) 270 { 271 return (buf->buf); 272 } 273 274 size_t 275 ibuf_size(struct ibuf *buf) 276 { 277 return (buf->wpos); 278 } 279 280 size_t 281 ibuf_left(struct ibuf *buf) 282 { 283 return (buf->max - buf->wpos); 284 } 285 286 void 287 ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) 288 { 289 ibuf_enqueue(msgbuf, buf); 290 } 291 292 void 293 ibuf_free(struct ibuf *buf) 294 { 295 if (buf == NULL) 296 return; 297 #ifdef NOTYET 298 if (buf->fd != -1) 299 close(buf->fd); 300 #endif 301 freezero(buf->buf, buf->size); 302 free(buf); 303 } 304 305 int 306 ibuf_fd_avail(struct ibuf *buf) 307 { 308 return (buf->fd != -1); 309 } 310 311 int 312 ibuf_fd_get(struct ibuf *buf) 313 { 314 int fd; 315 316 fd = buf->fd; 317 #ifdef NOTYET 318 buf->fd = -1; 319 #endif 320 return (fd); 321 } 322 323 void 324 ibuf_fd_set(struct ibuf *buf, int fd) 325 { 326 if (buf->fd != -1) 327 close(buf->fd); 328 buf->fd = fd; 329 } 330 331 int 332 ibuf_write(struct msgbuf *msgbuf) 333 { 334 struct iovec iov[IOV_MAX]; 335 struct ibuf *buf; 336 unsigned int i = 0; 337 ssize_t n; 338 339 memset(&iov, 0, sizeof(iov)); 340 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { 341 if (i >= IOV_MAX) 342 break; 343 iov[i].iov_base = buf->buf + buf->rpos; 344 iov[i].iov_len = buf->wpos - buf->rpos; 345 i++; 346 } 347 348 again: 349 if ((n = writev(msgbuf->fd, iov, i)) == -1) { 350 if (errno == EINTR) 351 goto again; 352 if (errno == ENOBUFS) 353 errno = EAGAIN; 354 return (-1); 355 } 356 357 if (n == 0) { /* connection closed */ 358 errno = 0; 359 return (0); 360 } 361 362 msgbuf_drain(msgbuf, n); 363 364 return (1); 365 } 366 367 void 368 msgbuf_init(struct msgbuf *msgbuf) 369 { 370 msgbuf->queued = 0; 371 msgbuf->fd = -1; 372 TAILQ_INIT(&msgbuf->bufs); 373 } 374 375 static void 376 msgbuf_drain(struct msgbuf *msgbuf, size_t n) 377 { 378 struct ibuf *buf, *next; 379 380 for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; 381 buf = next) { 382 next = TAILQ_NEXT(buf, entry); 383 if (n >= buf->wpos - buf->rpos) { 384 n -= buf->wpos - buf->rpos; 385 ibuf_dequeue(msgbuf, buf); 386 } else { 387 buf->rpos += n; 388 n = 0; 389 } 390 } 391 } 392 393 void 394 msgbuf_clear(struct msgbuf *msgbuf) 395 { 396 struct ibuf *buf; 397 398 while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) 399 ibuf_dequeue(msgbuf, buf); 400 } 401 402 int 403 msgbuf_write(struct msgbuf *msgbuf) 404 { 405 struct iovec iov[IOV_MAX]; 406 struct ibuf *buf, *buf0 = NULL; 407 unsigned int i = 0; 408 ssize_t n; 409 struct msghdr msg; 410 struct cmsghdr *cmsg; 411 union { 412 struct cmsghdr hdr; 413 char buf[CMSG_SPACE(sizeof(int))]; 414 } cmsgbuf; 415 416 memset(&iov, 0, sizeof(iov)); 417 memset(&msg, 0, sizeof(msg)); 418 memset(&cmsgbuf, 0, sizeof(cmsgbuf)); 419 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { 420 if (i >= IOV_MAX) 421 break; 422 if (i > 0 && buf->fd != -1) 423 break; 424 iov[i].iov_base = buf->buf + buf->rpos; 425 iov[i].iov_len = buf->wpos - buf->rpos; 426 i++; 427 if (buf->fd != -1) 428 buf0 = buf; 429 } 430 431 msg.msg_iov = iov; 432 msg.msg_iovlen = i; 433 434 if (buf0 != NULL) { 435 msg.msg_control = (caddr_t)&cmsgbuf.buf; 436 msg.msg_controllen = sizeof(cmsgbuf.buf); 437 cmsg = CMSG_FIRSTHDR(&msg); 438 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 439 cmsg->cmsg_level = SOL_SOCKET; 440 cmsg->cmsg_type = SCM_RIGHTS; 441 *(int *)CMSG_DATA(cmsg) = buf0->fd; 442 } 443 444 again: 445 if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { 446 if (errno == EINTR) 447 goto again; 448 if (errno == ENOBUFS) 449 errno = EAGAIN; 450 return (-1); 451 } 452 453 if (n == 0) { /* connection closed */ 454 errno = 0; 455 return (0); 456 } 457 458 /* 459 * assumption: fd got sent if sendmsg sent anything 460 * this works because fds are passed one at a time 461 */ 462 if (buf0 != NULL) { 463 close(buf0->fd); 464 buf0->fd = -1; 465 } 466 467 msgbuf_drain(msgbuf, n); 468 469 return (1); 470 } 471 472 static void 473 ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) 474 { 475 TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); 476 msgbuf->queued++; 477 } 478 479 static void 480 ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) 481 { 482 TAILQ_REMOVE(&msgbuf->bufs, buf, entry); 483 484 if (buf->fd != -1) { 485 close(buf->fd); 486 buf->fd = -1; 487 } 488 489 msgbuf->queued--; 490 ibuf_free(buf); 491 } 492