xref: /openbsd/usr.sbin/smtpd/iobuf.c (revision 73471bf0)
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
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
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
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
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
121 iobuf_left(struct iobuf *io)
122 {
123 	return io->size - io->wpos;
124 }
125 
126 size_t
127 iobuf_space(struct iobuf *io)
128 {
129 	return io->size - (io->wpos - io->rpos);
130 }
131 
132 size_t
133 iobuf_len(struct iobuf *io)
134 {
135 	return io->wpos - io->rpos;
136 }
137 
138 char *
139 iobuf_data(struct iobuf *io)
140 {
141 	return io->buf + io->rpos;
142 }
143 
144 void
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 *
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
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
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 *
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
246 iobuf_queued(struct iobuf *io)
247 {
248 	return io->queued;
249 }
250 
251 void *
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
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
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
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
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
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
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
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
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
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