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
36struct ioqbuf	*ioqbuf_alloc(struct iobuf *, size_t);
37void		 iobuf_drain(struct iobuf *, size_t);
38
39int
40iobuf_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
62void
63iobuf_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
77void
78iobuf_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
99int
100iobuf_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
120size_t
121iobuf_left(struct iobuf *io)
122{
123	return io->size - io->wpos;
124}
125
126size_t
127iobuf_space(struct iobuf *io)
128{
129	return io->size - (io->wpos - io->rpos);
130}
131
132size_t
133iobuf_len(struct iobuf *io)
134{
135	return io->wpos - io->rpos;
136}
137
138char *
139iobuf_data(struct iobuf *io)
140{
141	return io->buf + io->rpos;
142}
143
144void
145iobuf_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
155char *
156iobuf_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
183void
184iobuf_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
199ssize_t
200iobuf_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
219struct ioqbuf *
220ioqbuf_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
245size_t
246iobuf_queued(struct iobuf *io)
247{
248	return io->queued;
249}
250
251void *
252iobuf_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
272int
273iobuf_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
288int
289iobuf_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
312int
313iobuf_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
325int
326iobuf_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
342ssize_t
343iobuf_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
373int
374iobuf_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
387int
388iobuf_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
399ssize_t
400iobuf_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
422ssize_t
423iobuf_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