Module Name:    src
Committed By:   christos
Date:           Fri Dec 21 18:07:37 UTC 2012

Modified Files:
        src/usr.bin/ftp: Makefile fetch.c ftp_var.h main.c
Added Files:
        src/usr.bin/ftp: ssl.c ssl.h

Log Message:
PR/47276: Add https support


To generate a diff of this commit:
cvs rdiff -u -r1.35 -r1.36 src/usr.bin/ftp/Makefile
cvs rdiff -u -r1.198 -r1.199 src/usr.bin/ftp/fetch.c
cvs rdiff -u -r1.81 -r1.82 src/usr.bin/ftp/ftp_var.h
cvs rdiff -u -r1.120 -r1.121 src/usr.bin/ftp/main.c
cvs rdiff -u -r0 -r1.1 src/usr.bin/ftp/ssl.c src/usr.bin/ftp/ssl.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/ftp/Makefile
diff -u src/usr.bin/ftp/Makefile:1.35 src/usr.bin/ftp/Makefile:1.36
--- src/usr.bin/ftp/Makefile:1.35	Sun Aug 14 08:58:15 2011
+++ src/usr.bin/ftp/Makefile	Fri Dec 21 13:07:36 2012
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.35 2011/08/14 12:58:15 christos Exp $
+#	$NetBSD: Makefile,v 1.36 2012/12/21 18:07:36 christos Exp $
 #	from: @(#)Makefile	8.2 (Berkeley) 4/3/94
 
 .include <bsd.own.mk>
@@ -18,6 +18,12 @@ CPPFLAGS+=-DNO_EDITCOMPLETE -DNO_ABOUT -
 .else
 LDADD+=	-ledit -lterminfo
 DPADD+=	${LIBEDIT} ${LIBTERMINFO}
+.if (${MKCRYPTO} != "no")
+CPPFLAGS+= -DWITH_SSL
+SRCS+=ssl.c
+LDADD+= -lssl -lcrypto
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+.endif
 .endif
 
 .if (!defined(SMALLPROG) || defined(SMALLPROG_INET6)) && (${USE_INET6} != "no")

Index: src/usr.bin/ftp/fetch.c
diff -u src/usr.bin/ftp/fetch.c:1.198 src/usr.bin/ftp/fetch.c:1.199
--- src/usr.bin/ftp/fetch.c:1.198	Wed Jul  4 02:09:37 2012
+++ src/usr.bin/ftp/fetch.c	Fri Dec 21 13:07:36 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: fetch.c,v 1.198 2012/07/04 06:09:37 is Exp $	*/
+/*	$NetBSD: fetch.c,v 1.199 2012/12/21 18:07:36 christos Exp $	*/
 
 /*-
  * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: fetch.c,v 1.198 2012/07/04 06:09:37 is Exp $");
+__RCSID("$NetBSD: fetch.c,v 1.199 2012/12/21 18:07:36 christos Exp $");
 #endif /* not lint */
 
 /*
@@ -64,12 +64,16 @@ __RCSID("$NetBSD: fetch.c,v 1.198 2012/0
 #include <unistd.h>
 #include <time.h>
 
+#include "ssl.h"
 #include "ftp_var.h"
 #include "version.h"
 
 typedef enum {
 	UNKNOWN_URL_T=-1,
 	HTTP_URL_T,
+#ifdef WITH_SSL
+	HTTPS_URL_T,
+#endif
 	FTP_URL_T,
 	FILE_URL_T,
 	CLASSIC_URL_T
@@ -100,7 +104,15 @@ static int	redirect_loop;
 #define	FILE_URL	"file://"	/* file URL prefix */
 #define	FTP_URL		"ftp://";	/* ftp URL prefix */
 #define	HTTP_URL	"http://";	/* http URL prefix */
+#ifdef WITH_SSL
+#define	HTTPS_URL	"https://";	/* https URL prefix */
 
