Module Name: src Committed By: bouyer Date: Tue Dec 17 21:07:59 UTC 2013
Modified Files: src/usr.bin/ftp [netbsd-6]: Makefile cmds.c cmdtab.c extern.h fetch.c ftp.1 ftp.c ftp_var.h main.c progressbar.c util.c version.h Added Files: src/usr.bin/ftp [netbsd-6]: ssl.c ssl.h Log Message: Apply patch, requested by tron in ticket #997: usr.bin/ftp/Makefile patch usr.bin/ftp/cmds.c patch usr.bin/ftp/cmdtab.c patch usr.bin/ftp/extern.h patch usr.bin/ftp/fetch.c patch usr.bin/ftp/ftp.1 patch usr.bin/ftp/ftp.c patch usr.bin/ftp/ftp_var.h patch usr.bin/ftp/main.c patch usr.bin/ftp/progressbar.c patch usr.bin/ftp/ssl.c patch usr.bin/ftp/ssl.h patch usr.bin/ftp/util.c patch usr.bin/ftp/version.h patch Add HTTPS support to ftp(1). To generate a diff of this commit: cvs rdiff -u -r1.35 -r1.35.4.1 src/usr.bin/ftp/Makefile cvs rdiff -u -r1.134 -r1.134.2.1 src/usr.bin/ftp/cmds.c cvs rdiff -u -r1.51 -r1.51.8.1 src/usr.bin/ftp/cmdtab.c cvs rdiff -u -r1.79 -r1.79.4.1 src/usr.bin/ftp/extern.h cvs rdiff -u -r1.195 -r1.195.2.1 src/usr.bin/ftp/fetch.c cvs rdiff -u -r1.131 -r1.131.8.1 src/usr.bin/ftp/ftp.1 cvs rdiff -u -r1.163 -r1.163.2.1 src/usr.bin/ftp/ftp.c cvs rdiff -u -r1.81 -r1.81.8.1 src/usr.bin/ftp/ftp_var.h cvs rdiff -u -r1.120 -r1.120.2.1 src/usr.bin/ftp/main.c cvs rdiff -u -r1.21 -r1.21.8.1 src/usr.bin/ftp/progressbar.c cvs rdiff -u -r0 -r1.2.10.2 src/usr.bin/ftp/ssl.c cvs rdiff -u -r0 -r1.1.10.2 src/usr.bin/ftp/ssl.h cvs rdiff -u -r1.156 -r1.156.2.1 src/usr.bin/ftp/util.c cvs rdiff -u -r1.82 -r1.82.8.1 src/usr.bin/ftp/version.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.35.4.1 --- src/usr.bin/ftp/Makefile:1.35 Sun Aug 14 12:58:15 2011 +++ src/usr.bin/ftp/Makefile Tue Dec 17 21:07:59 2013 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.35 2011/08/14 12:58:15 christos Exp $ +# $NetBSD: Makefile,v 1.35.4.1 2013/12/17 21:07:59 bouyer 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/cmds.c diff -u src/usr.bin/ftp/cmds.c:1.134 src/usr.bin/ftp/cmds.c:1.134.2.1 --- src/usr.bin/ftp/cmds.c:1.134 Sun Jan 15 20:43:24 2012 +++ src/usr.bin/ftp/cmds.c Tue Dec 17 21:07:59 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: cmds.c,v 1.134 2012/01/15 20:43:24 christos Exp $ */ +/* $NetBSD: cmds.c,v 1.134.2.1 2013/12/17 21:07:59 bouyer Exp $ */ /*- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. @@ -96,7 +96,7 @@ #if 0 static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94"; #else -__RCSID("$NetBSD: cmds.c,v 1.134 2012/01/15 20:43:24 christos Exp $"); +__RCSID("$NetBSD: cmds.c,v 1.134.2.1 2013/12/17 21:07:59 bouyer Exp $"); #endif #endif /* not lint */ @@ -2675,7 +2675,7 @@ setoption(int argc, char *argv[]) return; } -#define OPTIONINDENT ((int) sizeof("http_proxy")) +#define OPTIONINDENT ((int) sizeof("https_proxy")) if (argc == 1) { for (o = optiontab; o->name != NULL; o++) { fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT, Index: src/usr.bin/ftp/cmdtab.c diff -u src/usr.bin/ftp/cmdtab.c:1.51 src/usr.bin/ftp/cmdtab.c:1.51.8.1 --- src/usr.bin/ftp/cmdtab.c:1.51 Sun Apr 12 10:18:52 2009 +++ src/usr.bin/ftp/cmdtab.c Tue Dec 17 21:07:59 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: cmdtab.c,v 1.51 2009/04/12 10:18:52 lukem Exp $ */ +/* $NetBSD: cmdtab.c,v 1.51.8.1 2013/12/17 21:07:59 bouyer Exp $ */ /*- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. @@ -63,7 +63,7 @@ #if 0 static char sccsid[] = "@(#)cmdtab.c 8.4 (Berkeley) 10/9/94"; #else -__RCSID("$NetBSD: cmdtab.c,v 1.51 2009/04/12 10:18:52 lukem Exp $"); +__RCSID("$NetBSD: cmdtab.c,v 1.51.8.1 2013/12/17 21:07:59 bouyer Exp $"); #endif #endif /* not lint */ @@ -298,6 +298,7 @@ struct option optiontab[] = { { "anonpass", NULL }, { "ftp_proxy", NULL }, { "http_proxy", NULL }, + { "https_proxy",NULL }, { "no_proxy", NULL }, { "pager", NULL }, { "prompt", NULL }, Index: src/usr.bin/ftp/extern.h diff -u src/usr.bin/ftp/extern.h:1.79 src/usr.bin/ftp/extern.h:1.79.4.1 --- src/usr.bin/ftp/extern.h:1.79 Fri Sep 16 15:39:26 2011 +++ src/usr.bin/ftp/extern.h Tue Dec 17 21:07:59 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: extern.h,v 1.79 2011/09/16 15:39:26 joerg Exp $ */ +/* $NetBSD: extern.h,v 1.79.4.1 2013/12/17 21:07:59 bouyer Exp $ */ /*- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. @@ -239,7 +239,7 @@ void unsetoption(int, char **); void updatelocalcwd(void); void updateremotecwd(void); void user(int, char **); -int ftp_connect(int, const struct sockaddr *, socklen_t); +int ftp_connect(int, const struct sockaddr *, socklen_t, int); int ftp_listen(int, int); int ftp_poll(struct pollfd *, int, int); void *ftp_malloc(size_t); Index: src/usr.bin/ftp/fetch.c diff -u src/usr.bin/ftp/fetch.c:1.195 src/usr.bin/ftp/fetch.c:1.195.2.1 --- src/usr.bin/ftp/fetch.c:1.195 Sat Dec 10 05:53:58 2011 +++ src/usr.bin/ftp/fetch.c Tue Dec 17 21:07:59 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: fetch.c,v 1.195 2011/12/10 05:53:58 lukem Exp $ */ +/* $NetBSD: fetch.c,v 1.195.2.1 2013/12/17 21:07:59 bouyer 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.195 2011/12/10 05:53:58 lukem Exp $"); +__RCSID("$NetBSD: fetch.c,v 1.195.2.1 2013/12/17 21:07:59 bouyer Exp $"); #endif /* not lint */ /* @@ -64,18 +64,23 @@ __RCSID("$NetBSD: fetch.c,v 1.195 2011/1 #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 } url_t; __dead static void aborthttp(int); +__dead static void timeouthttp(int); #ifndef NO_AUTH static int auth_url(const char *, char **, const char *, const char *); static void base64_encode(const unsigned char *, size_t, unsigned char *); @@ -100,7 +105,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 +359,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: @@ -463,7 +483,7 @@ sigjmp_buf httpabort; /* * Retrieve URL, via a proxy if necessary, using HTTP. * If proxyenv is set, use that for the proxy, otherwise try ftp_proxy or - * http_proxy as appropriate. + * http_proxy/https_proxy as appropriate. * Supports HTTP redirects. * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection * is still open (e.g, ftp xfer with trailing /) @@ -473,8 +493,10 @@ fetch_url(const char *url, const char *p { struct addrinfo hints, *res, *res0 = NULL; int error; - sigfunc volatile oldintr; - sigfunc volatile oldintp; + sigfunc volatile oldint; + sigfunc volatile oldpipe; + sigfunc volatile oldalrm; + sigfunc volatile oldquit; int volatile s; struct stat sb; int volatile ischunked; @@ -498,17 +520,22 @@ 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; + const char *volatile penv = proxyenv; 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)); + DPRINTF("%s: `%s' proxyenv `%s'\n", __func__, url, STRorNULL(penv)); - oldintr = oldintp = NULL; + oldquit = oldalrm = oldint = oldpipe = NULL; closefunc = NULL; - fin = fout = NULL; + fin = NULL; + fout = NULL; s = -1; savefile = NULL; auth = location = message = NULL; @@ -516,6 +543,9 @@ fetch_url(const char *url, const char *p rval = 1; uuser = pass = host = path = decodedpath = puser = ppass = NULL; + if (sigsetjmp(httpabort, 1)) + goto cleanup_fetch_url; + if (parse_url(url, "URL", &urltype, &uuser, &pass, &host, &port, &portnum, &path) == -1) goto cleanup_fetch_url; @@ -531,7 +561,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; } @@ -549,7 +579,7 @@ fetch_url(const char *url, const char *p else savefile = ftp_strdup(decodedpath); } - DPRINTF("fetch_url: savefile `%s'\n", savefile); + DPRINTF("%s: savefile `%s'\n", __func__, savefile); if (EMPTYSTRING(savefile)) { if (urltype == FTP_URL_T) { rval = fetch_ftp(url); @@ -571,17 +601,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,18 +624,25 @@ 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) - proxyenv = getoptionvalue("http_proxy"); + if (penv == NULL) { +#ifdef WITH_SSL + if (urltype == HTTPS_URL_T) + penv = getoptionvalue("https_proxy"); +#endif + if (penv == NULL && IS_HTTP_TYPE(urltype)) + penv = getoptionvalue("http_proxy"); else if (urltype == FTP_URL_T) - proxyenv = getoptionvalue("ftp_proxy"); + penv = getoptionvalue("ftp_proxy"); } direction = "retrieved"; - if (! EMPTYSTRING(proxyenv)) { /* use proxy */ + if (! EMPTYSTRING(penv)) { /* use proxy */ url_t purltype; char *phost, *ppath; char *pport, *no_proxy; @@ -652,21 +689,20 @@ fetch_url(const char *url, const char *p if (isproxy) { if (restart_point) { warnx("Can't restart via proxy URL `%s'", - proxyenv); + penv); goto cleanup_fetch_url; } - if (parse_url(proxyenv, "proxy URL", &purltype, + if (parse_url(penv, "proxy URL", &purltype, &puser, &ppass, &phost, &pport, &pportnum, &ppath) == -1) goto cleanup_fetch_url; - if ((purltype != HTTP_URL_T + if ((!IS_HTTP_TYPE(purltype) && purltype != FTP_URL_T) || EMPTYSTRING(phost) || (! EMPTYSTRING(ppath) && strcmp(ppath, "/") != 0)) { - warnx("Malformed proxy URL `%s'", - proxyenv); + warnx("Malformed proxy URL `%s'", penv); FREEPTR(phost); FREEPTR(pport); FREEPTR(ppath); @@ -690,8 +726,9 @@ fetch_url(const char *url, const char *p FREEPTR(path); path = ftp_strdup(url); FREEPTR(ppath); + urltype = purltype; } - } /* ! EMPTYSTRING(proxyenv) */ + } /* ! EMPTYSTRING(penv) */ memset(&hints, 0, sizeof(hints)); hints.ai_flags = 0; @@ -700,7 +737,7 @@ fetch_url(const char *url, const char *p hints.ai_protocol = 0; error = getaddrinfo(host, port, &hints, &res0); if (error) { - warnx("Can't lookup `%s:%s': %s", host, port, + warnx("Can't LOOKUP `%s:%s': %s", host, port, (error == EAI_SYSTEM) ? strerror(errno) : gai_strerror(error)); goto cleanup_fetch_url; @@ -709,6 +746,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]; @@ -734,12 +774,23 @@ fetch_url(const char *url, const char *p continue; } - if (ftp_connect(s, res->ai_addr, res->ai_addrlen) < 0) { + if (ftp_connect(s, res->ai_addr, res->ai_addrlen, + verbose || !res->ai_next) < 0) { close(s); s = -1; continue; } +#ifdef WITH_SSL + if (urltype == HTTPS_URL_T) { + if ((ssl = fetch_start_ssl(s)) == NULL) { + close(s); + s = -1; + continue; + } + } +#endif + /* success */ break; } @@ -749,7 +800,13 @@ fetch_url(const char *url, const char *p goto cleanup_fetch_url; } - fin = fdopen(s, "r+"); + oldalrm = xsignal(SIGALRM, timeouthttp); + alarmtimer(quit_time ? quit_time : 60); + fin = fetch_fdopen(s, "r+"); + fetch_set_ssl(fin, ssl); + alarmtimer(0); + + alarmtimer(quit_time ? quit_time : 60); /* * Construct and send the request. */ @@ -764,11 +821,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; @@ -781,18 +838,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", + fetch_printf(fin, "Range: bytes=" LLF "-\r\n", (LLT)restart_point); fprintf(ttyout, "restarting at " LLF, (LLT)restart_point); @@ -800,12 +862,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) { @@ -815,7 +877,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) { @@ -824,18 +886,22 @@ 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"); + alarmtimer(0); goto cleanup_fetch_url; } + alarmtimer(0); /* Read the response */ - len = get_line(fin, buf, sizeof(buf), &errormsg); + alarmtimer(quit_time ? quit_time : 60); + len = fetch_getline(fin, buf, sizeof(buf), &errormsg); + alarmtimer(0); if (len < 0) { if (*errormsg == '\n') errormsg++; @@ -844,7 +910,7 @@ fetch_url(const char *url, const char *p } while (len > 0 && (ISLWS(buf[len-1]))) buf[--len] = '\0'; - DPRINTF("fetch_url: received `%s'\n", buf); + DPRINTF("%s: received `%s'\n", __func__, buf); /* Determine HTTP response code */ cp = strchr(buf, ' '); @@ -859,7 +925,9 @@ fetch_url(const char *url, const char *p /* Read the rest of the header. */ while (1) { - len = get_line(fin, buf, sizeof(buf), &errormsg); + alarmtimer(quit_time ? quit_time : 60); + len = fetch_getline(fin, buf, sizeof(buf), &errormsg); + alarmtimer(0); if (len < 0) { if (*errormsg == '\n') errormsg++; @@ -870,7 +938,7 @@ fetch_url(const char *url, const char *p buf[--len] = '\0'; if (len == 0) break; - DPRINTF("fetch_url: received `%s'\n", buf); + DPRINTF("%s: received `%s'\n", __func__, buf); /* * Look for some headers @@ -882,8 +950,8 @@ fetch_url(const char *url, const char *p filesize = STRTOLL(cp, &ep, 10); if (filesize < 0 || *ep != '\0') goto improper; - DPRINTF("fetch_url: parsed len as: " LLF "\n", - (LLT)filesize); + DPRINTF("%s: parsed len as: " LLF "\n", + __func__, (LLT)filesize); } else if (match_token(&cp, "Content-Range:")) { if (! match_token(&cp, "bytes")) @@ -954,8 +1022,8 @@ fetch_url(const char *url, const char *p } else if (match_token(&cp, "Location:")) { location = ftp_strdup(cp); - DPRINTF("fetch_url: parsed location as `%s'\n", - cp); + DPRINTF("%s: parsed location as `%s'\n", + __func__, cp); } else if (match_token(&cp, "Transfer-Encoding:")) { if (match_token(&cp, "binary")) { @@ -970,19 +1038,20 @@ fetch_url(const char *url, const char *p goto cleanup_fetch_url; } ischunked++; - DPRINTF("fetch_url: using chunked encoding\n"); + DPRINTF("%s: using chunked encoding\n", + __func__); } else if (match_token(&cp, "Proxy-Authenticate:") || match_token(&cp, "WWW-Authenticate:")) { if (! (token = match_token(&cp, "Basic"))) { - DPRINTF( - "fetch_url: skipping unknown auth scheme `%s'\n", - token); + DPRINTF("%s: skipping unknown auth " + "scheme `%s'\n", __func__, token); continue; } FREEPTR(auth); auth = ftp_strdup(token); - DPRINTF("fetch_url: parsed auth as `%s'\n", cp); + DPRINTF("%s: parsed auth as `%s'\n", + __func__, cp); } } @@ -1064,7 +1133,7 @@ fetch_url(const char *url, const char *p apass = NULL; } if (auth_url(auth, authp, auser, apass) == 0) { - rval = fetch_url(url, proxyenv, + rval = fetch_url(url, penv, proxyauth, wwwauth); memset(*authp, 0, strlen(*authp)); FREEPTR(*authp); @@ -1085,7 +1154,7 @@ fetch_url(const char *url, const char *p if (strcmp(savefile, "-") == 0) { fout = stdout; } else if (*savefile == '|') { - oldintp = xsignal(SIGPIPE, SIG_IGN); + oldpipe = xsignal(SIGPIPE, SIG_IGN); fout = popen(savefile + 1, "w"); if (fout == NULL) { warn("Can't execute `%s'", savefile + 1); @@ -1121,10 +1190,8 @@ fetch_url(const char *url, const char *p } /* Trap signals */ - if (sigsetjmp(httpabort, 1)) - goto cleanup_fetch_url; - (void)xsignal(SIGQUIT, psummary); - oldintr = xsignal(SIGINT, aborthttp); + oldquit = xsignal(SIGQUIT, psummary); + oldint = xsignal(SIGINT, aborthttp); assert(rcvbuf_size > 0); if ((size_t)rcvbuf_size > bufsize) { @@ -1136,6 +1203,10 @@ fetch_url(const char *url, const char *p bytes = 0; hashbytes = mark; + if (oldalrm) { + (void)xsignal(SIGALRM, oldalrm); + oldalrm = NULL; + } progressmeter(-1); /* Finally, suck down the file. */ @@ -1147,7 +1218,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; } @@ -1182,7 +1253,7 @@ fetch_url(const char *url, const char *p warnx("Unexpected data following chunk-size"); goto cleanup_fetch_url; } - DPRINTF("fetch_url: got chunk-size of " LLF "\n", + DPRINTF("%s: got chunk-size of " LLF "\n", __func__, (LLT)chunksize); if (chunksize == 0) { lastchunk = 1; @@ -1192,7 +1263,7 @@ fetch_url(const char *url, const char *p /* transfer file or chunk */ while (1) { struct timeval then, now, td; - off_t bufrem; + volatile off_t bufrem; if (rate_get) (void)gettimeofday(&then, NULL); @@ -1200,7 +1271,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; @@ -1239,7 +1310,8 @@ 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) { + alarmtimer(0); warnx("Unexpected EOF reading chunk CRLF"); goto cleanup_fetch_url; } @@ -1259,7 +1331,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; } @@ -1291,12 +1363,16 @@ fetch_url(const char *url, const char *p warnx("Improper response from `%s:%s'", host, port); cleanup_fetch_url: - if (oldintr) - (void)xsignal(SIGINT, oldintr); - if (oldintp) - (void)xsignal(SIGPIPE, oldintp); + if (oldint) + (void)xsignal(SIGINT, oldint); + if (oldpipe) + (void)xsignal(SIGPIPE, oldpipe); + if (oldalrm) + (void)xsignal(SIGALRM, oldalrm); + if (oldquit) + (void)xsignal(SIGQUIT, oldpipe); if (fin != NULL) - fclose(fin); + fetch_close(fin); else if (s != -1) close(s); if (closefunc != NULL && fout != NULL) @@ -1329,12 +1405,32 @@ static void aborthttp(int notused) { char msgbuf[100]; - size_t len; + int len; sigint_raised = 1; alarmtimer(0); - len = strlcpy(msgbuf, "\nHTTP fetch aborted.\n", sizeof(msgbuf)); - write(fileno(ttyout), msgbuf, len); + if (fromatty) { + len = snprintf(msgbuf, sizeof(msgbuf), + "\n%s: HTTP fetch aborted.\n", getprogname()); + if (len > 0) + write(fileno(ttyout), msgbuf, len); + } + siglongjmp(httpabort, 1); +} + +static void +timeouthttp(int notused) +{ + char msgbuf[100]; + int len; + + alarmtimer(0); + if (fromatty) { + len = snprintf(msgbuf, sizeof(msgbuf), + "\n%s: HTTP fetch timeout.\n", getprogname()); + if (len > 0) + write(fileno(ttyout), msgbuf, len); + } siglongjmp(httpabort, 1); } @@ -1687,6 +1783,7 @@ static int go_fetch(const char *url) { char *proxyenv; + char *p; #ifndef NO_ABOUT /* @@ -1727,10 +1824,26 @@ 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)); /* + * If it contains "://" but does not begin with ftp:// + * or something that was already handled, then it's + * unsupported. + * + * If it contains ":" but not "://" then we assume the + * part before the colon is a host name, not an URL scheme, + * so we don't try to match that here. + */ + if ((p = strstr(url, "://")) != NULL && ! STRNEQUAL(url, FTP_URL)) + errx(1, "Unsupported URL scheme `%.*s'", (int)(p - url), url); + + /* * Try FTP URL-style and host:file arguments next. * If ftpproxy is set with an FTP URL, use fetch_url() * Othewise, use fetch_ftp(). Index: src/usr.bin/ftp/ftp.1 diff -u src/usr.bin/ftp/ftp.1:1.131 src/usr.bin/ftp/ftp.1:1.131.8.1 --- src/usr.bin/ftp/ftp.1:1.131 Fri Mar 5 07:41:10 2010 +++ src/usr.bin/ftp/ftp.1 Tue Dec 17 21:07:59 2013 @@ -1,4 +1,4 @@ -.\" $NetBSD: ftp.1,v 1.131 2010/03/05 07:41:10 lukem Exp $ +.\" $NetBSD: ftp.1,v 1.131.8.1 2013/12/17 21:07:59 bouyer Exp $ .\" .\" Copyright (c) 1996-2010 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -57,7 +57,7 @@ .\" .\" @(#)ftp.1 8.3 (Berkeley) 10/9/94 .\" -.Dd March 5, 2010 +.Dd December 22, 2012 .Dt FTP 1 .Os .Sh NAME @@ -66,21 +66,11 @@ .Sh SYNOPSIS .Nm .Op Fl 46AadefginpRtVv -.Bk -words .Op Fl N Ar netrc -.Ek -.Bk -words .Op Fl o Ar output -.Ek -.Bk -words .Op Fl P Ar port -.Ek -.Bk -words .Op Fl q Ar quittime -.Ek -.Bk -words .Op Fl r Ar retry -.Ek .Op Fl s Ar srcaddr .Bk -words .\" [-T dir,max[,inc]] @@ -1335,7 +1325,7 @@ and .Ar value are not given, display all of the options and their values. The currently supported options are: -.Bl -tag -width "http_proxy" -offset indent +.Bl -tag -width "https_proxy" -offset indent .It Cm anonpass Defaults to .Ev $FTPANONPASS @@ -1345,6 +1335,9 @@ Defaults to .It Cm http_proxy Defaults to .Ev $http_proxy . +.It Cm https_proxy +Defaults to +.Ev $https_proxy . .It Cm no_proxy Defaults to .Ev $no_proxy . @@ -1752,6 +1745,29 @@ and (and optionally .Sq password ) is in the URL, use them for the first attempt to authenticate. +.\" https://[user[:password]@]host[:port]/path +.It Li https:// Ns Oo Ar user Ns Oo Li \&: Ns Ar password Oc Ns Li \&@ Oc \ +Ns Ar host Ns Oo Li \&: Ns Ar port Oc Ns Li / Ns Ar path +An +.Tn HTTPS +URL, retrieved using the +.Tn HTTPS +protocol. +If +.Ic "set https_proxy" +is defined, it is used as a URL to an +.Tn HTTPS +proxy server. +If +.Tn HTTPS +authorization is required to retrieve +.Ar path , +and +.Sq user +(and optionally +.Sq password ) +is in the URL, use them for the first attempt to authenticate. +There is currently no certificate validation and verification. .\" file:///path .It Li file:/// Ns Ar path A local URL, copied from @@ -1901,7 +1917,7 @@ Failing the above checks, if .Dq globbing is enabled, local file names are expanded according to the rules used in the -.Xr csh 1 ; +.Xr csh 1 ; see the .Ic glob command. Index: src/usr.bin/ftp/ftp.c diff -u src/usr.bin/ftp/ftp.c:1.163 src/usr.bin/ftp/ftp.c:1.163.2.1 --- src/usr.bin/ftp/ftp.c:1.163 Sat Dec 10 05:53:58 2011 +++ src/usr.bin/ftp/ftp.c Tue Dec 17 21:07:59 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: ftp.c,v 1.163 2011/12/10 05:53:58 lukem Exp $ */ +/* $NetBSD: ftp.c,v 1.163.2.1 2013/12/17 21:07:59 bouyer Exp $ */ /*- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. @@ -92,7 +92,7 @@ #if 0 static char sccsid[] = "@(#)ftp.c 8.6 (Berkeley) 10/27/94"; #else -__RCSID("$NetBSD: ftp.c,v 1.163 2011/12/10 05:53:58 lukem Exp $"); +__RCSID("$NetBSD: ftp.c,v 1.163.2.1 2013/12/17 21:07:59 bouyer Exp $"); #endif #endif /* not lint */ @@ -208,7 +208,8 @@ hookup(const char *host, const char *por hname, sname); continue; } - if (ftp_connect(s, res->ai_addr, res->ai_addrlen) < 0) { + if (ftp_connect(s, res->ai_addr, res->ai_addrlen, + verbose || !res->ai_next) < 0) { close(s); s = -1; continue; @@ -1468,7 +1469,7 @@ initconn(void) goto bad; if (ftp_connect(data, (struct sockaddr *)&data_addr.si_su, - data_addr.su_len) < 0) { + data_addr.su_len, 1) < 0) { if (activefallback) { (void)close(data); data = -1; 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.81.8.1 --- src/usr.bin/ftp/ftp_var.h:1.81 Sun Apr 12 10:18:52 2009 +++ src/usr.bin/ftp/ftp_var.h Tue Dec 17 21:07:59 2013 @@ -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.81.8.1 2013/12/17 21:07:59 bouyer 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.120.2.1 --- src/usr.bin/ftp/main.c:1.120 Sat Dec 10 05:53:58 2011 +++ src/usr.bin/ftp/main.c Tue Dec 17 21:07:59 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: main.c,v 1.120 2011/12/10 05:53:58 lukem Exp $ */ +/* $NetBSD: main.c,v 1.120.2.1 2013/12/17 21:07:59 bouyer 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.120.2.1 2013/12/17 21:07:59 bouyer Exp $"); #endif #endif /* not lint */ @@ -126,6 +126,7 @@ __RCSID("$NetBSD: main.c,v 1.120 2011/12 #define FTP_PROXY "ftp_proxy" /* env var with FTP proxy location */ #define HTTP_PROXY "http_proxy" /* env var with HTTP proxy location */ +#define HTTPS_PROXY "https_proxy" /* env var with HTTPS proxy location */ #define NO_PROXY "no_proxy" /* env var with list of non-proxied * hosts, comma or space separated */ @@ -150,6 +151,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) @@ -485,6 +489,7 @@ main(int volatile argc, char **volatile setupoption("anonpass", getenv("FTPANONPASS"), anonpass); setupoption("ftp_proxy", getenv(FTP_PROXY), ""); setupoption("http_proxy", getenv(HTTP_PROXY), ""); + setupoption("https_proxy", getenv(HTTPS_PROXY), ""); setupoption("no_proxy", getenv(NO_PROXY), ""); setupoption("pager", getenv("PAGER"), DEFAULTPAGER); setupoption("prompt", getenv("FTPPROMPT"), DEFAULTPROMPT); @@ -1044,6 +1049,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); } Index: src/usr.bin/ftp/progressbar.c diff -u src/usr.bin/ftp/progressbar.c:1.21 src/usr.bin/ftp/progressbar.c:1.21.8.1 --- src/usr.bin/ftp/progressbar.c:1.21 Sun Apr 12 10:18:52 2009 +++ src/usr.bin/ftp/progressbar.c Tue Dec 17 21:07:59 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: progressbar.c,v 1.21 2009/04/12 10:18:52 lukem Exp $ */ +/* $NetBSD: progressbar.c,v 1.21.8.1 2013/12/17 21:07:59 bouyer Exp $ */ /*- * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. @@ -31,14 +31,15 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: progressbar.c,v 1.21 2009/04/12 10:18:52 lukem Exp $"); +__RCSID("$NetBSD: progressbar.c,v 1.21.8.1 2013/12/17 21:07:59 bouyer Exp $"); #endif /* not lint */ /* * FTP User Program -- Misc support routines */ -#include <sys/types.h> #include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> #include <err.h> #include <errno.h> Index: src/usr.bin/ftp/util.c diff -u src/usr.bin/ftp/util.c:1.156 src/usr.bin/ftp/util.c:1.156.2.1 --- src/usr.bin/ftp/util.c:1.156 Sat Dec 10 05:53:58 2011 +++ src/usr.bin/ftp/util.c Tue Dec 17 21:07:59 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: util.c,v 1.156 2011/12/10 05:53:58 lukem Exp $ */ +/* $NetBSD: util.c,v 1.156.2.1 2013/12/17 21:07:59 bouyer Exp $ */ /*- * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. @@ -64,7 +64,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: util.c,v 1.156 2011/12/10 05:53:58 lukem Exp $"); +__RCSID("$NetBSD: util.c,v 1.156.2.1 2013/12/17 21:07:59 bouyer Exp $"); #endif /* not lint */ /* @@ -202,25 +202,20 @@ getremoteinfo(void) /* determine remote system type */ if (command("SYST") == COMPLETE) { if (overbose) { - char *cp, c; - - c = 0; - cp = strchr(reply_string + 4, ' '); - if (cp == NULL) - cp = strchr(reply_string + 4, '\r'); - if (cp) { - if (cp[-1] == '.') - cp--; - c = *cp; - *cp = '\0'; - } - - fprintf(ttyout, "Remote system type is %s.\n", - reply_string + 4); - if (cp) - *cp = c; + int os_len = strcspn(reply_string + 4, " \r\n\t"); + if (os_len > 1 && reply_string[4 + os_len - 1] == '.') + os_len--; + fprintf(ttyout, "Remote system type is %.*s.\n", + os_len, reply_string + 4); } - if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) { + /* + * Decide whether we should default to bninary. + * Traditionally checked for "215 UNIX Type: L8", but + * some printers report "Linux" ! so be more forgiving. + * In reality we probably almost never want text any more. + */ + if (!strncasecmp(reply_string + 4, "unix", 4) || + !strncasecmp(reply_string + 4, "linux", 5)) { if (proxy) unix_proxy = 1; else @@ -1351,7 +1346,7 @@ get_line(FILE *stream, char *buf, size_t * error message displayed.) */ int -ftp_connect(int sock, const struct sockaddr *name, socklen_t namelen) +ftp_connect(int sock, const struct sockaddr *name, socklen_t namelen, int pe) { int flags, rv, timeout, error; socklen_t slen; @@ -1417,8 +1412,9 @@ ftp_connect(int sock, const struct socka rv = connect(sock, name, namelen); /* inititate the connection */ if (rv == -1) { /* connection error */ if (errno != EINPROGRESS) { /* error isn't "please wait" */ + if (pe || (errno != EHOSTUNREACH)) connecterror: - warn("Can't connect to `%s:%s'", hname, sname); + warn("Can't connect to `%s:%s'", hname, sname); return -1; } Index: src/usr.bin/ftp/version.h diff -u src/usr.bin/ftp/version.h:1.82 src/usr.bin/ftp/version.h:1.82.8.1 --- src/usr.bin/ftp/version.h:1.82 Sat Jun 5 13:59:39 2010 +++ src/usr.bin/ftp/version.h Tue Dec 17 21:07:59 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: version.h,v 1.82 2010/06/05 13:59:39 lukem Exp $ */ +/* $NetBSD: version.h,v 1.82.8.1 2013/12/17 21:07:59 bouyer Exp $ */ /*- * Copyright (c) 1999-2009 The NetBSD Foundation, Inc. @@ -34,5 +34,5 @@ #endif #ifndef FTP_VERSION -#define FTP_VERSION "20100605" +#define FTP_VERSION "20130220" #endif Added files: Index: src/usr.bin/ftp/ssl.c diff -u /dev/null src/usr.bin/ftp/ssl.c:1.2.10.2 --- /dev/null Tue Dec 17 21:07:59 2013 +++ src/usr.bin/ftp/ssl.c Tue Dec 17 21:07:59 2013 @@ -0,0 +1,608 @@ +/* $NetBSD: ssl.c,v 1.2.10.2 2013/12/17 21:07:59 bouyer 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.2.10.2 2013/12/17 21:07:59 bouyer 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; +#if defined(SO_NOSIGPIPE) || defined(TCP_NOPUSH) + int opt = 1; +#endif + + if ((conn = calloc(1, sizeof(*conn))) == NULL) + return NULL; + + conn->sd = sd; + conn->issock = 1; + fcntl(sd, F_SETFD, FD_CLOEXEC); +#ifdef SO_NOSIGPIPE + setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); +#endif +#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.10.2 --- /dev/null Tue Dec 17 21:07:59 2013 +++ src/usr.bin/ftp/ssl.h Tue Dec 17 21:07:59 2013 @@ -0,0 +1,62 @@ +/* $NetBSD: ssl.h,v 1.1.10.2 2013/12/17 21:07:59 bouyer 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 */