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