+#define	IS_HTTP_TYPE(urltype) \
+	(((urltype) == HTTP_URL_T) || ((urltype) == HTTPS_URL_T))
+#else
+#define	IS_HTTP_TYPE(urltype) \
+	((urltype) == HTTP_URL_T)
+#endif
 
 /*
  * Determine if token is the next word in buf (case insensitive).
@@ -346,6 +358,13 @@ parse_url(const char *url, const char *d
 	} else if (STRNEQUAL(url, FILE_URL)) {
 		url += sizeof(FILE_URL) - 1;
 		*utype = FILE_URL_T;
+#ifdef WITH_SSL
+	} else if (STRNEQUAL(url, HTTPS_URL)) {
+		url += sizeof(HTTPS_URL) - 1;
+		*utype = HTTPS_URL_T;
+		*portnum = HTTPS_PORT;
+		tport = httpsport;
+#endif
 	} else {
 		warnx("Invalid %s `%s'", desc, url);
  cleanup_parse_url:
@@ -498,17 +517,21 @@ fetch_url(const char *url, const char *p
 	char			*puser, *ppass, *useragent;
 	off_t			hashbytes, rangestart, rangeend, entitylen;
 	int			(*volatile closefunc)(FILE *);
-	FILE			*volatile fin;
+	FETCH			*volatile fin;
 	FILE			*volatile fout;
 	time_t			mtime;
 	url_t			urltype;
 	in_port_t		portnum;
+#ifdef WITH_SSL
+	void			*ssl;
+#endif
 
 	DPRINTF("fetch_url: `%s' proxyenv `%s'\n", url, STRorNULL(proxyenv));
 
 	oldintr = oldintp = NULL;
 	closefunc = NULL;
-	fin = fout = NULL;
+	fin = NULL;
+	fout = NULL;
 	s = -1;
 	savefile = NULL;
 	auth = location = message = NULL;
@@ -531,7 +554,7 @@ fetch_url(const char *url, const char *p
 			rval = fetch_ftp(url);
 			goto cleanup_fetch_url;
 		}
-		if (urltype != HTTP_URL_T || outfile == NULL)  {
+		if (!IS_HTTP_TYPE(urltype) || outfile == NULL)  {
 			warnx("Invalid URL (no file after host) `%s'", url);
 			goto cleanup_fetch_url;
 		}
@@ -571,17 +594,17 @@ fetch_url(const char *url, const char *p
 	}
 	if (urltype == FILE_URL_T) {		/* file:// URLs */
 		direction = "copied";
-		fin = fopen(decodedpath, "r");
+		fin = fetch_open(decodedpath, "r");
 		if (fin == NULL) {
 			warn("Can't open `%s'", decodedpath);
 			goto cleanup_fetch_url;
 		}
