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