xref: /openbsd-src/usr.sbin/smtpd/iobuf.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: iobuf.c,v 1.2 2012/02/01 17:25:29 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/param.h>
19 #include <sys/socket.h>
20 #include <sys/uio.h>
21 
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #ifdef IO_SSL
29 #include <openssl/ssl.h>
30 #endif
31 
32 #include "iobuf.h"
33 
34 #define IOBUF_MAX	65536
35 #define IOBUFQ_MIN	4096
36 
37 struct ioqbuf	*ioqbuf_alloc(struct iobuf *, size_t);
38 void		 iobuf_drain(struct iobuf *, size_t);
39 
40 int
41 iobuf_init(struct iobuf *io, size_t size, size_t max)
42 {
43 	memset(io, 0, sizeof *io);
44 
45 	if (max == 0)
46 		max = IOBUF_MAX;
47 
48 	if (size == 0)
49 		size = max;
50 
51 	if (size > max)
52 		return (-1);
53 
54 	if ((io->buf = malloc(size)) == NULL)
55 		return (-1);
56 
57 	io->size = size;
58 	io->max = max;
59 
60 	return (0);
61 }
62 
63 void
64 iobuf_clear(struct iobuf *io)
65 {
66 	struct ioqbuf	*q;
67 
68 	if (io->buf)
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 = realloc(io->buf, io->size + n);
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 			len = (i && buf[i - 1] == '\r') ? i - 1 : i;
177 			buf[len] = '\0';
178 			if (rlen)
179 				*rlen = len;
180 			return (buf);
181 		}
182 
183         return (NULL);
184 }
185 
186 void
187 iobuf_normalize(struct iobuf *io)
188 {
189 	if (io->rpos == 0)
190 		return;
191 
192 	if (io->rpos == io->wpos) {
193 		io->rpos = io->wpos = 0;
194 		return;
195 	}
196 
197 	memmove(io->buf, io->buf + io->rpos, io->wpos - io->rpos);
198 	io->wpos -= io->rpos;
199 	io->rpos = 0;
200 }
201 
202 ssize_t
203 iobuf_read(struct iobuf *io, int fd)
204 {
205 	ssize_t	n;
206 
207 	n = read(fd, io->buf + io->wpos, iobuf_left(io));
208 	if (n == -1) {
209 		/* XXX is this really what we want? */
210 		if (errno == EAGAIN || errno == EINTR)
211 			return (IOBUF_WANT_READ);
212 		return (IOBUF_ERROR);
213 	}
214 	if (n == 0)
215 		return (IOBUF_CLOSED);
216 
217 	io->wpos += n;
218 
219 	return (n);
220 }
221 
222 struct ioqbuf *
223 ioqbuf_alloc(struct iobuf *io, size_t len)
224 {
225 	struct ioqbuf   *q;
226 
227 	if (len < IOBUFQ_MIN)
228 		len = IOBUFQ_MIN;
229 
230 	if ((q = malloc(sizeof(*q) + len)) == NULL)
231 		return (NULL);
232 
233 	q->rpos = 0;
234         q->wpos = 0;
235         q->size = len;
236         q->next = NULL;
237 	q->buf = (char *)(q) + sizeof(*q);
238 
239 	if (io->outqlast == NULL)
240 		io->outq = q;
241 	else
242 		io->outqlast->next = q;
243 	io->outqlast = q;
244 
245 	return (q);
246 }
247 
248 size_t
249 iobuf_queued(struct iobuf *io)
250 {
251 	return io->queued;
252 }
253 
254 void *
255 iobuf_reserve(struct iobuf *io, size_t len)
256 {
257 	struct ioqbuf	*q;
258 	void		*r;
259 
260 	if (len == 0)
261 		return (NULL);
262 
263 	if (((q = io->outqlast) == NULL) || q->size - q->wpos <= len) {
264 		if ((q = ioqbuf_alloc(io, len)) == NULL)
265 			return (NULL);
266 	}
267 
268 	r = q->buf + q->wpos;
269 	q->wpos += len;
270 	io->queued += len;
271 
272 	return (r);
273 }
274 
275 int
276 iobuf_queue(struct iobuf *io, const void *data, size_t len)
277 {
278 	void	*buf;
279 
280 	if (len == 0)
281 		return (0);
282 
283 	if ((buf = iobuf_reserve(io, len)) == NULL)
284 		return (-1);
285 
286 	memmove(buf, data, len);
287 
288 	return (0);
289 }
290 
291 int
292 iobuf_queuev(struct iobuf *io, const struct iovec *iov, int iovcnt)
293 {
294 	int	 i;
295 	size_t	 len = 0;
296 	char	*buf;
297 
298 	for(i = 0; i < iovcnt; i++)
299 		len += iov[i].iov_len;
300 
301 	if ((buf = iobuf_reserve(io, len)) == NULL)
302 		return (-1);
303 
304 	for(i = 0; i < iovcnt; i++) {
305 		if (iov[i].iov_len == 0)
306 			continue;
307 		memmove(buf, iov[i].iov_base, iov[i].iov_len);
308 		buf += iov[i].iov_len;
309 	}
310 
311 	return (0);
312 
313 }
314 
315 int
316 iobuf_fqueue(struct iobuf *io, const char *fmt, ...)
317 {
318 	va_list	ap;
319 	int	len;
320 
321 	va_start(ap, fmt);
322 	len = iobuf_vfqueue(io, fmt, ap);
323 	va_end(ap);
324 
325 	return (len);
326 }
327 
328 int
329 iobuf_vfqueue(struct iobuf *io, const char *fmt, va_list ap)
330 {
331 	char	*buf;
332 	int	 len;
333 
334 	len = vasprintf(&buf, fmt, ap);
335 
336 	if (len == -1)
337 		return (-1);
338 
339 	len = iobuf_queue(io, buf, len);
340 	free(buf);
341 
342 	return (len);
343 }
344 
345 ssize_t
346 iobuf_write(struct iobuf *io, int fd)
347 {
348 	struct iovec	 iov[IOV_MAX];
349 	struct ioqbuf	*q;
350 	int		 i;
351 	ssize_t		 n;
352 
353 	i = 0;
354 	for(q = io->outq; q ; q = q->next) {
355 		if (i >= IOV_MAX)
356 			break;
357 		iov[i].iov_base = q->buf + q->rpos;
358 		iov[i].iov_len = q->wpos - q->rpos;
359 		i++;
360 	}
361 
362 	n = writev(fd, iov, i);
363 	if (n == -1) {
364 		if (errno == EAGAIN || errno == EINTR)
365 			return (IOBUF_WANT_WRITE);
366 		if (errno == EPIPE)
367 			return (IOBUF_CLOSED);
368 		return (IOBUF_ERROR);
369 	}
370 
371 	iobuf_drain(io, n);
372 
373 	return (n);
374 }
375 
376 int
377 iobuf_flush(struct iobuf *io, int fd)
378 {
379 	ssize_t	s;
380 
381 	while (io->queued)
382 		if ((s = iobuf_write(io, fd)) < 0)
383 			return (s);
384 
385 	return (0);
386 }
387 
388 #ifdef IO_SSL
389 
390 int
391 iobuf_flush_ssl(struct iobuf *io, void *ssl)
392 {
393 	ssize_t	s;
394 
395 	while (io->queued)
396 		if ((s = iobuf_write_ssl(io, ssl) < 0))
397 			return (s);
398 
399 	return (0);
400 }
401 
402 ssize_t
403 iobuf_write_ssl(struct iobuf *io, void *ssl)
404 {
405 	struct ioqbuf	*q;
406 	int		 r;
407 	ssize_t		 n;
408 
409 	q = io->outq;
410 	n = SSL_write(ssl, q->buf + q->rpos, q->wpos - q->rpos);
411 	if (n <= 0) {
412 		switch ((r = SSL_get_error(ssl, n))) {
413 		case SSL_ERROR_WANT_READ:
414 			return (IOBUF_WANT_READ);
415 		case SSL_ERROR_WANT_WRITE:
416 			return (IOBUF_WANT_WRITE);
417 		case SSL_ERROR_ZERO_RETURN:
418 			/* connection closed */
419 			return (IOBUF_CLOSED);
420 		default:
421 			return (IOBUF_ERROR);
422 		}
423 	}
424 	iobuf_drain(io, n);
425 
426 	return (n);
427 }
428 
429 ssize_t
430 iobuf_read_ssl(struct iobuf *io, void *ssl)
431 {
432 	ssize_t	n;
433 	int	r;
434 
435 	n = SSL_read(ssl, io->buf + io->wpos, iobuf_left(io));
436 	if (n < 0) {
437 		switch ((r = SSL_get_error(ssl, n))) {
438 		case SSL_ERROR_WANT_READ:
439 			return (IOBUF_WANT_READ);
440 		case SSL_ERROR_WANT_WRITE:
441 			return (IOBUF_WANT_WRITE);
442 		default:
443 			return (IOBUF_ERROR);
444 		}
445 	} else if (n == 0)
446 		return (IOBUF_CLOSED);
447 
448 	io->wpos += n;
449 
450 	return (n);
451 }
452 
453 #endif /* IO_SSL */
454