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