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