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