xref: /dragonfly/contrib/tnftp/src/ssl.c (revision 3a184c67)
1*3a184c67SAntonio Huete Jimenez /*	$NetBSD: ssl.c,v 1.7 2021/08/27 01:48:01 lukem Exp $	*/
2*3a184c67SAntonio Huete Jimenez /*	from	NetBSD: ssl.c,v 1.10 2021/06/03 10:23:33 lukem Exp	*/
36cdfca03SJohn Marino 
46cdfca03SJohn Marino /*-
56cdfca03SJohn Marino  * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
66cdfca03SJohn Marino  * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
76cdfca03SJohn Marino  * Copyright (c) 2015 Thomas Klausner <wiz@NetBSD.org>
86cdfca03SJohn Marino  * All rights reserved.
96cdfca03SJohn Marino  *
106cdfca03SJohn Marino  * Redistribution and use in source and binary forms, with or without
116cdfca03SJohn Marino  * modification, are permitted provided that the following conditions
126cdfca03SJohn Marino  * are met:
136cdfca03SJohn Marino  * 1. Redistributions of source code must retain the above copyright
146cdfca03SJohn Marino  *    notice, this list of conditions and the following disclaimer
156cdfca03SJohn Marino  *    in this position and unchanged.
166cdfca03SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
176cdfca03SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
186cdfca03SJohn Marino  *    documentation and/or other materials provided with the distribution.
196cdfca03SJohn Marino  * 3. The name of the author may not be used to endorse or promote products
206cdfca03SJohn Marino  *    derived from this software without specific prior written permission
216cdfca03SJohn Marino  *
226cdfca03SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
236cdfca03SJohn Marino  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
246cdfca03SJohn Marino  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
256cdfca03SJohn Marino  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
266cdfca03SJohn Marino  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
276cdfca03SJohn Marino  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
286cdfca03SJohn Marino  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
296cdfca03SJohn Marino  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
306cdfca03SJohn Marino  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
316cdfca03SJohn Marino  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
326cdfca03SJohn Marino  *
336cdfca03SJohn Marino  * $FreeBSD: common.c,v 1.53 2007/12/19 00:26:36 des Exp $
346cdfca03SJohn Marino  */
356cdfca03SJohn Marino 
366cdfca03SJohn Marino #include "tnftp.h"
376cdfca03SJohn Marino 
386cdfca03SJohn Marino #if 0	/* tnftp */
396cdfca03SJohn Marino 
406cdfca03SJohn Marino #include <sys/cdefs.h>
416cdfca03SJohn Marino #ifndef lint
42*3a184c67SAntonio Huete Jimenez __RCSID(" NetBSD: ssl.c,v 1.10 2021/06/03 10:23:33 lukem Exp  ");
436cdfca03SJohn Marino #endif
446cdfca03SJohn Marino 
45*3a184c67SAntonio Huete Jimenez #include <errno.h>
46*3a184c67SAntonio Huete Jimenez #include <fcntl.h>
47*3a184c67SAntonio Huete Jimenez #include <stdarg.h>
48*3a184c67SAntonio Huete Jimenez #include <stdio.h>
49*3a184c67SAntonio Huete Jimenez #include <stdlib.h>
50*3a184c67SAntonio Huete Jimenez #include <string.h>
516cdfca03SJohn Marino #include <time.h>
526cdfca03SJohn Marino #include <unistd.h>
536cdfca03SJohn Marino 
546cdfca03SJohn Marino #include <sys/param.h>
556cdfca03SJohn Marino #include <sys/select.h>
566cdfca03SJohn Marino #include <sys/uio.h>
576cdfca03SJohn Marino 
586cdfca03SJohn Marino #include <netinet/tcp.h>
596cdfca03SJohn Marino #include <netinet/in.h>
60*3a184c67SAntonio Huete Jimenez 
616cdfca03SJohn Marino #endif	/* tnftp */
626cdfca03SJohn Marino 
63*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
646cdfca03SJohn Marino #include <openssl/crypto.h>
656cdfca03SJohn Marino #include <openssl/x509.h>
666cdfca03SJohn Marino #include <openssl/pem.h>
676cdfca03SJohn Marino #include <openssl/ssl.h>
686cdfca03SJohn Marino #include <openssl/err.h>
69*3a184c67SAntonio Huete Jimenez #endif
706cdfca03SJohn Marino 
716cdfca03SJohn Marino #include "ssl.h"
726cdfca03SJohn Marino 
736cdfca03SJohn Marino extern int quit_time, verbose, ftp_debug;
746cdfca03SJohn Marino extern FILE *ttyout;
756cdfca03SJohn Marino 
766cdfca03SJohn Marino struct fetch_connect {
776cdfca03SJohn Marino 	int			 sd;		/* file/socket descriptor */
786cdfca03SJohn Marino 	char			*buf;		/* buffer */
796cdfca03SJohn Marino 	size_t			 bufsize;	/* buffer size */
806cdfca03SJohn Marino 	size_t			 bufpos;	/* position of buffer */
816cdfca03SJohn Marino 	size_t			 buflen;	/* length of buffer contents */
826cdfca03SJohn Marino 	struct {				/* data cached after an
836cdfca03SJohn Marino 						   interrupted read */
846cdfca03SJohn Marino 		char	*buf;
856cdfca03SJohn Marino 		size_t	 size;
866cdfca03SJohn Marino 		size_t	 pos;
876cdfca03SJohn Marino 		size_t	 len;
886cdfca03SJohn Marino 	} cache;
896cdfca03SJohn Marino 	int 			 issock;
906cdfca03SJohn Marino 	int			 iserr;
916cdfca03SJohn Marino 	int			 iseof;
92*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
936cdfca03SJohn Marino 	SSL			*ssl;		/* SSL handle */
94*3a184c67SAntonio Huete Jimenez #endif
956cdfca03SJohn Marino };
966cdfca03SJohn Marino 
976cdfca03SJohn Marino /*
986cdfca03SJohn Marino  * Write a vector to a connection w/ timeout
996cdfca03SJohn Marino  * Note: can modify the iovec.
1006cdfca03SJohn Marino  */
1016cdfca03SJohn Marino static ssize_t
fetch_writev(struct fetch_connect * conn,struct iovec * iov,int iovcnt)1026cdfca03SJohn Marino fetch_writev(struct fetch_connect *conn, struct iovec *iov, int iovcnt)
1036cdfca03SJohn Marino {
1046cdfca03SJohn Marino 	struct timeval now, timeout, delta;
1056cdfca03SJohn Marino 	fd_set writefds;
1066cdfca03SJohn Marino 	ssize_t len, total;
107*3a184c67SAntonio Huete Jimenez 	int fd = conn->sd;
1086cdfca03SJohn Marino 	int r;
1096cdfca03SJohn Marino 
1106cdfca03SJohn Marino 	if (quit_time > 0) {
1116cdfca03SJohn Marino 		FD_ZERO(&writefds);
1126cdfca03SJohn Marino 		gettimeofday(&timeout, NULL);
1136cdfca03SJohn Marino 		timeout.tv_sec += quit_time;
1146cdfca03SJohn Marino 	}
1156cdfca03SJohn Marino 
1166cdfca03SJohn Marino 	total = 0;
1176cdfca03SJohn Marino 	while (iovcnt > 0) {
118*3a184c67SAntonio Huete Jimenez 		while (quit_time > 0 && !FD_ISSET(fd, &writefds)) {
119*3a184c67SAntonio Huete Jimenez 			FD_SET(fd, &writefds);
1206cdfca03SJohn Marino 			gettimeofday(&now, NULL);
1216cdfca03SJohn Marino 			delta.tv_sec = timeout.tv_sec - now.tv_sec;
1226cdfca03SJohn Marino 			delta.tv_usec = timeout.tv_usec - now.tv_usec;
1236cdfca03SJohn Marino 			if (delta.tv_usec < 0) {
1246cdfca03SJohn Marino 				delta.tv_usec += 1000000;
1256cdfca03SJohn Marino 				delta.tv_sec--;
1266cdfca03SJohn Marino 			}
1276cdfca03SJohn Marino 			if (delta.tv_sec < 0) {
1286cdfca03SJohn Marino 				errno = ETIMEDOUT;
1296cdfca03SJohn Marino 				return -1;
1306cdfca03SJohn Marino 			}
1316cdfca03SJohn Marino 			errno = 0;
132*3a184c67SAntonio Huete Jimenez 			r = select(fd + 1, NULL, &writefds, NULL, &delta);
1336cdfca03SJohn Marino 			if (r == -1) {
1346cdfca03SJohn Marino 				if (errno == EINTR)
1356cdfca03SJohn Marino 					continue;
1366cdfca03SJohn Marino 				return -1;
1376cdfca03SJohn Marino 			}
1386cdfca03SJohn Marino 		}
1396cdfca03SJohn Marino 		errno = 0;
140*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
1416cdfca03SJohn Marino 		if (conn->ssl != NULL)
1426cdfca03SJohn Marino 			len = SSL_write(conn->ssl, iov->iov_base, iov->iov_len);
1436cdfca03SJohn Marino 		else
144*3a184c67SAntonio Huete Jimenez #endif
145*3a184c67SAntonio Huete Jimenez 			len = writev(fd, iov, iovcnt);
1466cdfca03SJohn Marino 		if (len == 0) {
1476cdfca03SJohn Marino 			/* we consider a short write a failure */
1486cdfca03SJohn Marino 			/* XXX perhaps we shouldn't in the SSL case */
1496cdfca03SJohn Marino 			errno = EPIPE;
1506cdfca03SJohn Marino 			return -1;
1516cdfca03SJohn Marino 		}
1526cdfca03SJohn Marino 		if (len < 0) {
153*3a184c67SAntonio Huete Jimenez 			if (errno == EINTR || errno == EAGAIN)
1546cdfca03SJohn Marino 				continue;
1556cdfca03SJohn Marino 			return -1;
1566cdfca03SJohn Marino 		}
1576cdfca03SJohn Marino 		total += len;
1586cdfca03SJohn Marino 		while (iovcnt > 0 && len >= (ssize_t)iov->iov_len) {
1596cdfca03SJohn Marino 			len -= iov->iov_len;
1606cdfca03SJohn Marino 			iov++;
1616cdfca03SJohn Marino 			iovcnt--;
1626cdfca03SJohn Marino 		}
1636cdfca03SJohn Marino 		if (iovcnt > 0) {
1646cdfca03SJohn Marino 			iov->iov_len -= len;
1656cdfca03SJohn Marino 			iov->iov_base = (char *)iov->iov_base + len;
1666cdfca03SJohn Marino 		}
1676cdfca03SJohn Marino 	}
1686cdfca03SJohn Marino 	return total;
1696cdfca03SJohn Marino }
1706cdfca03SJohn Marino 
171*3a184c67SAntonio Huete Jimenez static ssize_t
fetch_write(const void * str,size_t len,struct fetch_connect * conn)172*3a184c67SAntonio Huete Jimenez fetch_write(const void *str, size_t len, struct fetch_connect *conn)
1736cdfca03SJohn Marino {
1746cdfca03SJohn Marino 	struct iovec iov[1];
1756cdfca03SJohn Marino 
1766cdfca03SJohn Marino 	iov[0].iov_base = __DECONST(char *, str);
1776cdfca03SJohn Marino 	iov[0].iov_len = len;
1786cdfca03SJohn Marino 	return fetch_writev(conn, iov, 1);
1796cdfca03SJohn Marino }
1806cdfca03SJohn Marino 
1816cdfca03SJohn Marino /*
1826cdfca03SJohn Marino  * Send a formatted line; optionally echo to terminal
1836cdfca03SJohn Marino  */
1846cdfca03SJohn Marino int
fetch_printf(struct fetch_connect * conn,const char * fmt,...)1856cdfca03SJohn Marino fetch_printf(struct fetch_connect *conn, const char *fmt, ...)
1866cdfca03SJohn Marino {
1876cdfca03SJohn Marino 	va_list ap;
1886cdfca03SJohn Marino 	size_t len;
1896cdfca03SJohn Marino 	char *msg;
1906cdfca03SJohn Marino 	int r;
1916cdfca03SJohn Marino 
1926cdfca03SJohn Marino 	va_start(ap, fmt);
1936cdfca03SJohn Marino 	len = vasprintf(&msg, fmt, ap);
1946cdfca03SJohn Marino 	va_end(ap);
1956cdfca03SJohn Marino 
1966cdfca03SJohn Marino 	if (msg == NULL) {
1976cdfca03SJohn Marino 		errno = ENOMEM;
1986cdfca03SJohn Marino 		return -1;
1996cdfca03SJohn Marino 	}
2006cdfca03SJohn Marino 
201*3a184c67SAntonio Huete Jimenez 	r = fetch_write(msg, len, conn);
2026cdfca03SJohn Marino 	free(msg);
2036cdfca03SJohn Marino 	return r;
2046cdfca03SJohn Marino }
2056cdfca03SJohn Marino 
2066cdfca03SJohn Marino int
fetch_fileno(struct fetch_connect * conn)2076cdfca03SJohn Marino fetch_fileno(struct fetch_connect *conn)
2086cdfca03SJohn Marino {
2096cdfca03SJohn Marino 
2106cdfca03SJohn Marino 	return conn->sd;
2116cdfca03SJohn Marino }
2126cdfca03SJohn Marino 
2136cdfca03SJohn Marino int
fetch_error(struct fetch_connect * conn)2146cdfca03SJohn Marino fetch_error(struct fetch_connect *conn)
2156cdfca03SJohn Marino {
2166cdfca03SJohn Marino 
2176cdfca03SJohn Marino 	return conn->iserr;
2186cdfca03SJohn Marino }
2196cdfca03SJohn Marino 
2206cdfca03SJohn Marino static void
fetch_clearerr(struct fetch_connect * conn)2216cdfca03SJohn Marino fetch_clearerr(struct fetch_connect *conn)
2226cdfca03SJohn Marino {
2236cdfca03SJohn Marino 
2246cdfca03SJohn Marino 	conn->iserr = 0;
2256cdfca03SJohn Marino }
2266cdfca03SJohn Marino 
2276cdfca03SJohn Marino int
fetch_flush(struct fetch_connect * conn)2286cdfca03SJohn Marino fetch_flush(struct fetch_connect *conn)
2296cdfca03SJohn Marino {
2306cdfca03SJohn Marino 
2316cdfca03SJohn Marino 	if (conn->issock) {
232*3a184c67SAntonio Huete Jimenez 		int fd = conn->sd;
233*3a184c67SAntonio Huete Jimenez 		int v;
2346cdfca03SJohn Marino #ifdef TCP_NOPUSH
2356cdfca03SJohn Marino 		v = 0;
236*3a184c67SAntonio Huete Jimenez 		setsockopt(fd, IPPROTO_TCP, TCP_NOPUSH, &v, sizeof(v));
2376cdfca03SJohn Marino #endif
2386cdfca03SJohn Marino 		v = 1;
239*3a184c67SAntonio Huete Jimenez 		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v));
2406cdfca03SJohn Marino 	}
2416cdfca03SJohn Marino 	return 0;
2426cdfca03SJohn Marino }
2436cdfca03SJohn Marino 
2446cdfca03SJohn Marino /*ARGSUSED*/
2456cdfca03SJohn Marino struct fetch_connect *
fetch_open(const char * fname,const char * fmode)2466cdfca03SJohn Marino fetch_open(const char *fname, const char *fmode)
2476cdfca03SJohn Marino {
2486cdfca03SJohn Marino 	struct fetch_connect *conn;
2496cdfca03SJohn Marino 	int fd;
2506cdfca03SJohn Marino 
2516cdfca03SJohn Marino 	fd = open(fname, O_RDONLY); /* XXX: fmode */
2526cdfca03SJohn Marino 	if (fd < 0)
2536cdfca03SJohn Marino 		return NULL;
2546cdfca03SJohn Marino 
2556cdfca03SJohn Marino 	if ((conn = calloc(1, sizeof(*conn))) == NULL) {
2566cdfca03SJohn Marino 		close(fd);
2576cdfca03SJohn Marino 		return NULL;
2586cdfca03SJohn Marino 	}
2596cdfca03SJohn Marino 
2606cdfca03SJohn Marino 	conn->sd = fd;
2616cdfca03SJohn Marino 	conn->issock = 0;
2626cdfca03SJohn Marino 	return conn;
2636cdfca03SJohn Marino }
2646cdfca03SJohn Marino 
2656cdfca03SJohn Marino /*ARGSUSED*/
2666cdfca03SJohn Marino struct fetch_connect *
fetch_fdopen(int sd,const char * fmode)2676cdfca03SJohn Marino fetch_fdopen(int sd, const char *fmode)
2686cdfca03SJohn Marino {
2696cdfca03SJohn Marino 	struct fetch_connect *conn;
2706cdfca03SJohn Marino #if defined(SO_NOSIGPIPE) || defined(TCP_NOPUSH)
2716cdfca03SJohn Marino 	int opt = 1;
2726cdfca03SJohn Marino #endif
2736cdfca03SJohn Marino 
2746cdfca03SJohn Marino 	if ((conn = calloc(1, sizeof(*conn))) == NULL)
2756cdfca03SJohn Marino 		return NULL;
2766cdfca03SJohn Marino 
2776cdfca03SJohn Marino 	conn->sd = sd;
2786cdfca03SJohn Marino 	conn->issock = 1;
2796cdfca03SJohn Marino 	fcntl(sd, F_SETFD, FD_CLOEXEC);
2806cdfca03SJohn Marino #ifdef SO_NOSIGPIPE
2816cdfca03SJohn Marino 	setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
2826cdfca03SJohn Marino #endif
2836cdfca03SJohn Marino #ifdef TCP_NOPUSH
2846cdfca03SJohn Marino 	setsockopt(sd, IPPROTO_TCP, TCP_NOPUSH, &opt, sizeof(opt));
2856cdfca03SJohn Marino #endif
2866cdfca03SJohn Marino 	return conn;
2876cdfca03SJohn Marino }
2886cdfca03SJohn Marino 
2896cdfca03SJohn Marino int
fetch_close(struct fetch_connect * conn)2906cdfca03SJohn Marino fetch_close(struct fetch_connect *conn)
2916cdfca03SJohn Marino {
292*3a184c67SAntonio Huete Jimenez 	if (conn == NULL)
293*3a184c67SAntonio Huete Jimenez 		return 0;
2946cdfca03SJohn Marino 
2956cdfca03SJohn Marino 	fetch_flush(conn);
296*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
2976cdfca03SJohn Marino 	SSL_free(conn->ssl);
298*3a184c67SAntonio Huete Jimenez #endif
299*3a184c67SAntonio Huete Jimenez 	close(conn->sd);
3006cdfca03SJohn Marino 	free(conn->cache.buf);
3016cdfca03SJohn Marino 	free(conn->buf);
3026cdfca03SJohn Marino 	free(conn);
303*3a184c67SAntonio Huete Jimenez 	return 0;
3046cdfca03SJohn Marino }
3056cdfca03SJohn Marino 
306*3a184c67SAntonio Huete Jimenez #define FETCH_WRITE_WAIT	-3
3076cdfca03SJohn Marino #define FETCH_READ_WAIT		-2
3086cdfca03SJohn Marino #define FETCH_READ_ERROR	-1
3096cdfca03SJohn Marino 
310*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
3116cdfca03SJohn Marino static ssize_t
fetch_ssl_read(SSL * ssl,void * buf,size_t len)3126cdfca03SJohn Marino fetch_ssl_read(SSL *ssl, void *buf, size_t len)
3136cdfca03SJohn Marino {
3146cdfca03SJohn Marino 	ssize_t rlen;
3156cdfca03SJohn Marino 	rlen = SSL_read(ssl, buf, len);
316*3a184c67SAntonio Huete Jimenez 	if (rlen >= 0)
317*3a184c67SAntonio Huete Jimenez 		return rlen;
318*3a184c67SAntonio Huete Jimenez 
319*3a184c67SAntonio Huete Jimenez 	switch (SSL_get_error(ssl, rlen)) {
320*3a184c67SAntonio Huete Jimenez 	case SSL_ERROR_WANT_READ:
3216cdfca03SJohn Marino 		return FETCH_READ_WAIT;
322*3a184c67SAntonio Huete Jimenez 	case SSL_ERROR_WANT_WRITE:
323*3a184c67SAntonio Huete Jimenez 		return FETCH_WRITE_WAIT;
324*3a184c67SAntonio Huete Jimenez 	default:
3256cdfca03SJohn Marino 		ERR_print_errors_fp(ttyout);
3266cdfca03SJohn Marino 		return FETCH_READ_ERROR;
3276cdfca03SJohn Marino 	}
3286cdfca03SJohn Marino }
329*3a184c67SAntonio Huete Jimenez #endif /* WITH_SSL */
3306cdfca03SJohn Marino 
3316cdfca03SJohn Marino static ssize_t
fetch_nonssl_read(int sd,void * buf,size_t len)3326cdfca03SJohn Marino fetch_nonssl_read(int sd, void *buf, size_t len)
3336cdfca03SJohn Marino {
3346cdfca03SJohn Marino 	ssize_t rlen;
3356cdfca03SJohn Marino 
3366cdfca03SJohn Marino 	rlen = read(sd, buf, len);
337*3a184c67SAntonio Huete Jimenez 	if (rlen == -1) {
3386cdfca03SJohn Marino 		if (errno == EAGAIN || errno == EINTR)
3396cdfca03SJohn Marino 			return FETCH_READ_WAIT;
3406cdfca03SJohn Marino 		return FETCH_READ_ERROR;
3416cdfca03SJohn Marino 	}
3426cdfca03SJohn Marino 	return rlen;
3436cdfca03SJohn Marino }
3446cdfca03SJohn Marino 
3456cdfca03SJohn Marino /*
3466cdfca03SJohn Marino  * Cache some data that was read from a socket but cannot be immediately
3476cdfca03SJohn Marino  * returned because of an interrupted system call.
3486cdfca03SJohn Marino  */
3496cdfca03SJohn Marino static int
fetch_cache_data(struct fetch_connect * conn,char * src,size_t nbytes)3506cdfca03SJohn Marino fetch_cache_data(struct fetch_connect *conn, char *src, size_t nbytes)
3516cdfca03SJohn Marino {
3526cdfca03SJohn Marino 
3536cdfca03SJohn Marino 	if (conn->cache.size < nbytes) {
3546cdfca03SJohn Marino 		char *tmp = realloc(conn->cache.buf, nbytes);
3556cdfca03SJohn Marino 		if (tmp == NULL)
3566cdfca03SJohn Marino 			return -1;
3576cdfca03SJohn Marino 
3586cdfca03SJohn Marino 		conn->cache.buf = tmp;
3596cdfca03SJohn Marino 		conn->cache.size = nbytes;
3606cdfca03SJohn Marino 	}
3616cdfca03SJohn Marino 
3626cdfca03SJohn Marino 	memcpy(conn->cache.buf, src, nbytes);
3636cdfca03SJohn Marino 	conn->cache.len = nbytes;
3646cdfca03SJohn Marino 	conn->cache.pos = 0;
3656cdfca03SJohn Marino 	return 0;
3666cdfca03SJohn Marino }
3676cdfca03SJohn Marino 
368*3a184c67SAntonio Huete Jimenez static int
fetch_wait(struct fetch_connect * conn,ssize_t rlen,struct timeval * timeout)369*3a184c67SAntonio Huete Jimenez fetch_wait(struct fetch_connect *conn, ssize_t rlen, struct timeval *timeout)
370*3a184c67SAntonio Huete Jimenez {
371*3a184c67SAntonio Huete Jimenez 	struct timeval now, delta;
372*3a184c67SAntonio Huete Jimenez 	int fd = conn->sd;
373*3a184c67SAntonio Huete Jimenez 	fd_set fds;
374*3a184c67SAntonio Huete Jimenez 
375*3a184c67SAntonio Huete Jimenez 	FD_ZERO(&fds);
376*3a184c67SAntonio Huete Jimenez 	while (!FD_ISSET(fd, &fds)) {
377*3a184c67SAntonio Huete Jimenez 		FD_SET(fd, &fds);
378*3a184c67SAntonio Huete Jimenez 		if (quit_time > 0) {
379*3a184c67SAntonio Huete Jimenez 			gettimeofday(&now, NULL);
380*3a184c67SAntonio Huete Jimenez 			if (!timercmp(timeout, &now, >)) {
381*3a184c67SAntonio Huete Jimenez 				fprintf(ttyout, "\r\n%s: transfer aborted"
382*3a184c67SAntonio Huete Jimenez 				    " because stalled for %lu sec.\r\n",
383*3a184c67SAntonio Huete Jimenez 				    getprogname(), (unsigned long)quit_time);
384*3a184c67SAntonio Huete Jimenez 				errno = ETIMEDOUT;
385*3a184c67SAntonio Huete Jimenez 				conn->iserr = ETIMEDOUT;
386*3a184c67SAntonio Huete Jimenez 				return -1;
387*3a184c67SAntonio Huete Jimenez 			}
388*3a184c67SAntonio Huete Jimenez 			timersub(timeout, &now, &delta);
389*3a184c67SAntonio Huete Jimenez 		}
390*3a184c67SAntonio Huete Jimenez 		errno = 0;
391*3a184c67SAntonio Huete Jimenez 		if (select(fd + 1,
392*3a184c67SAntonio Huete Jimenez 			rlen == FETCH_READ_WAIT ? &fds : NULL,
393*3a184c67SAntonio Huete Jimenez 			rlen == FETCH_WRITE_WAIT ? &fds : NULL,
394*3a184c67SAntonio Huete Jimenez 			NULL, quit_time > 0 ? &delta : NULL) < 0) {
395*3a184c67SAntonio Huete Jimenez 			if (errno == EINTR)
396*3a184c67SAntonio Huete Jimenez 				continue;
397*3a184c67SAntonio Huete Jimenez 			conn->iserr = errno;
398*3a184c67SAntonio Huete Jimenez 			return -1;
399*3a184c67SAntonio Huete Jimenez 		}
400*3a184c67SAntonio Huete Jimenez 	}
401*3a184c67SAntonio Huete Jimenez 	return 0;
402*3a184c67SAntonio Huete Jimenez }
403*3a184c67SAntonio Huete Jimenez 
404*3a184c67SAntonio Huete Jimenez size_t
fetch_read(void * ptr,size_t size,size_t nmemb,struct fetch_connect * conn)4056cdfca03SJohn Marino fetch_read(void *ptr, size_t size, size_t nmemb, struct fetch_connect *conn)
4066cdfca03SJohn Marino {
4076cdfca03SJohn Marino 	ssize_t rlen, total;
4086cdfca03SJohn Marino 	size_t len;
4096cdfca03SJohn Marino 	char *start, *buf;
410*3a184c67SAntonio Huete Jimenez 	struct timeval timeout;
4116cdfca03SJohn Marino 
4126cdfca03SJohn Marino 	if (quit_time > 0) {
4136cdfca03SJohn Marino 		gettimeofday(&timeout, NULL);
4146cdfca03SJohn Marino 		timeout.tv_sec += quit_time;
4156cdfca03SJohn Marino 	}
4166cdfca03SJohn Marino 
4176cdfca03SJohn Marino 	total = 0;
4186cdfca03SJohn Marino 	start = buf = ptr;
4196cdfca03SJohn Marino 	len = size * nmemb;
4206cdfca03SJohn Marino 
4216cdfca03SJohn Marino 	if (conn->cache.len > 0) {
4226cdfca03SJohn Marino 		/*
4236cdfca03SJohn Marino 		 * The last invocation of fetch_read was interrupted by a
4246cdfca03SJohn Marino 		 * signal after some data had been read from the socket. Copy
4256cdfca03SJohn Marino 		 * the cached data into the supplied buffer before trying to
4266cdfca03SJohn Marino 		 * read from the socket again.
4276cdfca03SJohn Marino 		 */
4286cdfca03SJohn Marino 		total = (conn->cache.len < len) ? conn->cache.len : len;
4296cdfca03SJohn Marino 		memcpy(buf, conn->cache.buf, total);
4306cdfca03SJohn Marino 
4316cdfca03SJohn Marino 		conn->cache.len -= total;
4326cdfca03SJohn Marino 		conn->cache.pos += total;
4336cdfca03SJohn Marino 		len -= total;
4346cdfca03SJohn Marino 		buf += total;
4356cdfca03SJohn Marino 	}
4366cdfca03SJohn Marino 
4376cdfca03SJohn Marino 	while (len > 0) {
4386cdfca03SJohn Marino 		/*
4396cdfca03SJohn Marino 		 * The socket is non-blocking.  Instead of the canonical
4406cdfca03SJohn Marino 		 * select() -> read(), we do the following:
4416cdfca03SJohn Marino 		 *
4426cdfca03SJohn Marino 		 * 1) call read() or SSL_read().
4436cdfca03SJohn Marino 		 * 2) if an error occurred, return -1.
4446cdfca03SJohn Marino 		 * 3) if we received data but we still expect more,
4456cdfca03SJohn Marino 		 *    update our counters and loop.
4466cdfca03SJohn Marino 		 * 4) if read() or SSL_read() signaled EOF, return.
4476cdfca03SJohn Marino 		 * 5) if we did not receive any data but we're not at EOF,
4486cdfca03SJohn Marino 		 *    call select().
4496cdfca03SJohn Marino 		 *
4506cdfca03SJohn Marino 		 * In the SSL case, this is necessary because if we
4516cdfca03SJohn Marino 		 * receive a close notification, we have to call
4526cdfca03SJohn Marino 		 * SSL_read() one additional time after we've read
4536cdfca03SJohn Marino 		 * everything we received.
4546cdfca03SJohn Marino 		 *
4556cdfca03SJohn Marino 		 * In the non-SSL case, it may improve performance (very
4566cdfca03SJohn Marino 		 * slightly) when reading small amounts of data.
4576cdfca03SJohn Marino 		 */
458*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
4596cdfca03SJohn Marino 		if (conn->ssl != NULL)
4606cdfca03SJohn Marino 			rlen = fetch_ssl_read(conn->ssl, buf, len);
4616cdfca03SJohn Marino 		else
462*3a184c67SAntonio Huete Jimenez #endif
4636cdfca03SJohn Marino 			rlen = fetch_nonssl_read(conn->sd, buf, len);
464*3a184c67SAntonio Huete Jimenez 		switch (rlen) {
465*3a184c67SAntonio Huete Jimenez 		case 0:
466*3a184c67SAntonio Huete Jimenez 			conn->iseof = 1;
467*3a184c67SAntonio Huete Jimenez 			return total;
468*3a184c67SAntonio Huete Jimenez 		case FETCH_READ_ERROR:
469*3a184c67SAntonio Huete Jimenez 			conn->iserr = errno;
470*3a184c67SAntonio Huete Jimenez 			if (errno == EINTR)
471*3a184c67SAntonio Huete Jimenez 				fetch_cache_data(conn, start, total);
472*3a184c67SAntonio Huete Jimenez 			return 0;
473*3a184c67SAntonio Huete Jimenez 		case FETCH_READ_WAIT:
474*3a184c67SAntonio Huete Jimenez 		case FETCH_WRITE_WAIT:
475*3a184c67SAntonio Huete Jimenez 			if (fetch_wait(conn, rlen, &timeout) == -1)
476*3a184c67SAntonio Huete Jimenez 				return 0;
4776cdfca03SJohn Marino 			break;
478*3a184c67SAntonio Huete Jimenez 		default:
4796cdfca03SJohn Marino 			len -= rlen;
4806cdfca03SJohn Marino 			buf += rlen;
4816cdfca03SJohn Marino 			total += rlen;
482*3a184c67SAntonio Huete Jimenez 			break;
4836cdfca03SJohn Marino 		}
4846cdfca03SJohn Marino 	}
4856cdfca03SJohn Marino 	return total;
4866cdfca03SJohn Marino }
4876cdfca03SJohn Marino 
4886cdfca03SJohn Marino #define MIN_BUF_SIZE 1024
4896cdfca03SJohn Marino 
4906cdfca03SJohn Marino /*
4916cdfca03SJohn Marino  * Read a line of text from a connection w/ timeout
4926cdfca03SJohn Marino  */
4936cdfca03SJohn Marino char *
fetch_getln(char * str,int size,struct fetch_connect * conn)4946cdfca03SJohn Marino fetch_getln(char *str, int size, struct fetch_connect *conn)
4956cdfca03SJohn Marino {
4966cdfca03SJohn Marino 	size_t tmpsize;
497*3a184c67SAntonio Huete Jimenez 	size_t len;
4986cdfca03SJohn Marino 	char c;
4996cdfca03SJohn Marino 
5006cdfca03SJohn Marino 	if (conn->buf == NULL) {
5016cdfca03SJohn Marino 		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
5026cdfca03SJohn Marino 			errno = ENOMEM;
5036cdfca03SJohn Marino 			conn->iserr = 1;
5046cdfca03SJohn Marino 			return NULL;
5056cdfca03SJohn Marino 		}
5066cdfca03SJohn Marino 		conn->bufsize = MIN_BUF_SIZE;
5076cdfca03SJohn Marino 	}
5086cdfca03SJohn Marino 
5096cdfca03SJohn Marino 	if (conn->iserr || conn->iseof)
5106cdfca03SJohn Marino 		return NULL;
5116cdfca03SJohn Marino 
5126cdfca03SJohn Marino 	if (conn->buflen - conn->bufpos > 0)
5136cdfca03SJohn Marino 		goto done;
5146cdfca03SJohn Marino 
5156cdfca03SJohn Marino 	conn->buf[0] = '\0';
5166cdfca03SJohn Marino 	conn->bufpos = 0;
5176cdfca03SJohn Marino 	conn->buflen = 0;
5186cdfca03SJohn Marino 	do {
5196cdfca03SJohn Marino 		len = fetch_read(&c, sizeof(c), 1, conn);
5206cdfca03SJohn Marino 		if (len == 0) {
521*3a184c67SAntonio Huete Jimenez 			if (conn->iserr)
522*3a184c67SAntonio Huete Jimenez 				return NULL;
523*3a184c67SAntonio Huete Jimenez 			if (conn->iseof)
5246cdfca03SJohn Marino 				break;
525*3a184c67SAntonio Huete Jimenez 			abort();
5266cdfca03SJohn Marino 		}
5276cdfca03SJohn Marino 		conn->buf[conn->buflen++] = c;
5286cdfca03SJohn Marino 		if (conn->buflen == conn->bufsize) {
5296cdfca03SJohn Marino 			char *tmp = conn->buf;
5306cdfca03SJohn Marino 			tmpsize = conn->bufsize * 2 + 1;
5316cdfca03SJohn Marino 			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
5326cdfca03SJohn Marino 				errno = ENOMEM;
5336cdfca03SJohn Marino 				conn->iserr = 1;
5346cdfca03SJohn Marino 				return NULL;
5356cdfca03SJohn Marino 			}
5366cdfca03SJohn Marino 			conn->buf = tmp;
5376cdfca03SJohn Marino 			conn->bufsize = tmpsize;
5386cdfca03SJohn Marino 		}
5396cdfca03SJohn Marino 	} while (c != '\n');
5406cdfca03SJohn Marino 
5416cdfca03SJohn Marino 	if (conn->buflen == 0)
5426cdfca03SJohn Marino 		return NULL;
5436cdfca03SJohn Marino  done:
5446cdfca03SJohn Marino 	tmpsize = MIN(size - 1, (int)(conn->buflen - conn->bufpos));
5456cdfca03SJohn Marino 	memcpy(str, conn->buf + conn->bufpos, tmpsize);
5466cdfca03SJohn Marino 	str[tmpsize] = '\0';
5476cdfca03SJohn Marino 	conn->bufpos += tmpsize;
5486cdfca03SJohn Marino 	return str;
5496cdfca03SJohn Marino }
5506cdfca03SJohn Marino 
5516cdfca03SJohn Marino int
fetch_getline(struct fetch_connect * conn,char * buf,size_t buflen,const char ** errormsg)5526cdfca03SJohn Marino fetch_getline(struct fetch_connect *conn, char *buf, size_t buflen,
5536cdfca03SJohn Marino     const char **errormsg)
5546cdfca03SJohn Marino {
5556cdfca03SJohn Marino 	size_t len;
5566cdfca03SJohn Marino 	int rv;
5576cdfca03SJohn Marino 
5586cdfca03SJohn Marino 	if (fetch_getln(buf, buflen, conn) == NULL) {
5596cdfca03SJohn Marino 		if (conn->iseof) {	/* EOF */
5606cdfca03SJohn Marino 			rv = -2;
5616cdfca03SJohn Marino 			if (errormsg)
5626cdfca03SJohn Marino 				*errormsg = "\nEOF received";
5636cdfca03SJohn Marino 		} else {		/* error */
5646cdfca03SJohn Marino 			rv = -1;
5656cdfca03SJohn Marino 			if (errormsg)
5666cdfca03SJohn Marino 				*errormsg = "Error encountered";
5676cdfca03SJohn Marino 		}
5686cdfca03SJohn Marino 		fetch_clearerr(conn);
5696cdfca03SJohn Marino 		return rv;
5706cdfca03SJohn Marino 	}
5716cdfca03SJohn Marino 	len = strlen(buf);
5726cdfca03SJohn Marino 	if (buf[len - 1] == '\n') {	/* clear any trailing newline */
5736cdfca03SJohn Marino 		buf[--len] = '\0';
5746cdfca03SJohn Marino 	} else if (len == buflen - 1) {	/* line too long */
5756cdfca03SJohn Marino 		while (1) {
5766cdfca03SJohn Marino 			char c;
577*3a184c67SAntonio Huete Jimenez 			size_t rlen = fetch_read(&c, sizeof(c), 1, conn);
578*3a184c67SAntonio Huete Jimenez 			if (rlen == 0 || c == '\n')
5796cdfca03SJohn Marino 				break;
5806cdfca03SJohn Marino 		}
5816cdfca03SJohn Marino 		if (errormsg)
5826cdfca03SJohn Marino 			*errormsg = "Input line is too long";
5836cdfca03SJohn Marino 		fetch_clearerr(conn);
5846cdfca03SJohn Marino 		return -3;
5856cdfca03SJohn Marino 	}
5866cdfca03SJohn Marino 	if (errormsg)
5876cdfca03SJohn Marino 		*errormsg = NULL;
5886cdfca03SJohn Marino 	return len;
5896cdfca03SJohn Marino }
5906cdfca03SJohn Marino 
591*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
5926cdfca03SJohn Marino void *
fetch_start_ssl(int sock,const char * servername)5936cdfca03SJohn Marino fetch_start_ssl(int sock, const char *servername)
5946cdfca03SJohn Marino {
5956cdfca03SJohn Marino 	SSL *ssl;
5966cdfca03SJohn Marino 	SSL_CTX *ctx;
5976cdfca03SJohn Marino 	int ret, ssl_err;
5986cdfca03SJohn Marino 
5996cdfca03SJohn Marino 	/* Init the SSL library and context */
6006cdfca03SJohn Marino 	if (!SSL_library_init()){
6016cdfca03SJohn Marino 		fprintf(ttyout, "SSL library init failed\n");
6026cdfca03SJohn Marino 		return NULL;
6036cdfca03SJohn Marino 	}
6046cdfca03SJohn Marino 
6056cdfca03SJohn Marino 	SSL_load_error_strings();
6066cdfca03SJohn Marino 
6076cdfca03SJohn Marino 	ctx = SSL_CTX_new(SSLv23_client_method());
6086cdfca03SJohn Marino 	SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
6096cdfca03SJohn Marino 
6106cdfca03SJohn Marino 	ssl = SSL_new(ctx);
6116cdfca03SJohn Marino 	if (ssl == NULL){
6126cdfca03SJohn Marino 		fprintf(ttyout, "SSL context creation failed\n");
6136cdfca03SJohn Marino 		SSL_CTX_free(ctx);
6146cdfca03SJohn Marino 		return NULL;
6156cdfca03SJohn Marino 	}
6166cdfca03SJohn Marino 	SSL_set_fd(ssl, sock);
6176cdfca03SJohn Marino 	if (!SSL_set_tlsext_host_name(ssl, __UNCONST(servername))) {
6186cdfca03SJohn Marino 		fprintf(ttyout, "SSL hostname setting failed\n");
6196cdfca03SJohn Marino 		SSL_CTX_free(ctx);
6206cdfca03SJohn Marino 		return NULL;
6216cdfca03SJohn Marino 	}
6226cdfca03SJohn Marino 	while ((ret = SSL_connect(ssl)) == -1) {
6236cdfca03SJohn Marino 		ssl_err = SSL_get_error(ssl, ret);
6246cdfca03SJohn Marino 		if (ssl_err != SSL_ERROR_WANT_READ &&
6256cdfca03SJohn Marino 		    ssl_err != SSL_ERROR_WANT_WRITE) {
6266cdfca03SJohn Marino 			ERR_print_errors_fp(ttyout);
6276cdfca03SJohn Marino 			SSL_free(ssl);
6286cdfca03SJohn Marino 			return NULL;
6296cdfca03SJohn Marino 		}
6306cdfca03SJohn Marino 	}
6316cdfca03SJohn Marino 
6326cdfca03SJohn Marino 	if (ftp_debug && verbose) {
6336cdfca03SJohn Marino 		X509 *cert;
6346cdfca03SJohn Marino 		X509_NAME *name;
6356cdfca03SJohn Marino 		char *str;
6366cdfca03SJohn Marino 
6376cdfca03SJohn Marino 		fprintf(ttyout, "SSL connection established using %s\n",
6386cdfca03SJohn Marino 		    SSL_get_cipher(ssl));
6396cdfca03SJohn Marino 		cert = SSL_get_peer_certificate(ssl);
6406cdfca03SJohn Marino 		name = X509_get_subject_name(cert);
6416cdfca03SJohn Marino 		str = X509_NAME_oneline(name, 0, 0);
6426cdfca03SJohn Marino 		fprintf(ttyout, "Certificate subject: %s\n", str);
6436cdfca03SJohn Marino 		free(str);
6446cdfca03SJohn Marino 		name = X509_get_issuer_name(cert);
6456cdfca03SJohn Marino 		str = X509_NAME_oneline(name, 0, 0);
6466cdfca03SJohn Marino 		fprintf(ttyout, "Certificate issuer: %s\n", str);
6476cdfca03SJohn Marino 		free(str);
6486cdfca03SJohn Marino 	}
6496cdfca03SJohn Marino 
6506cdfca03SJohn Marino 	return ssl;
6516cdfca03SJohn Marino }
652*3a184c67SAntonio Huete Jimenez #endif /* WITH_SSL */
6536cdfca03SJohn Marino 
6546cdfca03SJohn Marino 
6556cdfca03SJohn Marino void
fetch_set_ssl(struct fetch_connect * conn,void * ssl)6566cdfca03SJohn Marino fetch_set_ssl(struct fetch_connect *conn, void *ssl)
6576cdfca03SJohn Marino {
658*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
6596cdfca03SJohn Marino 	conn->ssl = ssl;
660*3a184c67SAntonio Huete Jimenez #endif
6616cdfca03SJohn Marino }
662