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
176d9574910SJohn 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