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