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