1 /* $OpenBSD: imsg-buffer.c,v 1.17 2023/10/24 14:05:23 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 if (buf->fd != -1) 298 close(buf->fd); 299 freezero(buf->buf, buf->size); 300 free(buf); 301 } 302 303 int 304 ibuf_fd_avail(struct ibuf *buf) 305 { 306 return (buf->fd != -1); 307 } 308 309 int 310 ibuf_fd_get(struct ibuf *buf) 311 { 312 int fd; 313 314 fd = buf->fd; 315 buf->fd = -1; 316 return (fd); 317 } 318 319 void 320 ibuf_fd_set(struct ibuf *buf, int fd) 321 { 322 if (buf->fd != -1) 323 close(buf->fd); 324 buf->fd = fd; 325 } 326 327 int 328 ibuf_write(struct msgbuf *msgbuf) 329 { 330 struct iovec iov[IOV_MAX]; 331 struct ibuf *buf; 332 unsigned int i = 0; 333 ssize_t n; 334 335 memset(&iov, 0, sizeof(iov)); 336 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { 337 if (i >= IOV_MAX) 338 break; 339 iov[i].iov_base = buf->buf + buf->rpos; 340 iov[i].iov_len = buf->wpos - buf->rpos; 341 i++; 342 } 343 344 again: 345 if ((n = writev(msgbuf->fd, iov, i)) == -1) { 346 if (errno == EINTR) 347 goto again; 348 if (errno == ENOBUFS) 349 errno = EAGAIN; 350 return (-1); 351 } 352 353 if (n == 0) { /* connection closed */ 354 errno = 0; 355 return (0); 356 } 357 358 msgbuf_drain(msgbuf, n); 359 360 return (1); 361 } 362 363 void 364 msgbuf_init(struct msgbuf *msgbuf) 365 { 366 msgbuf->queued = 0; 367 msgbuf->fd = -1; 368 TAILQ_INIT(&msgbuf->bufs); 369 } 370 371 static void 372 msgbuf_drain(struct msgbuf *msgbuf, size_t n) 373 { 374 struct ibuf *buf, *next; 375 376 for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; 377 buf = next) { 378 next = TAILQ_NEXT(buf, entry); 379 if (n >= buf->wpos - buf->rpos) { 380 n -= buf->wpos - buf->rpos; 381 ibuf_dequeue(msgbuf, buf); 382 } else { 383 buf->rpos += n; 384 n = 0; 385 } 386 } 387 } 388 389 void 390 msgbuf_clear(struct msgbuf *msgbuf) 391 { 392 struct ibuf *buf; 393 394 while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) 395 ibuf_dequeue(msgbuf, buf); 396 } 397 398 int 399 msgbuf_write(struct msgbuf *msgbuf) 400 { 401 struct iovec iov[IOV_MAX]; 402 struct ibuf *buf, *buf0 = NULL; 403 unsigned int i = 0; 404 ssize_t n; 405 struct msghdr msg; 406 struct cmsghdr *cmsg; 407 union { 408 struct cmsghdr hdr; 409 char buf[CMSG_SPACE(sizeof(int))]; 410 } cmsgbuf; 411 412 memset(&iov, 0, sizeof(iov)); 413 memset(&msg, 0, sizeof(msg)); 414 memset(&cmsgbuf, 0, sizeof(cmsgbuf)); 415 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { 416 if (i >= IOV_MAX) 417 break; 418 if (i > 0 && buf->fd != -1) 419 break; 420 iov[i].iov_base = buf->buf + buf->rpos; 421 iov[i].iov_len = buf->wpos - buf->rpos; 422 i++; 423 if (buf->fd != -1) 424 buf0 = buf; 425 } 426 427 msg.msg_iov = iov; 428 msg.msg_iovlen = i; 429 430 if (buf0 != NULL) { 431 msg.msg_control = (caddr_t)&cmsgbuf.buf; 432 msg.msg_controllen = sizeof(cmsgbuf.buf); 433 cmsg = CMSG_FIRSTHDR(&msg); 434 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 435 cmsg->cmsg_level = SOL_SOCKET; 436 cmsg->cmsg_type = SCM_RIGHTS; 437 *(int *)CMSG_DATA(cmsg) = buf0->fd; 438 } 439 440 again: 441 if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { 442 if (errno == EINTR) 443 goto again; 444 if (errno == ENOBUFS) 445 errno = EAGAIN; 446 return (-1); 447 } 448 449 if (n == 0) { /* connection closed */ 450 errno = 0; 451 return (0); 452 } 453 454 /* 455 * assumption: fd got sent if sendmsg sent anything 456 * this works because fds are passed one at a time 457 */ 458 if (buf0 != NULL) { 459 close(buf0->fd); 460 buf0->fd = -1; 461 } 462 463 msgbuf_drain(msgbuf, n); 464 465 return (1); 466 } 467 468 static void 469 ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) 470 { 471 TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); 472 msgbuf->queued++; 473 } 474 475 static void 476 ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) 477 { 478 TAILQ_REMOVE(&msgbuf->bufs, buf, entry); 479 480 msgbuf->queued--; 481 ibuf_free(buf); 482 } 483