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 <[email protected]>
+ * 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 */