1 /* $OpenBSD: iobuf.c,v 1.15 2021/03/05 12:37:32 eric Exp $ */ 2 /* 3 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/socket.h> 20 #include <sys/uio.h> 21 22 #include <errno.h> 23 #include <limits.h> 24 #include <stdarg.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #ifdef IO_TLS 29 #include <tls.h> 30 #endif 31 #include <unistd.h> 32 33 #include "iobuf.h" 34 35 #define IOBUF_MAX 65536 36 #define IOBUFQ_MIN 4096 37 38 struct ioqbuf *ioqbuf_alloc(struct iobuf *, size_t); 39 void iobuf_drain(struct iobuf *, size_t); 40 41 int 42 iobuf_init(struct iobuf *io, size_t size, size_t max) 43 { 44 memset(io, 0, sizeof *io); 45 46 if (max == 0) 47 max = IOBUF_MAX; 48 49 if (size == 0) 50 size = max; 51 52 if (size > max) 53 return (-1); 54 55 if ((io->buf = calloc(size, 1)) == NULL) 56 return (-1); 57 58 io->size = size; 59 io->max = max; 60 61 return (0); 62 } 63 64 void 65 iobuf_clear(struct iobuf *io) 66 { 67 struct ioqbuf *q; 68 69 free(io->buf); 70 71 while ((q = io->outq)) { 72 io->outq = q->next; 73 free(q); 74 } 75 76 memset(io, 0, sizeof (*io)); 77 } 78 79 void 80 iobuf_drain(struct iobuf *io, size_t n) 81 { 82 struct ioqbuf *q; 83 size_t left = n; 84 85 while ((q = io->outq) && left) { 86 if ((q->wpos - q->rpos) > left) { 87 q->rpos += left; 88 left = 0; 89 } else { 90 left -= q->wpos - q->rpos; 91 io->outq = q->next; 92 free(q); 93 } 94 } 95 96 io->queued -= (n - left); 97 if (io->outq == NULL) 98 io->outqlast = NULL; 99 } 100 101 int 102 iobuf_extend(struct iobuf *io, size_t n) 103 { 104 char *t; 105 106 if (n > io->max) 107 return (-1); 108 109 if (io->max - io->size < n) 110 return (-1); 111 112 t = recallocarray(io->buf, io->size, io->size + n, 1); 113 if (t == NULL) 114 return (-1); 115 116 io->size += n; 117 io->buf = t; 118 119 return (0); 120 } 121 122 size_t 123 iobuf_left(struct iobuf *io) 124 { 125 return io->size - io->wpos; 126 } 127 128 size_t 129 iobuf_space(struct iobuf *io) 130 { 131 return io->size - (io->wpos - io->rpos); 132 } 133 134 size_t 135 iobuf_len(struct iobuf *io) 136 { 137 return io->wpos - io->rpos; 138 } 139 140 char * 141 iobuf_data(struct iobuf *io) 142 { 143 return io->buf + io->rpos; 144 } 145 146 void 147 iobuf_drop(struct iobuf *io, size_t n) 148 { 149 if (n >= iobuf_len(io)) { 150 io->rpos = io->wpos = 0; 151 return; 152 } 153 154 io->rpos += n; 155 } 156 157 char * 158 iobuf_getline(struct iobuf *iobuf, size_t *rlen) 159 { 160 char *buf; 161 size_t len, i; 162 163 buf = iobuf_data(iobuf); 164 len = iobuf_len(iobuf); 165 166 for (i = 0; i + 1 <= len; i++) 167 if (buf[i] == '\n') { 168 /* Note: the returned address points into the iobuf 169 * buffer. We NUL-end it for convenience, and discard 170 * the data from the iobuf, so that the caller doesn't 171 * have to do it. The data remains "valid" as long 172 * as the iobuf does not overwrite it, that is until 173 * the next call to iobuf_normalize() or iobuf_extend(). 174 */ 175 iobuf_drop(iobuf, i + 1); 176 buf[i] = '\0'; 177 if (rlen) 178 *rlen = i; 179 return (buf); 180 } 181 182 return (NULL); 183 } 184 185 void 186 iobuf_normalize(struct iobuf *io) 187 { 188 if (io->rpos == 0) 189 return; 190 191 if (io->rpos == io->wpos) { 192 io->rpos = io->wpos = 0; 193 return; 194 } 195 196 memmove(io->buf, io->buf + io->rpos, io->wpos - io->rpos); 197 io->wpos -= io->rpos; 198 io->rpos = 0; 199 } 200 201 ssize_t 202 iobuf_read(struct iobuf *io, int fd) 203 { 204 ssize_t n; 205 206 n = read(fd, io->buf + io->wpos, iobuf_left(io)); 207 if (n == -1) { 208 /* XXX is this really what we want? */ 209 if (errno == EAGAIN || errno == EINTR) 210 return (IOBUF_WANT_READ); 211 return (IOBUF_ERROR); 212 } 213 if (n == 0) 214 return (IOBUF_CLOSED); 215 216 io->wpos += n; 217 218 return (n); 219 } 220 221 struct ioqbuf * 222 ioqbuf_alloc(struct iobuf *io, size_t len) 223 { 224 struct ioqbuf *q; 225 226 if (len < IOBUFQ_MIN) 227 len = IOBUFQ_MIN; 228 229 if ((q = malloc(sizeof(*q) + len)) == NULL) 230 return (NULL); 231 232 q->rpos = 0; 233 q->wpos = 0; 234 q->size = len; 235 q->next = NULL; 236 q->buf = (char *)(q) + sizeof(*q); 237 238 if (io->outqlast == NULL) 239 io->outq = q; 240 else 241 io->outqlast->next = q; 242 io->outqlast = q; 243 244 return (q); 245 } 246 247 size_t 248 iobuf_queued(struct iobuf *io) 249 { 250 return io->queued; 251 } 252 253 void * 254 iobuf_reserve(struct iobuf *io, size_t len) 255 { 256 struct ioqbuf *q; 257 void *r; 258 259 if (len == 0) 260 return (NULL); 261 262 if (((q = io->outqlast) == NULL) || q->size - q->wpos <= len) { 263 if ((q = ioqbuf_alloc(io, len)) == NULL) 264 return (NULL); 265 } 266 267 r = q->buf + q->wpos; 268 q->wpos += len; 269 io->queued += len; 270 271 return (r); 272 } 273 274 int 275 iobuf_queue(struct iobuf *io, const void *data, size_t len) 276 { 277 void *buf; 278 279 if (len == 0) 280 return (0); 281 282 if ((buf = iobuf_reserve(io, len)) == NULL) 283 return (-1); 284 285 memmove(buf, data, len); 286 287 return (len); 288 } 289 290 int 291 iobuf_queuev(struct iobuf *io, const struct iovec *iov, int iovcnt) 292 { 293 int i; 294 size_t len = 0; 295 char *buf; 296 297 for (i = 0; i < iovcnt; i++) 298 len += iov[i].iov_len; 299 300 if ((buf = iobuf_reserve(io, len)) == NULL) 301 return (-1); 302 303 for (i = 0; i < iovcnt; i++) { 304 if (iov[i].iov_len == 0) 305 continue; 306 memmove(buf, iov[i].iov_base, iov[i].iov_len); 307 buf += iov[i].iov_len; 308 } 309 310 return (0); 311 312 } 313 314 int 315 iobuf_fqueue(struct iobuf *io, const char *fmt, ...) 316 { 317 va_list ap; 318 int len; 319 320 va_start(ap, fmt); 321 len = iobuf_vfqueue(io, fmt, ap); 322 va_end(ap); 323 324 return (len); 325 } 326 327 int 328 iobuf_vfqueue(struct iobuf *io, const char *fmt, va_list ap) 329 { 330 char *buf; 331 int len; 332 333 len = vasprintf(&buf, fmt, ap); 334 335 if (len == -1) 336 return (-1); 337 338 len = iobuf_queue(io, buf, len); 339 free(buf); 340 341 return (len); 342 } 343 344 ssize_t 345 iobuf_write(struct iobuf *io, int fd) 346 { 347 struct iovec iov[IOV_MAX]; 348 struct ioqbuf *q; 349 int i; 350 ssize_t n; 351 352 i = 0; 353 for (q = io->outq; q ; q = q->next) { 354 if (i >= IOV_MAX) 355 break; 356 iov[i].iov_base = q->buf + q->rpos; 357 iov[i].iov_len = q->wpos - q->rpos; 358 i++; 359 } 360 361 n = writev(fd, iov, i); 362 if (n == -1) { 363 if (errno == EAGAIN || errno == EINTR) 364 return (IOBUF_WANT_WRITE); 365 if (errno == EPIPE) 366 return (IOBUF_CLOSED); 367 return (IOBUF_ERROR); 368 } 369 370 iobuf_drain(io, n); 371 372 return (n); 373 } 374 375 int 376 iobuf_flush(struct iobuf *io, int fd) 377 { 378 ssize_t s; 379 380 while (io->queued) 381 if ((s = iobuf_write(io, fd)) < 0) 382 return (s); 383 384 return (0); 385 } 386 387 #ifdef IO_TLS 388 389 int 390 iobuf_flush_tls(struct iobuf *io, struct tls *tls) 391 { 392 ssize_t s; 393 394 while (io->queued) 395 if ((s = iobuf_write_tls(io, tls)) < 0) 396 return (s); 397 398 return (0); 399 } 400 401 ssize_t 402 iobuf_write_tls(struct iobuf *io, struct tls *tls) 403 { 404 struct ioqbuf *q; 405 ssize_t n; 406 407 q = io->outq; 408 409 n = tls_write(tls, q->buf + q->rpos, q->wpos - q->rpos); 410 if (n == TLS_WANT_POLLIN) 411 return (IOBUF_WANT_READ); 412 else if (n == TLS_WANT_POLLOUT) 413 return (IOBUF_WANT_WRITE); 414 else if (n == 0) 415 return (IOBUF_CLOSED); 416 else if (n == -1) 417 return (IOBUF_ERROR); 418 419 iobuf_drain(io, n); 420 421 return (n); 422 } 423 424 ssize_t 425 iobuf_read_tls(struct iobuf *io, struct tls *tls) 426 { 427 ssize_t n; 428 429 n = tls_read(tls, io->buf + io->wpos, iobuf_left(io)); 430 if (n == TLS_WANT_POLLIN) 431 return (IOBUF_WANT_READ); 432 else if (n == TLS_WANT_POLLOUT) 433 return (IOBUF_WANT_WRITE); 434 else if (n == 0) 435 return (IOBUF_CLOSED); 436 else if (n == -1) 437 return (IOBUF_ERROR); 438 439 io->wpos += n; 440 441 return (n); 442 } 443 444 #endif /* IO_TLS */ 445