-		if (fstat(fileno(fin), &sb) == 0) {
+		if (fstat(fetch_fileno(fin), &sb) == 0) {
 			mtime = sb.st_mtime;
 			filesize = sb.st_size;
 		}
 		if (restart_point) {
-			if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
+			if (lseek(fetch_fileno(fin), restart_point, SEEK_SET) < 0) {
 				warn("Can't seek to restart `%s'",
 				    decodedpath);
 				goto cleanup_fetch_url;
@@ -594,12 +617,15 @@ fetch_url(const char *url, const char *p
 				    (LLT)restart_point);
 			fputs("\n", ttyout);
 		}
+		if (0 == rcvbuf_size) {
+			rcvbuf_size = 8 * 1024; /* XXX */
+		}
 	} else {				/* ftp:// or http:// URLs */
 		const char *leading;
 		int hasleading;
 
 		if (proxyenv == NULL) {
-			if (urltype == HTTP_URL_T)
+			if (IS_HTTP_TYPE(urltype))
 				proxyenv = getoptionvalue("http_proxy");
 			else if (urltype == FTP_URL_T)
 				proxyenv = getoptionvalue("ftp_proxy");
@@ -660,7 +686,7 @@ fetch_url(const char *url, const char *p
 				    &ppath) == -1)
 					goto cleanup_fetch_url;
 
-				if ((purltype != HTTP_URL_T
+				if ((!IS_HTTP_TYPE(purltype)
 				     && purltype != FTP_URL_T) ||
 				    EMPTYSTRING(phost) ||
 				    (! EMPTYSTRING(ppath)
@@ -690,6 +716,7 @@ fetch_url(const char *url, const char *p
 				FREEPTR(path);
 				path = ftp_strdup(url);
 				FREEPTR(ppath);
+				urltype = purltype;
 			}
 		} /* ! EMPTYSTRING(proxyenv) */
 
@@ -709,6 +736,9 @@ fetch_url(const char *url, const char *p
 			host = res0->ai_canonname;
 
 		s = -1;
+#ifdef WITH_SSL
+		ssl = NULL;
+#endif
 		for (res = res0; res; res = res->ai_next) {
 			char	hname[NI_MAXHOST], sname[NI_MAXSERV];
 
@@ -741,6 +771,16 @@ fetch_url(const char *url, const char *p
 				continue;
 			}
 
+#ifdef WITH_SSL
+			if (urltype == HTTPS_URL_T) {
+				if ((ssl = fetch_start_ssl(s)) == NULL) {
+					close(s);
+					s = -1;
+					continue;
+				}
+			}
+#endif
+
 			/* success */
 			break;
 		}
@@ -750,7 +790,9 @@ fetch_url(const char *url, const char *p
 			goto cleanup_fetch_url;
 		}
 
-		fin = fdopen(s, "r+");
+		fin = fetch_fdopen(s, "r+");
+		fetch_set_ssl(fin, ssl);
+
 		/*
 		 * Construct and send the request.
 		 */
@@ -765,11 +807,11 @@ fetch_url(const char *url, const char *p
 				leading = ", ";
 				hasleading++;
 			}
-			fprintf(fin, "GET %s HTTP/1.0\r\n", path);
+			fetch_printf(fin, "GET %s HTTP/1.0\r\n", path);
 			if (flushcache)
-				fprintf(fin, "Pragma: no-cache\r\n");
+				fetch_printf(fin, "Pragma: no-cache\r\n");
 		} else {
-			fprintf(fin, "GET %s HTTP/1.1\r\n", path);
+			fetch_printf(fin, "GET %s HTTP/1.1\r\n", path);
 			if (strchr(host, ':')) {
 				char *h, *p;
 
@@ -782,18 +824,23 @@ fetch_url(const char *url, const char *p
 				    (p = strchr(h, '%')) != NULL) {
 					*p = '\0';
 				}
-				fprintf(fin, "Host: [%s]", h);
+				fetch_printf(fin, "Host: [%s]", h);
 				free(h);
 			} else
-				fprintf(fin, "Host: %s", host);
+				fetch_printf(fin, "Host: %s", host);
+#ifdef WITH_SSL
+			if ((urltype == HTTP_URL_T && portnum != HTTP_PORT) ||
+			    (urltype == HTTPS_URL_T && portnum != HTTPS_PORT))
+#else
 			if (portnum != HTTP_PORT)
-				fprintf(fin, ":%u", portnum);
-			fprintf(fin, "\r\n");
-			fprintf(fin, "Accept: */*\r\n");
-			fprintf(fin, "Connection: close\r\n");
+#endif
+				fetch_printf(fin, ":%u", portnum);
+			fetch_printf(fin, "\r\n");
+			fetch_printf(fin, "Accept: */*\r\n");
+			fetch_printf(fin, "Connection: close\r\n");
 			if (restart_point) {
 				fputs(leading, ttyout);
-				fprintf(fin, "Range: bytes=" LLF "-\r\n",
+				fprintf(ttyout, "Range: bytes=" LLF "-\r\n",
 				    (LLT)restart_point);
 				fprintf(ttyout, "restarting at " LLF,
 				    (LLT)restart_point);
@@ -801,12 +848,12 @@ fetch_url(const char *url, const char *p
 				hasleading++;
 			}
 			if (flushcache)
-				fprintf(fin, "Cache-Control: no-cache\r\n");
+				fetch_printf(fin, "Cache-Control: no-cache\r\n");
 		}
 		if ((useragent=getenv("FTPUSERAGENT")) != NULL) {
-			fprintf(fin, "User-Agent: %s\r\n", useragent);
+			fetch_printf(fin, "User-Agent: %s\r\n", useragent);
 		} else {
-			fprintf(fin, "User-Agent: %s/%s\r\n",
+			fetch_printf(fin, "User-Agent: %s/%s\r\n",
 			    FTP_PRODUCT, FTP_VERSION);
 		}
 		if (wwwauth) {
@@ -816,7 +863,7 @@ fetch_url(const char *url, const char *p
 				leading = ", ";
 				hasleading++;
 			}
-			fprintf(fin, "Authorization: %s\r\n", wwwauth);
+			fetch_printf(fin, "Authorization: %s\r\n", wwwauth);
 		}
 		if (proxyauth) {
 			if (verbose) {
@@ -825,18 +872,18 @@ fetch_url(const char *url, const char *p
 				leading = ", ";
 				hasleading++;
 			}
-			fprintf(fin, "Proxy-Authorization: %s\r\n", proxyauth);
+			fetch_printf(fin, "Proxy-Authorization: %s\r\n", proxyauth);
 		}
 		if (verbose && hasleading)
 			fputs(")\n", ttyout);
-		fprintf(fin, "\r\n");
-		if (fflush(fin) == EOF) {
+		fetch_printf(fin, "\r\n");
+		if (fetch_flush(fin) == EOF) {
 			warn("Writing HTTP request");
 			goto cleanup_fetch_url;
 		}
 
 				/* Read the response */
-		len = get_line(fin, buf, sizeof(buf), &errormsg);
+		len = fetch_getline(fin, buf, sizeof(buf), &errormsg);
 		if (len < 0) {
 			if (*errormsg == '\n')
 				errormsg++;
@@ -860,7 +907,7 @@ fetch_url(const char *url, const char *p
 
 				/* Read the rest of the header. */
 		while (1) {
-			len = get_line(fin, buf, sizeof(buf), &errormsg);
+			len = fetch_getline(fin, buf, sizeof(buf), &errormsg);
 			if (len < 0) {
 				if (*errormsg == '\n')
 					errormsg++;
@@ -1148,7 +1195,7 @@ fetch_url(const char *url, const char *p
 		lastchunk = 0;
 					/* read chunk-size */
 		if (ischunked) {
-			if (fgets(xferbuf, bufsize, fin) == NULL) {
+			if (fetch_getln(xferbuf, bufsize, fin) == NULL) {
 				warnx("Unexpected EOF reading chunk-size");
 				goto cleanup_fetch_url;
 			}
@@ -1201,7 +1248,7 @@ fetch_url(const char *url, const char *p
 			if (ischunked)
 				bufrem = MIN(chunksize, bufrem);
 			while (bufrem > 0) {
-				flen = fread(xferbuf, sizeof(char),
+				flen = fetch_read(xferbuf, sizeof(char),
 				    MIN((off_t)bufsize, bufrem), fin);
 				if (flen <= 0)
 					goto chunkdone;
@@ -1240,7 +1287,7 @@ fetch_url(const char *url, const char *p
 					/* read CRLF after chunk*/
  chunkdone:
 		if (ischunked) {
-			if (fgets(xferbuf, bufsize, fin) == NULL) {
+			if (fetch_getln(xferbuf, bufsize, fin) == NULL) {
 				warnx("Unexpected EOF reading chunk CRLF");
 				goto cleanup_fetch_url;
 			}
@@ -1260,7 +1307,7 @@ fetch_url(const char *url, const char *p
 			(void)putc('#', ttyout);
 		(void)putc('\n', ttyout);
 	}
-	if (ferror(fin)) {
+	if (fetch_error(fin)) {
 		warn("Reading file");
 		goto cleanup_fetch_url;
 	}
@@ -1297,7 +1344,7 @@ fetch_url(const char *url, const char *p
 	if (oldintp)
 		(void)xsignal(SIGPIPE, oldintp);
 	if (fin != NULL)
-		fclose(fin);
+		fetch_close(fin);
 	else if (s != -1)
 		close(s);
 	if (closefunc != NULL && fout != NULL)
@@ -1729,7 +1776,11 @@ go_fetch(const char *url)
 	/*
 	 * Check for file:// and http:// URLs.
 	 */
-	if (STRNEQUAL(url, HTTP_URL) || STRNEQUAL(url, FILE_URL))
+	if (STRNEQUAL(url, HTTP_URL)
+#ifdef WITH_SSL
+	    || STRNEQUAL(url, HTTPS_URL)
+#endif
+	    || STRNEQUAL(url, FILE_URL))
 		return (fetch_url(url, NULL, NULL, NULL));
 
 	/*

Index: src/usr.bin/ftp/ftp_var.h
diff -u src/usr.bin/ftp/ftp_var.h:1.81 src/usr.bin/ftp/ftp_var.h:1.82
--- src/usr.bin/ftp/ftp_var.h:1.81	Sun Apr 12 06:18:52 2009
+++ src/usr.bin/ftp/ftp_var.h	Fri Dec 21 13:07:36 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: ftp_var.h,v 1.81 2009/04/12 10:18:52 lukem Exp $	*/
+/*	$NetBSD: ftp_var.h,v 1.82 2012/12/21 18:07:36 christos Exp $	*/
 
 /*-
  * Copyright (c) 1996-2009 The NetBSD Foundation, Inc.
@@ -177,6 +177,7 @@ enum {
 
 #define	FTP_PORT	21	/* default if ! getservbyname("ftp/tcp") */
 #define	HTTP_PORT	80	/* default if ! getservbyname("http/tcp") */
+#define	HTTPS_PORT	443	/* default if ! getservbyname("https/tcp") */
 #ifndef	GATE_PORT
 #define	GATE_PORT	21	/* default if ! getservbyname("ftpgate/tcp") */
 #endif
@@ -273,6 +274,9 @@ GLOBAL	char   *username;	/* name of user
 GLOBAL	sa_family_t family;	/* address family to use for connections */
 GLOBAL	const char *ftpport;	/* port number to use for FTP connections */
 GLOBAL	const char *httpport;	/* port number to use for HTTP connections */
+#ifdef WITH_SSL
+GLOBAL	const char *httpsport;	/* port number to use for HTTPS connections */
+#endif
 GLOBAL	const char *gateport;	/* port number to use for gateftp connections */
 GLOBAL	struct addrinfo *bindai; /* local address to bind as */
 

Index: src/usr.bin/ftp/main.c
diff -u src/usr.bin/ftp/main.c:1.120 src/usr.bin/ftp/main.c:1.121
--- src/usr.bin/ftp/main.c:1.120	Sat Dec 10 00:53:58 2011
+++ src/usr.bin/ftp/main.c	Fri Dec 21 13:07:36 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: main.c,v 1.120 2011/12/10 05:53:58 lukem Exp $	*/
+/*	$NetBSD: main.c,v 1.121 2012/12/21 18:07:36 christos Exp $	*/
 
 /*-
  * Copyright (c) 1996-2009 The NetBSD Foundation, Inc.
@@ -98,7 +98,7 @@ __COPYRIGHT("@(#) Copyright (c) 1985, 19
 #if 0
 static char sccsid[] = "@(#)main.c	8.6 (Berkeley) 10/9/94";
 #else
-__RCSID("$NetBSD: main.c,v 1.120 2011/12/10 05:53:58 lukem Exp $");
+__RCSID("$NetBSD: main.c,v 1.121 2012/12/21 18:07:36 christos Exp $");
 #endif
 #endif /* not lint */
 
@@ -150,6 +150,9 @@ main(int volatile argc, char **volatile 
 
 	ftpport = "ftp";
 	httpport = "http";
+#ifdef WITH_SSL
+	httpsport = "https";
+#endif
 	gateport = NULL;
 	cp = getenv("FTPSERVERPORT");
 	if (cp != NULL)
@@ -1044,6 +1047,9 @@ usage(void)
 "           [[user@]host [port]] [host:path[/]] [file:///file]\n"
 "           [ftp://[user[:pass]@]host[:port]/path[/]]\n";
 "           [http://[user[:pass]@]host[:port]/path] [...]\n"
+#ifdef WITH_SSL
+"           [https://[user[:pass]@]host[:port]/path] [...]\n"
+#endif
 "       %s -u URL file [...]\n", progname, progname);
 	exit(1);
 }

Added files:

Index: src/usr.bin/ftp/ssl.c
diff -u /dev/null src/usr.bin/ftp/ssl.c:1.1
--- /dev/null	Fri Dec 21 13:07:37 2012
+++ src/usr.bin/ftp/ssl.c	Fri Dec 21 13:07:36 2012
@@ -0,0 +1,604 @@
+/*	$NetBSD: ssl.c,v 1.1 2012/12/21 18:07:36 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 2008, 2010 Joerg Sonnenberger <jo...@netbsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: common.c,v 1.53 2007/12/19 00:26:36 des Exp $
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: ssl.c,v 1.1 2012/12/21 18:07:36 christos Exp $");
+#endif
+
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/param.h>
+#include <sys/select.h>
+#include <sys/uio.h>
+
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#include "ssl.h"
+
+extern int quit_time, verbose, ftp_debug;
+extern FILE *ttyout;
+
+struct fetch_connect {
+	int			 sd;		/* file/socket descriptor */
+	char			*buf;		/* buffer */
+	size_t			 bufsize;	/* buffer size */
+	size_t			 bufpos;	/* position of buffer */
+	size_t			 buflen;	/* length of buffer contents */
+	struct {				/* data cached after an
+						   interrupted read */
+		char	*buf;
+		size_t	 size;
+		size_t	 pos;
+		size_t	 len;
+	} cache;
+	int 			 issock;
+	int			 iserr;
+	int			 iseof;
+	SSL			*ssl;		/* SSL handle */
+};
+
+/*
+ * Write a vector to a connection w/ timeout
+ * Note: can modify the iovec.
+ */
+static ssize_t
+fetch_writev(struct fetch_connect *conn, struct iovec *iov, int iovcnt)
+{
+	struct timeval now, timeout, delta;
+	fd_set writefds;
+	ssize_t len, total;
+	int r;
+
+	if (quit_time > 0) {
+		FD_ZERO(&writefds);
+		gettimeofday(&timeout, NULL);
+		timeout.tv_sec += quit_time;
+	}
+
+	total = 0;
+	while (iovcnt > 0) {
+		while (quit_time > 0 && !FD_ISSET(conn->sd, &writefds)) {
+			FD_SET(conn->sd, &writefds);
+			gettimeofday(&now, NULL);
+			delta.tv_sec = timeout.tv_sec - now.tv_sec;
+			delta.tv_usec = timeout.tv_usec - now.tv_usec;
+			if (delta.tv_usec < 0) {
+				delta.tv_usec += 1000000;
+				delta.tv_sec--;
+			}
+			if (delta.tv_sec < 0) {
+				errno = ETIMEDOUT;
+				return -1;
+			}
+			errno = 0;
+			r = select(conn->sd + 1, NULL, &writefds, NULL, &delta);
+			if (r == -1) {
+				if (errno == EINTR)
+					continue;
+				return -1;
+			}
+		}
+		errno = 0;
+		if (conn->ssl != NULL)
+			len = SSL_write(conn->ssl, iov->iov_base, iov->iov_len);
+		else
+			len = writev(conn->sd, iov, iovcnt);
+		if (len == 0) {
+			/* we consider a short write a failure */
+			/* XXX perhaps we shouldn't in the SSL case */
+			errno = EPIPE;
+			return -1;
+		}
+		if (len < 0) {
+			if (errno == EINTR)
+				continue;
+			return -1;
+		}
+		total += len;
+		while (iovcnt > 0 && len >= (ssize_t)iov->iov_len) {
+			len -= iov->iov_len;
+			iov++;
+			iovcnt--;
+		}
+		if (iovcnt > 0) {
+			iov->iov_len -= len;
+			iov->iov_base = (char *)iov->iov_base + len;
+		}
+	}
+	return total;
+}
+
+/*
+ * Write to a connection w/ timeout
+ */
+static int
+fetch_write(struct fetch_connect *conn, const char *str, size_t len)
+{
+	struct iovec iov[1];
+
+	iov[0].iov_base = (char *)__UNCONST(str);
+	iov[0].iov_len = len;
+	return fetch_writev(conn, iov, 1);
+}
+
+/*
+ * Send a formatted line; optionally echo to terminal
+ */
+int
+fetch_printf(struct fetch_connect *conn, const char *fmt, ...)
+{
+	va_list ap;
+	size_t len;
+	char *msg;
+	int r;
+
+	va_start(ap, fmt);
+	len = vasprintf(&msg, fmt, ap);
+	va_end(ap);
+
+	if (msg == NULL) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	r = fetch_write(conn, msg, len);
+	free(msg);
+	return r;
+}
+
+int
+fetch_fileno(struct fetch_connect *conn)
+{
+
+	return conn->sd;
+}
+
+int
+fetch_error(struct fetch_connect *conn)
+{
+
+	return conn->iserr;
+}
+
+static void
+fetch_clearerr(struct fetch_connect *conn)
+{
+
+	conn->iserr = 0;
+}
+
+int
+fetch_flush(struct fetch_connect *conn)
+{
+	int v;
+
+	if (conn->issock) {
+#ifdef TCP_NOPUSH
+		v = 0;
+		setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &v, sizeof(v));
+#endif
+		v = 1;
+		setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v));
+	}
+	return 0;
+}
+
+/*ARGSUSED*/
+struct fetch_connect *
+fetch_open(const char *fname, const char *fmode)
+{
+	struct fetch_connect *conn;
+	int fd;
+
+	fd = open(fname, O_RDONLY); /* XXX: fmode */
+	if (fd < 0)
+		return NULL;
+
+	if ((conn = calloc(1, sizeof(*conn))) == NULL) {
+		close(fd);
+		return NULL;
+	}
+
+	conn->sd = fd;
+	conn->issock = 0;
+	return conn;
+}
+
+/*ARGSUSED*/
+struct fetch_connect *
+fetch_fdopen(int sd, const char *fmode)
+{
+	struct fetch_connect *conn;
+	int opt = 1;
+
+	if ((conn = calloc(1, sizeof(*conn))) == NULL)
+		return NULL;
+
+	conn->sd = sd;
+	conn->issock = 1;
+	fcntl(sd, F_SETFD, FD_CLOEXEC);
+	setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
+#ifdef TCP_NOPUSH
+	setsockopt(sd, IPPROTO_TCP, TCP_NOPUSH, &opt, sizeof(opt));
+#endif
+	return conn;
+}
+
+int
+fetch_close(struct fetch_connect *conn)
+{
+	int rv = 0;
+
+	if (conn != NULL) {
+		fetch_flush(conn);
+		SSL_free(conn->ssl);
+		rv = close(conn->sd);
+		if (rv < 0) {
+			errno = rv;
+			rv = EOF;
+		}
+		free(conn->cache.buf);
+		free(conn->buf);
+		free(conn);
+	}
+	return rv;
+}
+
+#define FETCH_READ_WAIT		-2
+#define FETCH_READ_ERROR	-1
+
+static ssize_t
+fetch_ssl_read(SSL *ssl, void *buf, size_t len)
+{
+	ssize_t rlen;
+	int ssl_err;
+
+	rlen = SSL_read(ssl, buf, len);
+	if (rlen < 0) {
+		ssl_err = SSL_get_error(ssl, rlen);
+		if (ssl_err == SSL_ERROR_WANT_READ ||
+		    ssl_err == SSL_ERROR_WANT_WRITE) {
+			return FETCH_READ_WAIT;
+		}
+		ERR_print_errors_fp(ttyout);
+		return FETCH_READ_ERROR;
+	}
+	return rlen;
+}
+
+static ssize_t
+fetch_nonssl_read(int sd, void *buf, size_t len)
+{
+	ssize_t rlen;
+
+	rlen = read(sd, buf, len);
+	if (rlen < 0) {
+		if (errno == EAGAIN || errno == EINTR)
+			return FETCH_READ_WAIT;
+		return FETCH_READ_ERROR;
+	}
+	return rlen;
+}
+
+/*
+ * Cache some data that was read from a socket but cannot be immediately
+ * returned because of an interrupted system call.
+ */
+static int
+fetch_cache_data(struct fetch_connect *conn, char *src, size_t nbytes)
+{
+
+	if (conn->cache.size < nbytes) {
+		char *tmp = realloc(conn->cache.buf, nbytes);
+		if (tmp == NULL)
+			return -1;
+
+		conn->cache.buf = tmp;
+		conn->cache.size = nbytes;
+	}
+
+	memcpy(conn->cache.buf, src, nbytes);
+	conn->cache.len = nbytes;
+	conn->cache.pos = 0;
+	return 0;
+}
+
+ssize_t
+fetch_read(void *ptr, size_t size, size_t nmemb, struct fetch_connect *conn)
+{
+	struct timeval now, timeout, delta;
+	fd_set readfds;
+	ssize_t rlen, total;
+	size_t len;
+	char *start, *buf;
+
+	if (quit_time > 0) {
+		gettimeofday(&timeout, NULL);
+		timeout.tv_sec += quit_time;
+	}
+
+	total = 0;
+	start = buf = ptr;
+	len = size * nmemb;
+
+	if (conn->cache.len > 0) {
+		/*
+		 * The last invocation of fetch_read was interrupted by a
+		 * signal after some data had been read from the socket. Copy
+		 * the cached data into the supplied buffer before trying to
+		 * read from the socket again.
+		 */
+		total = (conn->cache.len < len) ? conn->cache.len : len;
+		memcpy(buf, conn->cache.buf, total);
+
+		conn->cache.len -= total;
+		conn->cache.pos += total;
+		len -= total;
+		buf += total;
+	}
+
+	while (len > 0) {
+		/*
+		 * The socket is non-blocking.  Instead of the canonical
+		 * select() -> read(), we do the following:
+		 *
+		 * 1) call read() or SSL_read().
+		 * 2) if an error occurred, return -1.
+		 * 3) if we received data but we still expect more,
+		 *    update our counters and loop.
+		 * 4) if read() or SSL_read() signaled EOF, return.
+		 * 5) if we did not receive any data but we're not at EOF,
+		 *    call select().
+		 *
+		 * In the SSL case, this is necessary because if we
+		 * receive a close notification, we have to call
+		 * SSL_read() one additional time after we've read
+		 * everything we received.
+		 *
+		 * In the non-SSL case, it may improve performance (very
+		 * slightly) when reading small amounts of data.
+		 */
+		if (conn->ssl != NULL)
+			rlen = fetch_ssl_read(conn->ssl, buf, len);
+		else
+			rlen = fetch_nonssl_read(conn->sd, buf, len);
+		if (rlen == 0) {
+			break;
+		} else if (rlen > 0) {
+			len -= rlen;
+			buf += rlen;
+			total += rlen;
+			continue;
+		} else if (rlen == FETCH_READ_ERROR) {
+			if (errno == EINTR)
+				fetch_cache_data(conn, start, total);
+			return -1;
+		}
+		FD_ZERO(&readfds);
+		while (!FD_ISSET(conn->sd, &readfds)) {
+			FD_SET(conn->sd, &readfds);
+			if (quit_time > 0) {
+				gettimeofday(&now, NULL);
+				if (!timercmp(&timeout, &now, >)) {
+					errno = ETIMEDOUT;
+					return -1;
+				}
+				timersub(&timeout, &now, &delta);
+			}
+			errno = 0;
+			if (select(conn->sd + 1, &readfds, NULL, NULL,
+				quit_time > 0 ? &delta : NULL) < 0) {
+				if (errno == EINTR)
+					continue;
+				return -1;
+			}
+		}
+	}
+	return total;
+}
+
+#define MIN_BUF_SIZE 1024
+
+/*
+ * Read a line of text from a connection w/ timeout
+ */
+char *
+fetch_getln(char *str, int size, struct fetch_connect *conn)
+{
+	size_t tmpsize;
+	ssize_t len;
+	char c;
+
+	if (conn->buf == NULL) {
+		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
+			errno = ENOMEM;
+			conn->iserr = 1;
+			return NULL;
+		}
+		conn->bufsize = MIN_BUF_SIZE;
+	}
+
+	if (conn->iserr || conn->iseof)
+		return NULL;
+
+	if (conn->buflen - conn->bufpos > 0)
+		goto done;
+
+	conn->buf[0] = '\0';
+	conn->bufpos = 0;
+	conn->buflen = 0;
+	do {
+		len = fetch_read(&c, sizeof(c), 1, conn);
+		if (len == -1) {
+			conn->iserr = 1;
+			return NULL;
+		}
+		if (len == 0) {
+			conn->iseof = 1;
+			break;
+		}
+		conn->buf[conn->buflen++] = c;
+		if (conn->buflen == conn->bufsize) {
+			char *tmp = conn->buf;
+			tmpsize = conn->bufsize * 2 + 1;
+			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
+				errno = ENOMEM;
+				conn->iserr = 1;
+				return NULL;
+			}
+			conn->buf = tmp;
+			conn->bufsize = tmpsize;
+		}
+	} while (c != '\n');
+
+	if (conn->buflen == 0)
+		return NULL;
+ done:
+	tmpsize = MIN(size - 1, (int)(conn->buflen - conn->bufpos));
+	memcpy(str, conn->buf + conn->bufpos, tmpsize);
+	str[tmpsize] = '\0';
+	conn->bufpos += tmpsize;
+	return str;
+}
+
+int
+fetch_getline(struct fetch_connect *conn, char *buf, size_t buflen,
+    const char **errormsg)
+{
+	size_t len;
+	int rv;
+
+	if (fetch_getln(buf, buflen, conn) == NULL) {
+		if (conn->iseof) {	/* EOF */
+			rv = -2;
+			if (errormsg)
+				*errormsg = "\nEOF received";
+		} else {		/* error */
+			rv = -1;
+			if (errormsg)
+				*errormsg = "Error encountered";
+		}
+		fetch_clearerr(conn);
+		return rv;
+	}
+	len = strlen(buf);
+	if (buf[len - 1] == '\n') {	/* clear any trailing newline */
+		buf[--len] = '\0';
+	} else if (len == buflen - 1) {	/* line too long */
+		while (1) {
+			char c;
+			ssize_t rlen = fetch_read(&c, sizeof(c), 1, conn);
+			if (rlen <= 0 || c == '\n')
+				break;
+		}
+		if (errormsg)
+			*errormsg = "Input line is too long";
+		fetch_clearerr(conn);
+		return -3;
+	}
+	if (errormsg)
+		*errormsg = NULL;
+	return len;
+}
+
+void *
+fetch_start_ssl(int sock)
+{
+	SSL *ssl;
+	SSL_CTX *ctx;
+	int ret, ssl_err;
+
+	/* Init the SSL library and context */
+	if (!SSL_library_init()){
+		fprintf(ttyout, "SSL library init failed\n");
+		return NULL;
+	}
+
+	SSL_load_error_strings();
+
+	ctx = SSL_CTX_new(SSLv23_client_method());
+	SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
+
+	ssl = SSL_new(ctx);
+	if (ssl == NULL){
+		fprintf(ttyout, "SSL context creation failed\n");
+		SSL_CTX_free(ctx);
+		return NULL;
+	}
+	SSL_set_fd(ssl, sock);
+	while ((ret = SSL_connect(ssl)) == -1) {
+		ssl_err = SSL_get_error(ssl, ret);
+		if (ssl_err != SSL_ERROR_WANT_READ &&
+		    ssl_err != SSL_ERROR_WANT_WRITE) {
+			ERR_print_errors_fp(ttyout);
+			SSL_free(ssl);
+			return NULL;
+		}
+	}
+
+	if (ftp_debug && verbose) {
+		X509 *cert;
+		X509_NAME *name;
+		char *str;
+
+		fprintf(ttyout, "SSL connection established using %s\n",
+		    SSL_get_cipher(ssl));
+		cert = SSL_get_peer_certificate(ssl);
+		name = X509_get_subject_name(cert);
+		str = X509_NAME_oneline(name, 0, 0);
+		fprintf(ttyout, "Certificate subject: %s\n", str);
+		free(str);
+		name = X509_get_issuer_name(cert);
+		str = X509_NAME_oneline(name, 0, 0);
+		fprintf(ttyout, "Certificate issuer: %s\n", str);
+		free(str);
+	}
+
+	return ssl;
+}
+
+
+void
+fetch_set_ssl(struct fetch_connect *conn, void *ssl)
+{
+	conn->ssl = ssl;
+}
Index: src/usr.bin/ftp/ssl.h
diff -u /dev/null src/usr.bin/ftp/ssl.h:1.1
--- /dev/null	Fri Dec 21 13:07:37 2012
+++ src/usr.bin/ftp/ssl.h	Fri Dec 21 13:07:36 2012
@@ -0,0 +1,62 @@
+/*	$NetBSD: ssl.h,v 1.1 2012/12/21 18:07:36 christos Exp $	*/
+
+/*-
+ * Copyright (c) 2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifdef WITH_SSL
+
+#define FETCH struct fetch_connect
+struct fetch_connect;
+
+int fetch_printf(struct fetch_connect *, const char *fmt, ...);
+int fetch_fileno(struct fetch_connect *);
+int fetch_error(struct fetch_connect *);
+int fetch_flush(struct fetch_connect *);
+struct fetch_connect *fetch_open(const char *, const char *);
+struct fetch_connect *fetch_fdopen(int, const char *);
+int fetch_close(struct fetch_connect *);
+ssize_t fetch_read(void *, size_t, size_t, struct fetch_connect *);
+char *fetch_getln(char *, int, struct fetch_connect *);
+int fetch_getline(struct fetch_connect *, char *, size_t, const char **);
+void fetch_set_ssl(struct fetch_connect *, void *);
+void *fetch_start_ssl(int);
+
+#else	/* !WITH_SSL */
+
+#define FETCH FILE
+
+#define	fetch_printf	fprintf
+#define	fetch_fileno	fileno
+#define	fetch_error	ferror
+#define	fetch_flush	fflush
+#define	fetch_open	fopen
+#define	fetch_fdopen	fdopen
+#define	fetch_close	fclose
+#define	fetch_read	fread
+#define	fetch_getln	fgets
+#define	fetch_getline	get_line
+#define	fetch_set_ssl(a, b)
+
+#endif	/* !WITH_SSL */

Reply via email to