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