NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

bin/47920: ftp(1): FTPS support



>Number:         47920
>Category:       bin
>Synopsis:       ftp(1): FTPS support
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Wed Jun 12 12:05:00 +0000 2013
>Originator:     NONAKA Kimihiro
>Release:        6.99.21
>Organization:
>Environment:
NetBSD koharu.myhome.local 6.99.21 NetBSD 6.99.21 (KOHARU) #1985: Thu Jun  6 
22:05:15 JST 2013  
nonaka@koharu.myhome.local:/usr/obj.amd64/sys/arch/amd64/compile/KOHARU amd64
>Description:
ftp(1): FTPS support
>How-To-Repeat:

>Fix:
Apply following patch.

diff --git a/extern.h b/extern.h
index e856ef4..41e899b 100644
--- a/extern.h
+++ b/extern.h
@@ -89,11 +89,13 @@
  * SUCH DAMAGE.
  */
 
+#include "ssl.h"
+
 struct sockaddr;
 struct tm;
 struct addrinfo;
 
-void   abort_remote(FILE *);
+void   abort_remote(FETCH *);
 void   account(int, char **);
 void   ai_unmapped(struct addrinfo *);
 int    another(int *, char ***, const char *);
@@ -114,7 +116,7 @@ unsigned char complete(EditLine *, int);
 void   controlediting(void);
 #endif /* !NO_EDITCOMPLETE */
 void   crankrate(int);
-FILE   *dataconn(const char *);
+FETCH   *dataconn(const char *);
 void   delete(int, char **);
 void   disconnect(int, char **);
 void   do_chmod(int, char **);
diff --git a/fetch.c b/fetch.c
index e837d8f..d0b2ed2 100644
--- a/fetch.c
+++ b/fetch.c
@@ -64,7 +64,6 @@ __RCSID("$NetBSD: fetch.c,v 1.202 2013/02/23 13:47:36 
christos Exp $");
 #include <unistd.h>
 #include <time.h>
 
-#include "ssl.h"
 #include "ftp_var.h"
 #include "version.h"
 
@@ -75,6 +74,10 @@ typedef enum {
        HTTPS_URL_T,
 #endif
        FTP_URL_T,
+#ifdef WITH_SSL
+       FTPS_URL_T,
+       FTPES_URL_T,
+#endif
        FILE_URL_T,
        CLASSIC_URL_T
 } url_t;
@@ -105,14 +108,18 @@ static int        redirect_loop;
 #define        FTP_URL         "ftp://";        /* ftp URL prefix */
 #define        HTTP_URL        "http://";       /* http URL prefix */
 #ifdef WITH_SSL
+#define        FTPS_URL        "ftps://"       /* ftps URL prefix */
+#define        FTPES_URL       "ftpes://"      /* ftpes URL prefix */
 #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
+#define        IS_FTP_TYPE(t) \
+       (((t) == FTP_URL_T) || ((t) == FTPS_URL_T) || ((t) == FTPES_URL_T))
+#define        IS_HTTP_TYPE(t) \
+       (((t) == HTTP_URL_T) || ((t) == HTTPS_URL_T))
+#else  /* !WITH_SSL */
+#define        IS_FTP_TYPE(t)  ((t) == FTP_URL_T)
+#define        IS_HTTP_TYPE(t) ((t) == HTTP_URL_T)
+#endif /* WITH_SSL */
 
 /*
  * Determine if token is the next word in buf (case insensitive).
@@ -364,6 +371,20 @@ parse_url(const char *url, const char *desc, url_t *utype,
                *utype = HTTPS_URL_T;
                *portnum = HTTPS_PORT;
                tport = httpsport;
+       } else if (STRNEQUAL(url, FTPS_URL)) {
+               url += sizeof(FTPS_URL) - 1;
+               *utype = FTPS_URL_T;
+               *portnum = FTPS_PORT;
+               tport = ftpsport;
+               ftpssl = 1;
+               ftps_explicit = 0;
+       } else if (STRNEQUAL(url, FTPES_URL)) {
+               url += sizeof(FTPES_URL) - 1;
+               *utype = FTPES_URL_T;
+               *portnum = FTPS_PORT;
+               tport = ftpsport;
+               ftpssl = 1;
+               ftps_explicit = 1;
 #endif
        } else {
                warnx("Invalid %s `%s'", desc, url);
@@ -389,14 +410,14 @@ parse_url(const char *url, const char *desc, url_t *utype,
                len = ep - url;
                thost = (char *)ftp_malloc(len + 1);
                (void)strlcpy(thost, url, len + 1);
-               if (*utype == FTP_URL_T)        /* skip first / for ftp URLs */
+               if (IS_FTP_TYPE(*utype))        /* skip first / for ftp URLs */
                        ep++;
                *path = ftp_strdup(ep);
        }
 
        cp = strchr(thost, '@');        /* look for user[:pass]@ in URLs */
        if (cp != NULL) {
-               if (*utype == FTP_URL_T)
+               if (IS_FTP_TYPE(*utype))
                        anonftp = 0;    /* disable anonftp */
                *uuser = thost;
                *cp = '\0';
@@ -463,7 +484,7 @@ parse_url(const char *url, const char *desc, url_t *utype,
                *port = ftp_strdup(tport);
        if (*path == NULL) {
                const char *emptypath = "/";
-               if (*utype == FTP_URL_T)        /* skip first / for ftp URLs */
+               if (IS_FTP_TYPE(*utype))        /* skip first / for ftp URLs */
                        emptypath++;
                *path = ftp_strdup(emptypath);
        }
@@ -523,7 +544,7 @@ fetch_url(const char *url, const char *proxyenv, char 
*proxyauth, char *wwwauth)
        url_t                   urltype;
        in_port_t               portnum;
 #ifdef WITH_SSL
-       void                    *ssl;
+       struct fetch_ssl        *volatile ssl;
 #endif
 
        DPRINTF("fetch_url: `%s' proxyenv `%s'\n", url, STRorNULL(proxyenv));
@@ -550,7 +571,7 @@ fetch_url(const char *url, const char *proxyenv, char 
*proxyauth, char *wwwauth)
        }
 
        if (EMPTYSTRING(path)) {
-               if (urltype == FTP_URL_T) {
+               if (IS_FTP_TYPE(urltype)) {
                        rval = fetch_ftp(url);
                        goto cleanup_fetch_url;
                }
@@ -574,7 +595,7 @@ fetch_url(const char *url, const char *proxyenv, char 
*proxyauth, char *wwwauth)
        }
        DPRINTF("fetch_url: savefile `%s'\n", savefile);
        if (EMPTYSTRING(savefile)) {
-               if (urltype == FTP_URL_T) {
+               if (IS_FTP_TYPE(urltype)) {
                        rval = fetch_ftp(url);
                        goto cleanup_fetch_url;
                }
@@ -631,7 +652,7 @@ fetch_url(const char *url, const char *proxyenv, char 
*proxyauth, char *wwwauth)
 #endif
                        if (proxyenv == NULL && IS_HTTP_TYPE(urltype))
                                proxyenv = getoptionvalue("http_proxy");
-                       else if (urltype == FTP_URL_T)
+                       else if (IS_FTP_TYPE(urltype))
                                proxyenv = getoptionvalue("ftp_proxy");
                }
                direction = "retrieved";
@@ -673,7 +694,7 @@ fetch_url(const char *url, const char *proxyenv, char 
*proxyauth, char *wwwauth)
                                        }
                                }
                                FREEPTR(np_copy);
-                               if (isproxy == 0 && urltype == FTP_URL_T) {
+                               if (isproxy == 0 && IS_FTP_TYPE(urltype)) {
                                        rval = fetch_ftp(url);
                                        goto cleanup_fetch_url;
                                }
@@ -691,7 +712,7 @@ fetch_url(const char *url, const char *proxyenv, char 
*proxyauth, char *wwwauth)
                                        goto cleanup_fetch_url;
 
                                if ((!IS_HTTP_TYPE(purltype)
-                                    && purltype != FTP_URL_T) ||
+                                    && !IS_FTP_TYPE(purltype)) ||
                                    EMPTYSTRING(phost) ||
                                    (! EMPTYSTRING(ppath)
                                     && strcmp(ppath, "/") != 0)) {
@@ -791,11 +812,26 @@ fetch_url(const char *url, const char *proxyenv, char 
*proxyauth, char *wwwauth)
 
                if (s < 0) {
                        warnx("Can't connect to `%s:%s'", host, port);
+#ifdef WITH_SSL
+                       fetch_stop_ssl(ssl);
+#endif
                        goto cleanup_fetch_url;
                }
 
                fin = fetch_fdopen(s, "r+");
-               fetch_set_ssl(fin, ssl);
+               if (fin == NULL) {
+                       warnx("fdopen failed");
+#ifdef WITH_SSL
+                       fetch_stop_ssl(ssl);
+#endif
+                       goto cleanup_fetch_url;
+               }
+#ifdef WITH_SSL
+               if (urltype == HTTPS_URL_T) {
+                       fetch_set_ssl(fin, ssl);
+                       ssl = NULL;
+               }
+#endif
 
                /*
                 * Construct and send the request.
@@ -1254,7 +1290,7 @@ fetch_url(const char *url, const char *proxyenv, char 
*proxyauth, char *wwwauth)
                        while (bufrem > 0) {
                                flen = fetch_read(xferbuf, sizeof(char),
                                    MIN((off_t)bufsize, bufrem), fin);
-                               if (flen <= 0)
+                               if (flen == 0)
                                        goto chunkdone;
                                bytes += flen;
                                bufrem -= flen;
@@ -1414,7 +1450,12 @@ fetch_ftp(const char *url)
        rval = 1;
        transtype = TYPE_I;
 
-       if (STRNEQUAL(url, FTP_URL)) {
+       if (STRNEQUAL(url, FTP_URL)
+#ifdef WITH_SSL
+           || STRNEQUAL(url, FTPS_URL)
+           || STRNEQUAL(url, FTPES_URL)
+#endif
+          ) {
                if ((parse_url(url, "URL", &urltype, &uuser, &pass,
                    &host, &port, &portnum, &path) == -1) ||
                    (uuser != NULL && *uuser == '\0') ||
@@ -1502,7 +1543,7 @@ fetch_ftp(const char *url)
                }
        } else
                dir = NULL;
-       if (urltype == FTP_URL_T && file != NULL) {
+       if (IS_FTP_TYPE(urltype) && file != NULL) {
                url_decode(file);
                /* but still don't url_decode(dir) */
        }
@@ -1628,7 +1669,7 @@ fetch_ftp(const char *url)
                 * Note that we don't need `dir' after this point.
                 */
                do {
-                       if (urltype == FTP_URL_T) {
+                       if (IS_FTP_TYPE(urltype)) {
                                nextpart = strchr(dir, '/');
                                if (nextpart) {
                                        *nextpart = '\0';
@@ -1639,7 +1680,7 @@ fetch_ftp(const char *url)
                                nextpart = NULL;
                        DPRINTF("fetch_ftp: dir `%s', nextpart `%s'\n",
                            STRorNULL(dir), STRorNULL(nextpart));
-                       if (urltype == FTP_URL_T || *dir != '\0') {
+                       if (IS_FTP_TYPE(urltype) || *dir != '\0') {
                                (void)strlcpy(cmdbuf, "cd", sizeof(cmdbuf));
                                xargv[0] = cmdbuf;
                                xargv[1] = dir;
@@ -1796,7 +1837,11 @@ go_fetch(const char *url)
         * 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))
+       if ((p = strstr(url, "://")) != NULL && ! STRNEQUAL(url, FTP_URL)
+#ifdef WITH_SSL
+           && ! STRNEQUAL(url, FTPS_URL) && ! STRNEQUAL(url, FTPES_URL)
+#endif
+          )
                errx(1, "Unsupported URL scheme `%.*s'", (int)(p - url), url);
 
        /*
@@ -1805,7 +1850,11 @@ go_fetch(const char *url)
         * Othewise, use fetch_ftp().
         */
        proxyenv = getoptionvalue("ftp_proxy");
-       if (!EMPTYSTRING(proxyenv) && STRNEQUAL(url, FTP_URL))
+       if (!EMPTYSTRING(proxyenv) && (STRNEQUAL(url, FTP_URL)
+#ifdef WITH_SSL
+           || STRNEQUAL(url, FTPS_URL) || STRNEQUAL(url, FTPES_URL)
+#endif
+           ))
                return (fetch_url(url, NULL, NULL, NULL));
 
        return (fetch_ftp(url));
diff --git a/ftp.c b/ftp.c
index d1a8785..d43a35b 100644
--- a/ftp.c
+++ b/ftp.c
@@ -131,7 +131,7 @@ int ptabflg;
 int    ptflag = 0;
 char   pasv[BUFSIZ];   /* passive port for proxy data connection */
 
-static int empty(FILE *, FILE *, int);
+static int empty(FETCH *, FETCH *, int);
 __dead static void abort_squared(int);
 
 struct sockinet {
@@ -164,6 +164,9 @@ hookup(const char *host, const char *port)
        static char hostnamebuf[MAXHOSTNAMELEN];
        socklen_t len;
        int on = 1;
+#ifdef WITH_SSL
+       struct fetch_ssl *ssl = NULL;
+#endif
 
        memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
        memset((char *)&myctladdr, 0, sizeof (myctladdr));
@@ -202,6 +205,9 @@ hookup(const char *host, const char *port)
                                /* if we have multiple possibilities */
                        fprintf(ttyout, "Trying %s:%s ...\n", hname, sname);
                }
+#ifdef WITH_SSL
+ retry:
+#endif
                s = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
                if (s < 0) {
                        warn("Can't create socket for connection to `%s:%s'",
@@ -215,6 +221,21 @@ hookup(const char *host, const char *port)
                        continue;
                }
 
+#ifdef WITH_SSL
+               if (ftpssl && !ftps_explicit) {
+                       if ((ssl = fetch_start_ssl(s)) == NULL) {
+                               close(s);
+                               s = -1;
+                               if (ftps_fallback) {
+                                       ftps_explicit = 1;
+                                       goto retry;
+                               } else {
+                                       continue;
+                               }
+                       }
+               }
+#endif
+
                /* finally we got one */
                break;
        }
@@ -248,24 +269,31 @@ hookup(const char *host, const char *port)
                }
        }
 #endif
-       cin = fdopen(s, "r");
-       cout = fdopen(s, "w");
+       cin = fetch_fdopen(s, "r");
+       cout = fetch_fdopen(s, "w");
        if (cin == NULL || cout == NULL) {
                warnx("Can't fdopen socket");
                if (cin)
-                       (void)fclose(cin);
+                       (void)fetch_close(cin);
                if (cout)
-                       (void)fclose(cout);
+                       (void)fetch_close(cout);
                code = -1;
                goto bad;
        }
+#ifdef WITH_SSL
+       if (ftpssl && !ftps_explicit) {
+               fetch_set_ssl(cin, ssl);
+               fetch_set_ssl(cout, ssl);
+               ssl = NULL;
+       }
+#endif
        if (verbose)
                fprintf(ttyout, "Connected to %s.\n", hostname);
        if (getreply(0) > 2) {  /* read startup message from server */
                if (cin)
-                       (void)fclose(cin);
+                       (void)fetch_close(cin);
                if (cout)
-                       (void)fclose(cout);
+                       (void)fetch_close(cout);
                code = -1;
                goto bad;
        }
@@ -277,6 +305,9 @@ hookup(const char *host, const char *port)
 
        return (hostname);
  bad:
+#ifdef WITH_SSL
+       fetch_stop_ssl(ssl);
+#endif
        (void)close(s);
        return (NULL);
 }
@@ -343,10 +374,10 @@ command(const char *fmt, ...)
        oldsigint = xsignal(SIGINT, cmdabort);
 
        va_start(ap, fmt);
-       vfprintf(cout, fmt, ap);
+       fetch_vprintf(cout, fmt, ap);
        va_end(ap);
-       fputs("\r\n", cout);
-       (void)fflush(cout);
+       fetch_printf(cout, "%s", "\r\n");
+       (void)fetch_flush(cout);
        cpend = 1;
        r = getreply(!strcmp(fmt, "QUIT"));
        if (abrtflag && oldsigint != SIG_IGN)
@@ -382,20 +413,22 @@ getreply(int expecteof)
                dig = n = code = 0;
                cp = current_line;
                while (alarmtimer(quit_time ? quit_time : 60),
-                      ((c = getc(cin)) != '\n')) {
+                      ((c = fetch_getc(cin)) != '\n')) {
                        if (c == IAC) {     /* handle telnet commands */
-                               switch (c = getc(cin)) {
+                               switch (c = fetch_getc(cin)) {
                                case WILL:
                                case WONT:
-                                       c = getc(cin);
-                                       fprintf(cout, "%c%c%c", IAC, DONT, c);
-                                       (void)fflush(cout);
+                                       c = fetch_getc(cin);
+                                       fetch_printf(cout, "%c%c%c", IAC, DONT,
+                                           c);
+                                       (void)fetch_flush(cout);
                                        break;
                                case DO:
                                case DONT:
-                                       c = getc(cin);
-                                       fprintf(cout, "%c%c%c", IAC, WONT, c);
-                                       (void)fflush(cout);
+                                       c = fetch_getc(cin);
+                                       fetch_printf(cout, "%c%c%c", IAC, WONT,
+                                           c);
+                                       (void)fetch_flush(cout);
                                        break;
                                default:
                                        break;
@@ -412,7 +445,7 @@ getreply(int expecteof)
                                int reply_abrtflag = abrtflag;
 
                                alarmtimer(0);
-                               if (expecteof && feof(cin)) {
+                               if (expecteof && fetch_eof(cin)) {
                                        (void)xsignal(SIGINT, oldsigint);
                                        (void)xsignal(SIGALRM, oldsigalrm);
                                        code = 221;
@@ -510,19 +543,19 @@ getreply(int expecteof)
 }
 
 static int
-empty(FILE *ecin, FILE *din, int sec)
+empty(FETCH *ecin, FETCH *din, int sec)
 {
        int             nr, nfd;
        struct pollfd   pfd[2];
 
        nfd = 0;
        if (ecin) {
-               pfd[nfd].fd = fileno(ecin);
+               pfd[nfd].fd = fetch_fileno(ecin);
                pfd[nfd++].events = POLLIN;
        }
 
        if (din) {
-               pfd[nfd].fd = fileno(din);
+               pfd[nfd].fd = fetch_fileno(din);
                pfd[nfd++].events = POLLIN;
        }
 
@@ -576,15 +609,16 @@ abortxfer(int notused)
  * In the case of error, errno contains the appropriate error code.
  */
 static int
-copy_bytes(int infd, int outfd, char *buf, size_t bufsize,
+copy_bytes(FETCH *infd, FETCH *outfd, char *buf, size_t bufsize,
        int rate_limit, int hash_interval)
 {
        volatile off_t  hashc;
-       ssize_t         inc, outc;
+       size_t          inc, outc;
        char            *bufp;
        struct timeval  tvthen, tvnow, tvdiff;
        off_t           bufrem, bufchunk;
        int             serr;
+       int             rv = 0;
 
        hashc = hash_interval;
        if (rate_limit)
@@ -601,16 +635,23 @@ copy_bytes(int infd, int outfd, char *buf, size_t bufsize,
                                        /* copy bufchunk at a time */
                bufrem = bufchunk;
                while (bufrem > 0) {
-                       inc = read(infd, buf, MIN((off_t)bufsize, bufrem));
-                       if (inc <= 0)
+                       inc = fetch_read(buf, 1, MIN((off_t)bufsize, bufrem),
+                           infd);
+                       if (inc == 0) {
+                               if (!fetch_eof(infd))
+                                       rv = 1;
                                goto copy_done;
+                       }
                        bytes += inc;
                        bufrem -= inc;
                        bufp = buf;
                        while (inc > 0) {
-                               outc = write(outfd, bufp, inc);
-                               if (outc < 0)
+                               outc = fetch_write(bufp, 1, inc, outfd);
+                               if (outc == 0) {
+                                       if (fetch_error(outfd))
+                                               rv = 2;
                                        goto copy_done;
+                               }
                                inc -= outc;
                                bufp += outc;
                        }
@@ -642,12 +683,7 @@ copy_bytes(int infd, int outfd, char *buf, size_t bufsize,
                (void)fflush(ttyout);
        }
        errno = serr;
-       if (inc == -1)
-               return 1;
-       if (outc == -1)
-               return 2;
-
-       return 0;
+       return rv;
 }
 
 void
@@ -657,7 +693,7 @@ sendrequest(const char *cmd, const char *local, const char 
*remote,
        struct stat st;
        int c;
        FILE *volatile fin;
-       FILE *volatile dout;
+       FETCH *volatile dout;
        int (*volatile closefunc)(FILE *);
        sigfunc volatile oldintr;
        sigfunc volatile oldintp;
@@ -667,6 +703,10 @@ sendrequest(const char *cmd, const char *local, const char 
*remote,
        static size_t bufsize;
        static char *buf;
        int oprogress;
+#ifdef WITH_SSL
+       FETCH *volatile fin2;
+       int fdin;
+#endif
 
        hashbytes = mark;
        direction = "sent";
@@ -783,8 +823,22 @@ sendrequest(const char *cmd, const char *local, const char 
*remote,
 
        case TYPE_I:
        case TYPE_L:
-               c = copy_bytes(fileno(fin), fileno(dout), buf, bufsize,
-                              rate_put, hash_interval);
+#ifdef WITH_SSL
+               c = 1;
+               fdin = dup(fileno(fin));
+               if (fdin >= 0) {
+                       fin2 = fetch_fdopen(fdin, "r");
+                       if (fin2 != NULL) {
+                               c = copy_bytes(fin2, dout, buf, bufsize,
+                                   rate_put, hash_interval);
+                               fetch_close(fin2);
+                       } else
+                               close(fdin);
+               }
+#else /* !WITH_SSL */
+               c = copy_bytes(fin, dout, buf, bufsize, rate_put,
+                   hash_interval);
+#endif /* WITH_SSL */
                if (c == 1) {
                        warn("Reading `%s'", local);
                } else if (c == 2) {
@@ -802,16 +856,16 @@ sendrequest(const char *cmd, const char *local, const 
char *remote,
                                        (void)fflush(ttyout);
                                        hashbytes += mark;
                                }
-                               if (ferror(dout))
+                               if (fetch_error(dout))
                                        break;
-                               (void)putc('\r', dout);
+                               (void)fetch_putc('\r', dout);
                                bytes++;
                        }
-                       (void)putc(c, dout);
+                       (void)fetch_putc(c, dout);
                        bytes++;
 #if 0  /* this violates RFC 959 */
                        if (c == '\r') {
-                               (void)putc('\0', dout);
+                               (void)fetch_putc('\0', dout);
                                bytes++;
                        }
 #endif
@@ -823,7 +877,7 @@ sendrequest(const char *cmd, const char *local, const char 
*remote,
                }
                if (ferror(fin))
                        warn("Reading `%s'", local);
-               if (ferror(dout)) {
+               if (fetch_error(dout)) {
                        if (errno != EPIPE)
                                warn("Writing to network");
                        bytes = -1;
@@ -836,7 +890,7 @@ sendrequest(const char *cmd, const char *local, const char 
*remote,
                (*closefunc)(fin);
                fin = NULL;
        }
-       (void)fclose(dout);
+       (void)fetch_close(dout);
        dout = NULL;
        (void)getreply(0);
        if (bytes > 0)
@@ -855,7 +909,7 @@ sendrequest(const char *cmd, const char *local, const char 
*remote,
                data = -1;
        }
        if (dout) {
-               (void)fclose(dout);
+               (void)fetch_close(dout);
                dout = NULL;
        }
        (void)getreply(0);
@@ -875,7 +929,7 @@ sendrequest(const char *cmd, const char *local, const char 
*remote,
        if (closefunc != NULL && fin != NULL)
                (*closefunc)(fin);
        if (dout)
-               (void)fclose(dout);
+               (void)fetch_close(dout);
        progress = oprogress;
        restart_point = 0;
        bytes = 0;
@@ -886,7 +940,7 @@ recvrequest(const char *cmd, const char *volatile local, 
const char *remote,
            const char *lmode, int printnames, int ignorespecial)
 {
        FILE *volatile fout;
-       FILE *volatile din;
+       FETCH *volatile din;
        int (*volatile closefunc)(FILE *);
        sigfunc volatile oldintr;
        sigfunc volatile oldintp;
@@ -903,6 +957,11 @@ recvrequest(const char *cmd, const char *volatile local, 
const char *remote,
        struct timeval tval[2];
        int oprogress;
        int opreserve;
+#ifdef WITH_SSL
+       const char *fmode;
+       FETCH *volatile fout2;
+       int fdout;
+#endif
 
        fout = NULL;
        din = NULL;
@@ -1003,11 +1062,17 @@ recvrequest(const char *cmd, const char *volatile 
local, const char *remote,
        if (din == NULL)
                goto abort;
        if (!ignorespecial && strcmp(local, "-") == 0) {
+#ifdef WITH_SSL
+               fmode = "w";
+#endif
                fout = stdout;
                progress = 0;
                preserve = 0;
        } else if (!ignorespecial && *local == '|') {
                oldintp = xsignal(SIGPIPE, SIG_IGN);
+#ifdef WITH_SSL
+               fmode = "w";
+#endif
                fout = popen(local + 1, "w");
                if (fout == NULL) {
                        warn("Can't execute `%s'", local+1);
@@ -1017,6 +1082,9 @@ recvrequest(const char *cmd, const char *volatile local, 
const char *remote,
                preserve = 0;
                closefunc = pclose;
        } else {
+#ifdef WITH_SSL
+               fmode = lmode;
+#endif
                fout = fopen(local, lmode);
                if (fout == NULL) {
                        warn("Can't open `%s'", local);
@@ -1049,8 +1117,22 @@ recvrequest(const char *cmd, const char *volatile local, 
const char *remote,
                        warn("Can't seek to restart `%s'", local);
                        goto cleanuprecv;
                }
-               c = copy_bytes(fileno(din), fileno(fout), buf, bufsize,
-                              rate_get, hash_interval);
+#ifdef WITH_SSL
+               c = 2;
+               fdout = dup(fileno(fout));
+               if (fdout >= 0) {
+                       fout2 = fetch_fdopen(fdout, fmode);
+                       if (fout2 != NULL) {
+                               c = copy_bytes(din, fout2, buf, bufsize,
+                                   rate_get, hash_interval);
+                               fetch_close(fout2);
+                       } else
+                               close(fdout);
+               }
+#else /* !WITH_SSL */
+               c = copy_bytes(din, fout, buf, bufsize, rate_get,
+                   hash_interval);
+#endif /* WITH_SSL */
                if (c == 1) {
                        if (errno != EPIPE)
                                warn("Reading from network");
@@ -1079,7 +1161,7 @@ recvrequest(const char *cmd, const char *volatile local, 
const char *remote,
                                goto cleanuprecv;
                        }
                }
-               while ((c = getc(din)) != EOF) {
+               while ((c = fetch_getc(din)) != EOF) {
                        if (c == '\n')
                                bare_lfs++;
                        while (c == '\r') {
@@ -1089,7 +1171,7 @@ recvrequest(const char *cmd, const char *volatile local, 
const char *remote,
                                        hashbytes += mark;
                                }
                                bytes++;
-                               if ((c = getc(din)) != '\n' || tcrflag) {
+                               if ((c = fetch_getc(din)) != '\n' || tcrflag) {
                                        if (ferror(fout))
                                                goto break2;
                                        (void)putc('\r', fout);
@@ -1111,7 +1193,7 @@ recvrequest(const char *cmd, const char *volatile local, 
const char *remote,
                                (void)putc('#', ttyout);
                        (void)putc('\n', ttyout);
                }
-               if (ferror(din)) {
+               if (fetch_error(din)) {
                        if (errno != EPIPE)
                                warn("Reading from network");
                        bytes = -1;
@@ -1126,7 +1208,7 @@ recvrequest(const char *cmd, const char *volatile local, 
const char *remote,
                (*closefunc)(fout);
                fout = NULL;
        }
-       (void)fclose(din);
+       (void)fetch_close(din);
        din = NULL;
        (void)getreply(0);
        if (bare_lfs) {
@@ -1184,7 +1266,7 @@ recvrequest(const char *cmd, const char *volatile local, 
const char *remote,
        if (closefunc != NULL && fout != NULL)
                (*closefunc)(fout);
        if (din)
-               (void)fclose(din);
+               (void)fetch_close(din);
        progress = oprogress;
        preserve = opreserve;
        bytes = 0;
@@ -1650,7 +1732,7 @@ initconn(void)
        return (1);
 }
 
-FILE *
+FETCH *
 dataconn(const char *lmode)
 {
        struct sockinet from;
@@ -1658,9 +1740,13 @@ dataconn(const char *lmode)
        struct timeval  endtime, now, td;
        struct pollfd   pfd[1];
        socklen_t       fromlen;
+       FETCH           *conn;
+#ifdef WITH_SSL
+       struct fetch_ssl *ssl;
+#endif
 
        if (passivemode)        /* passive data connection */
-               return (fdopen(data, lmode));
+               goto dataconn_open;
 
                                /* active mode data connection */
 
@@ -1718,7 +1804,27 @@ dataconn(const char *lmode)
                }
        }
 #endif
-       return (fdopen(data, lmode));
+ dataconn_open:
+#ifdef WITH_SSL
+       ssl = NULL;
+       if (ftpssl) {
+               if ((ssl = fetch_start_ssl(data)) == NULL) {
+                       warn("SSL negotiation failed on data connection");
+                       goto dataconn_failed;
+               }
+       }
+#endif
+       conn = fetch_fdopen(data, lmode);
+#ifdef WITH_SSL
+       if (ftpssl) {
+               if (conn == NULL) {
+                       fetch_stop_ssl(ssl);
+                       goto dataconn_failed;
+               }
+               fetch_set_ssl(conn, ssl);
+       }
+#endif
+       return conn;
 
  dataconn_failed:
        (void)close(data);
@@ -1746,8 +1852,8 @@ pswitch(int flag)
                char name[MAXHOSTNAMELEN];
                struct sockinet mctl;
                struct sockinet hctl;
-               FILE *in;
-               FILE *out;
+               FETCH *in;
+               FETCH *out;
                int tpe;
                int curtpe;
                int cpnd;
@@ -2052,7 +2158,7 @@ abort_squared(int dummy)
 }
 
 void
-abort_remote(FILE *din)
+abort_remote(FETCH *din)
 {
        char buf[BUFSIZ];
        int nfnd;
@@ -2071,10 +2177,10 @@ abort_remote(FILE *din)
        buf[0] = IAC;
        buf[1] = IP;
        buf[2] = IAC;
-       if (send(fileno(cout), buf, 3, MSG_OOB) != 3)
+       if (fetch_send(cout, buf, 3, MSG_OOB) != 3)
                warn("Can't send abort message");
-       fprintf(cout, "%cABOR\r\n", DM);
-       (void)fflush(cout);
+       fetch_printf(cout, "%cABOR\r\n", DM);
+       (void)fetch_flush(cout);
        if ((nfnd = empty(cin, din, 10)) <= 0) {
                if (nfnd < 0)
                        warn("Can't send abort message");
@@ -2083,7 +2189,7 @@ abort_remote(FILE *din)
                lostpeer(0);
        }
        if (din && (nfnd & 2)) {
-               while (read(fileno(din), buf, BUFSIZ) > 0)
+               while (fetch_read(buf, 1, BUFSIZ, din) > 0)
                        continue;
        }
        if (getreply(0) == ERROR && code == 552) {
diff --git a/ftp_var.h b/ftp_var.h
index 7e5040a..7db055e 100644
--- a/ftp_var.h
+++ b/ftp_var.h
@@ -116,6 +116,7 @@
 
 #include "extern.h"
 #include "progressbar.h"
+#include "ssl.h"
 
 /*
  * Format of command table.
@@ -162,6 +163,9 @@ enum {
        FEAT_REST_STREAM,       /* RESTart STREAM */
        FEAT_SIZE,              /* SIZE */
        FEAT_TVFS,              /* TVFS (not used) */
+       FEAT_PBSZ,              /* PBSZ */
+       FEAT_PROT,              /* PROT */
+       FEAT_AUTH_TLS,          /* AUTH TLS */
        FEAT_max
 };
 
@@ -176,6 +180,7 @@ enum {
 #define        DEFAULTINCR     1024    /* default increment for `rate' command 
*/
 
 #define        FTP_PORT        21      /* default if ! 
getservbyname("ftp/tcp") */
+#define        FTPS_PORT       990     /* default if ! 
getservbyname("ftps/tcp") */
 #define        HTTP_PORT       80      /* default if ! 
getservbyname("http/tcp") */
 #define        HTTPS_PORT      443     /* default if ! 
getservbyname("https/tcp") */
 #ifndef        GATE_PORT
@@ -255,6 +260,11 @@ GLOBAL     int     epsv6;          /* use EPSV/EPRT on 
IPv6 connections */
 GLOBAL int     epsv6bad;       /* EPSV doesn't work on the current server */
 GLOBAL int     editing;        /* command line editing enabled */
 GLOBAL int     features[FEAT_max];     /* remote FEATures supported */
+#ifdef WITH_SSL
+GLOBAL int     ftpssl;         /* use FTPS */
+GLOBAL int     ftps_explicit;  /* FTPS explicit mode */
+GLOBAL int     ftps_fallback;  /* explicit mode from implicit mode */
+#endif
 
 #ifndef NO_EDITCOMPLETE
 GLOBAL EditLine *el;           /* editline(3) status structure */
@@ -275,6 +285,7 @@ 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 *ftpsport;   /* port number to use for FTPS connections */
 GLOBAL const char *httpsport;  /* port number to use for HTTPS connections */
 #endif
 GLOBAL const char *gateport;   /* port number to use for gateftp connections */
@@ -315,8 +326,8 @@ GLOBAL      void    (*reply_callback)(const char *);
 
 GLOBAL volatile sig_atomic_t   sigint_raised;
 
-GLOBAL FILE    *cin;
-GLOBAL FILE    *cout;
+GLOBAL FETCH   *cin;
+GLOBAL FETCH   *cout;
 GLOBAL int      data;
 
 extern struct cmd      cmdtab[];
diff --git a/main.c b/main.c
index c84364d..438fc2a 100644
--- a/main.c
+++ b/main.c
@@ -152,6 +152,7 @@ main(int volatile argc, char **volatile argv)
        ftpport = "ftp";
        httpport = "http";
 #ifdef WITH_SSL
+       ftpsport = "ftps";
        httpsport = "https";
 #endif
        gateport = NULL;
@@ -201,6 +202,11 @@ main(int volatile argc, char **volatile argv)
 #else
        family = AF_INET;       /* force AF_INET if no INET6 support */
 #endif
+#ifdef WITH_SSL
+       ftpssl = 0;
+       ftps_explicit = 0;
+       ftps_fallback = 1;
+#endif
 
        netrc[0] = '\0';
        cp = getenv("NETRC");
@@ -335,6 +341,9 @@ main(int volatile argc, char **volatile argv)
                        break;
 
                case 'P':
+#ifdef WITH_SSL
+                       ftpsport =
+#endif
                        ftpport = optarg;
                        break;
 
@@ -1048,6 +1057,10 @@ usage(void)
 "           [-r retry] [-s srcaddr] [-T dir,max[,inc]]\n"
 "           [[user@]host [port]] [host:path[/]] [file:///file]\n"
 "           [ftp://[user[:pass]@]host[:port]/path[/]]\n";
+#ifdef WITH_SSL
+"           [ftps://[user[:pass]@]host[:port]/path[/]]\n" /* Implicit */
+"           [ftpes://[user[:pass]@]host[:port]/path[/]]\n" /* Explicit */
+#endif
 "           [http://[user[:pass]@]host[:port]/path] [...]\n"
 #ifdef WITH_SSL
 "           [https://[user[:pass]@]host[:port]/path] [...]\n"
diff --git a/ssl.c b/ssl.c
index b34c864..1439af3 100644
--- a/ssl.c
+++ b/ssl.c
@@ -42,6 +42,7 @@ __RCSID("$NetBSD: ssl.c,v 1.2 2012/12/24 22:12:28 christos 
Exp $");
 
 #include <sys/param.h>
 #include <sys/select.h>
+#include <sys/stat.h>
 #include <sys/uio.h>
 
 #include <netinet/tcp.h>
@@ -57,6 +58,11 @@ __RCSID("$NetBSD: ssl.c,v 1.2 2012/12/24 22:12:28 christos 
Exp $");
 extern int quit_time, verbose, ftp_debug;
 extern FILE *ttyout;
 
+struct fetch_ssl {
+       SSL     *ssl;
+       int     refcnt;
+};
+
 struct fetch_connect {
        int                      sd;            /* file/socket descriptor */
        char                    *buf;           /* buffer */
@@ -73,7 +79,7 @@ struct fetch_connect {
        int                      issock;
        int                      iserr;
        int                      iseof;
-       SSL                     *ssl;           /* SSL handle */
+       struct fetch_ssl        *ssl;           /* SSL handle */
 };
 
 /*
@@ -119,7 +125,8 @@ fetch_writev(struct fetch_connect *conn, struct iovec *iov, 
int iovcnt)
                }
                errno = 0;
                if (conn->ssl != NULL)
-                       len = SSL_write(conn->ssl, iov->iov_base, iov->iov_len);
+                       len = SSL_write(conn->ssl->ssl, iov->iov_base,
+                           iov->iov_len);
                else
                        len = writev(conn->sd, iov, iovcnt);
                if (len == 0) {
@@ -150,14 +157,23 @@ fetch_writev(struct fetch_connect *conn, struct iovec 
*iov, int iovcnt)
 /*
  * Write to a connection w/ timeout
  */
-static int
-fetch_write(struct fetch_connect *conn, const char *str, size_t len)
+size_t
+fetch_write(void *ptr, size_t size, size_t nmemb, struct fetch_connect *conn)
 {
        struct iovec iov[1];
+       int rv;
 
-       iov[0].iov_base = (char *)__UNCONST(str);
-       iov[0].iov_len = len;
-       return fetch_writev(conn, iov, 1);
+       if (size == 0 || nmemb == 0)
+               return 0;
+
+       iov[0].iov_base = ptr;
+       iov[0].iov_len = size * nmemb;
+       rv = fetch_writev(conn, iov, 1);
+       if (rv < 0) {
+               conn->iserr = 1;
+               return 0;
+       }
+       return rv;
 }
 
 /*
@@ -167,24 +183,35 @@ int
 fetch_printf(struct fetch_connect *conn, const char *fmt, ...)
 {
        va_list ap;
+       int r;
+
+       va_start(ap, fmt);
+       r = fetch_vprintf(conn, fmt, ap);
+       va_end(ap);
+
+       return r;
+}
+
+int
+fetch_vprintf(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);
+       r = fetch_write(msg, 1, len, conn);
        free(msg);
        return r;
 }
 
+
 int
 fetch_fileno(struct fetch_connect *conn)
 {
@@ -207,6 +234,13 @@ fetch_clearerr(struct fetch_connect *conn)
 }
 
 int
+fetch_eof(struct fetch_connect *conn)
+{
+
+       return conn->iseof;
+}
+
+int
 fetch_flush(struct fetch_connect *conn)
 {
        int v;
@@ -227,9 +261,47 @@ struct fetch_connect *
 fetch_open(const char *fname, const char *fmode)
 {
        struct fetch_connect *conn;
+       int mode, option;
        int fd;
 
-       fd = open(fname, O_RDONLY); /* XXX: fmode */
+        switch (*fmode++) {
+       default: /* illegal mode */
+               return NULL;
+       case 'r': /* open for reading */
+               mode = O_RDONLY;
+               option = 0;
+               break;
+       case 'w': /* open for writing */
+               mode = O_WRONLY;
+               option = O_CREAT | O_TRUNC;
+               break;
+       case 'a': /* open for appending */
+               mode = O_WRONLY;
+               option = O_CREAT | O_APPEND;
+               break;
+        }
+       for (; *fmode != '\0'; fmode++) {
+               switch (*fmode) {
+               case '+':
+                       mode = O_RDWR;
+                       break;
+               case 'f':
+                       option |= O_NONBLOCK;
+                       break;
+               case 'e':
+                       option |= O_CLOEXEC;
+                       break;
+               case 'x':
+                       option |= O_EXCL;
+                       break;
+               case 'b':
+                       break;
+               default:        /* We could produce a warning here */
+                       break;
+               }
+       }
+
+       fd = open(fname, mode | option);
        if (fd < 0)
                return NULL;
 
@@ -248,22 +320,28 @@ struct fetch_connect *
 fetch_fdopen(int sd, const char *fmode)
 {
        struct fetch_connect *conn;
+       struct stat sb;
 #if defined(SO_NOSIGPIPE) || defined(TCP_NOPUSH)
        int opt = 1;
 #endif
 
+       if (fstat(sd, &sb) < 0)
+               return NULL;
+
        if ((conn = calloc(1, sizeof(*conn))) == NULL)
                return NULL;
 
        conn->sd = sd;
-       conn->issock = 1;
-       fcntl(sd, F_SETFD, FD_CLOEXEC);
+       if (S_ISSOCK(sb.st_mode)) {
+               conn->issock = 1;
+               fcntl(sd, F_SETFD, FD_CLOEXEC);
 #ifdef SO_NOSIGPIPE
-       setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
+               setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
 #endif
 #ifdef TCP_NOPUSH
-       setsockopt(sd, IPPROTO_TCP, TCP_NOPUSH, &opt, sizeof(opt));
+               setsockopt(sd, IPPROTO_TCP, TCP_NOPUSH, &opt, sizeof(opt));
 #endif
+       }
        return conn;
 }
 
@@ -274,7 +352,7 @@ fetch_close(struct fetch_connect *conn)
 
        if (conn != NULL) {
                fetch_flush(conn);
-               SSL_free(conn->ssl);
+               fetch_free_ssl(conn);
                rv = close(conn->sd);
                if (rv < 0) {
                        errno = rv;
@@ -287,8 +365,9 @@ fetch_close(struct fetch_connect *conn)
        return rv;
 }
 
-#define FETCH_READ_WAIT                -2
-#define FETCH_READ_ERROR       -1
+#define FETCH_READ_INTR                -4
+#define FETCH_READ_WAIT                -3
+#define FETCH_READ_ERROR       -2
 
 static ssize_t
 fetch_ssl_read(SSL *ssl, void *buf, size_t len)
@@ -299,6 +378,9 @@ fetch_ssl_read(SSL *ssl, void *buf, size_t len)
        rlen = SSL_read(ssl, buf, len);
        if (rlen < 0) {
                ssl_err = SSL_get_error(ssl, rlen);
+               if (ssl_err == SSL_ERROR_SYSCALL && errno == EINTR) {
+                       return FETCH_READ_INTR;
+               }
                if (ssl_err == SSL_ERROR_WANT_READ ||
                    ssl_err == SSL_ERROR_WANT_WRITE) {
                        return FETCH_READ_WAIT;
@@ -316,7 +398,9 @@ fetch_nonssl_read(int sd, void *buf, size_t len)
 
        rlen = read(sd, buf, len);
        if (rlen < 0) {
-               if (errno == EAGAIN || errno == EINTR)
+               if (errno == EINTR)
+                       return FETCH_READ_INTR;
+               if (errno == EAGAIN)
                        return FETCH_READ_WAIT;
                return FETCH_READ_ERROR;
        }
@@ -346,8 +430,8 @@ fetch_cache_data(struct fetch_connect *conn, char *src, 
size_t nbytes)
        return 0;
 }
 
-ssize_t
-fetch_read(void *ptr, size_t size, size_t nmemb, struct fetch_connect *conn)
+static ssize_t
+fetch_read1(void *ptr, size_t size, size_t nmemb, struct fetch_connect *conn)
 {
        struct timeval now, timeout, delta;
        fd_set readfds;
@@ -402,7 +486,7 @@ fetch_read(void *ptr, size_t size, size_t nmemb, struct 
fetch_connect *conn)
                 * slightly) when reading small amounts of data.
                 */
                if (conn->ssl != NULL)
-                       rlen = fetch_ssl_read(conn->ssl, buf, len);
+                       rlen = fetch_ssl_read(conn->ssl->ssl, buf, len);
                else
                        rlen = fetch_nonssl_read(conn->sd, buf, len);
                if (rlen == 0) {
@@ -412,9 +496,10 @@ fetch_read(void *ptr, size_t size, size_t nmemb, struct 
fetch_connect *conn)
                        buf += rlen;
                        total += rlen;
                        continue;
+               } else if (rlen == FETCH_READ_INTR) {
+                       fetch_cache_data(conn, start, total);
+                       return -1;
                } else if (rlen == FETCH_READ_ERROR) {
-                       if (errno == EINTR)
-                               fetch_cache_data(conn, start, total);
                        return -1;
                }
                FD_ZERO(&readfds);
@@ -440,6 +525,23 @@ fetch_read(void *ptr, size_t size, size_t nmemb, struct 
fetch_connect *conn)
        return total;
 }
 
+size_t
+fetch_read(void *ptr, size_t size, size_t nmemb, struct fetch_connect *conn)
+{
+       ssize_t rlen;
+
+       rlen = fetch_read1(ptr, size, nmemb, conn);
+       if (rlen == -1) {
+               conn->iserr = 1;
+               return 0;
+       }
+       if (rlen == 0) {
+               conn->iseof = 1;
+               return 0;
+       }
+       return rlen;
+}
+
 #define MIN_BUF_SIZE 1024
 
 /*
@@ -449,8 +551,7 @@ char *
 fetch_getln(char *str, int size, struct fetch_connect *conn)
 {
        size_t tmpsize;
-       ssize_t len;
-       char c;
+       int c;
 
        if (conn->buf == NULL) {
                if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
@@ -471,14 +572,11 @@ fetch_getln(char *str, int size, struct fetch_connect 
*conn)
        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;
+               c = fetch_getc(conn);
+               if (c == EOF) {
+                       if (fetch_error(conn))
+                               return NULL;
+                       break;  /* EOF */
                }
                conn->buf[conn->buflen++] = c;
                if (conn->buflen == conn->bufsize) {
@@ -530,7 +628,7 @@ fetch_getline(struct fetch_connect *conn, char *buf, size_t 
buflen,
        } else if (len == buflen - 1) { /* line too long */
                while (1) {
                        char c;
-                       ssize_t rlen = fetch_read(&c, sizeof(c), 1, conn);
+                       ssize_t rlen = fetch_read1(&c, 1, sizeof(c), conn);
                        if (rlen <= 0 || c == '\n')
                                break;
                }
@@ -544,9 +642,54 @@ fetch_getline(struct fetch_connect *conn, char *buf, 
size_t buflen,
        return len;
 }
 
-void *
+int
+fetch_getc(struct fetch_connect *conn)
+{
+       ssize_t rlen;
+       char c;
+
+       rlen = fetch_read1(&c, 1, sizeof(c), conn);
+       if (rlen == -1) {
+               conn->iserr = 1;
+               return EOF;
+       }
+       if (rlen == 0) {
+               conn->iseof = 1;
+               return EOF;
+       }
+       return c;
+}
+
+int
+fetch_putc(int c, struct fetch_connect *conn)
+{
+       char buf[1];
+       int r;
+
+       buf[0] = c;
+       r = fetch_write(buf, 1, 1, conn);
+       if (r < 0)
+               return EOF;
+       return c;
+}
+
+ssize_t
+fetch_send(struct fetch_connect *conn, const void *msg, size_t len, int flags)
+{
+       struct iovec iov[1];
+
+       if (conn->ssl == NULL)
+               return send(fetch_fileno(conn), msg, len, flags);
+
+       iov[0].iov_base = __UNCONST(msg);
+       iov[0].iov_len = len;
+       return fetch_writev(conn, iov, 1);
+}
+
+struct fetch_ssl *
 fetch_start_ssl(int sock)
 {
+       struct fetch_ssl *fssl;
        SSL *ssl;
        SSL_CTX *ctx;
        int ret, ssl_err;
@@ -562,9 +705,17 @@ fetch_start_ssl(int sock)
        ctx = SSL_CTX_new(SSLv23_client_method());
        SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
 
+       fssl = calloc(1, sizeof(*fssl));
+       if (fssl == NULL) {
+               fprintf(ttyout, "SSL memory allocation failed\n");
+               SSL_CTX_free(ctx);
+               return NULL;
+       }
+
        ssl = SSL_new(ctx);
        if (ssl == NULL){
                fprintf(ttyout, "SSL context creation failed\n");
+               free(fssl);
                SSL_CTX_free(ctx);
                return NULL;
        }
@@ -575,6 +726,7 @@ fetch_start_ssl(int sock)
                    ssl_err != SSL_ERROR_WANT_WRITE) {
                        ERR_print_errors_fp(ttyout);
                        SSL_free(ssl);
+                       free(fssl);
                        return NULL;
                }
        }
@@ -597,12 +749,39 @@ fetch_start_ssl(int sock)
                free(str);
        }
 
-       return ssl;
+       fssl->ssl = ssl;
+       fssl->refcnt = 0;
+       return fssl;
+}
+
+void
+fetch_stop_ssl(struct fetch_ssl *ssl)
+{
+
+       if (ssl != NULL) {
+               SSL_free(ssl->ssl);
+               free(ssl);
+       }
 }
 
+void
+fetch_set_ssl(struct fetch_connect *conn, struct fetch_ssl *ssl)
+{
+
+       if (ssl != NULL) {
+               ssl->refcnt++;
+               conn->ssl = ssl;
+       }
+}
 
 void
-fetch_set_ssl(struct fetch_connect *conn, void *ssl)
+fetch_free_ssl(struct fetch_connect *conn)
 {
-       conn->ssl = ssl;
+
+       if (conn != NULL && conn->ssl != NULL) {
+               if (--conn->ssl->refcnt <= 0) {
+                       fetch_stop_ssl(conn->ssl);
+                       conn->ssl = NULL;
+               }
+       }
 }
diff --git a/ssl.h b/ssl.h
index 9ecc759..fc872a5 100644
--- a/ssl.h
+++ b/ssl.h
@@ -25,38 +25,60 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  */
+
+#ifndef        FTP_SSL_H
+#define        FTP_SSL_H
+
 #ifdef WITH_SSL
 
+#include <stdarg.h>
+
 #define FETCH struct fetch_connect
 struct fetch_connect;
+struct fetch_ssl;
 
-int fetch_printf(struct fetch_connect *, const char *fmt, ...);
+int fetch_printf(struct fetch_connect *, const char *, ...);
+int fetch_vprintf(struct fetch_connect *, const char *, va_list);
 int fetch_fileno(struct fetch_connect *);
 int fetch_error(struct fetch_connect *);
+int fetch_eof(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 *);
+size_t fetch_write(void *, size_t, size_t, struct fetch_connect *);
+size_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);
+int fetch_getc(struct fetch_connect *);
+int fetch_putc(int, struct fetch_connect *);
+ssize_t fetch_send(struct fetch_connect *, const void *, size_t, int);
+void fetch_set_ssl(struct fetch_connect *, struct fetch_ssl *);
+void fetch_free_ssl(struct fetch_connect *);
+struct fetch_ssl *fetch_start_ssl(int);
+void fetch_stop_ssl(struct fetch_ssl *);
 
 #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 */
+#define        fetch_printf            fprintf
+#define        fetch_vprintf           vfprintf
+#define        fetch_fileno            fileno
+#define        fetch_error             ferror
+#define        fetch_eof               feof
+#define        fetch_flush             fflush
+#define        fetch_open              fopen
+#define        fetch_fdopen            fdopen
+#define        fetch_close             fclose
+#define        fetch_write             fwrite
+#define        fetch_read              fread
+#define        fetch_getln             fgets
+#define        fetch_getline           get_line
+#define        fetch_getc              getc
+#define        fetch_putc              putc
+#define        fetch_send(f,m,l,fl)    send(fileno((f)),(m),(l),(fl))
+
+#endif /* WITH_SSL */
+
+#endif /* FTP_SSL_H */
diff --git a/util.c b/util.c
index ee610c6..976ca3b 100644
--- a/util.c
+++ b/util.c
@@ -124,6 +124,10 @@ setpeer(int argc, char *argv[])
        }
        if (gatemode)
                port = gateport;
+#ifdef WITH_SSL
+       else if (ftpssl)
+               port = ftpsport;
+#endif
        else
                port = ftpport;
        if (argc > 2)
@@ -184,6 +188,14 @@ parse_feat(const char *fline)
                features[FEAT_SIZE] = 1;
        else if (strcasecmp(fline, "TVFS") == 0)
                features[FEAT_TVFS] = 1;
+#ifdef WITH_SSL
+       else if (strcasecmp(fline, "PBSZ") == 0)
+               features[FEAT_PBSZ] = 1;
+       else if (strcasecmp(fline, "PROT") == 0)
+               features[FEAT_PROT] = 1;
+       else if (strcasecmp(fline, "AUTH TLS") == 0)
+               features[FEAT_AUTH_TLS] = 1;
+#endif
 }
 
 /*
@@ -266,6 +278,11 @@ getremoteinfo(void)
                DEBUG_FEAT(FEAT_REST_STREAM);
                DEBUG_FEAT(FEAT_SIZE);
                DEBUG_FEAT(FEAT_TVFS);
+#ifdef WITH_SSL
+               DEBUG_FEAT(FEAT_PBSZ);
+               DEBUG_FEAT(FEAT_PROT);
+               DEBUG_FEAT(FEAT_AUTH_TLS);
+#endif
 #undef DEBUG_FEAT
        }
 #endif
@@ -285,7 +302,7 @@ cleanuppeer(void)
 {
 
        if (cout)
-               (void)fclose(cout);
+               (void)fetch_close(cout);
        cout = NULL;
        connected = 0;
        unix_server = 0;
@@ -333,8 +350,8 @@ lostpeer(int dummy)
        alarmtimer(0);
        if (connected) {
                if (cout != NULL) {
-                       (void)shutdown(fileno(cout), 1+1);
-                       (void)fclose(cout);
+                       (void)shutdown(fetch_fileno(cout), 1+1);
+                       (void)fetch_close(cout);
                        cout = NULL;
                }
                if (data >= 0) {
@@ -347,8 +364,8 @@ lostpeer(int dummy)
        pswitch(1);
        if (connected) {
                if (cout != NULL) {
-                       (void)shutdown(fileno(cout), 1+1);
-                       (void)fclose(cout);
+                       (void)shutdown(fetch_fileno(cout), 1+1);
+                       (void)fetch_close(cout);
                        cout = NULL;
                }
                connected = 0;
@@ -372,6 +389,11 @@ ftp_login(const char *host, const char *luser, const char 
*lpass)
        char emptypass[] = "";
        const char *errormsg;
        int n, aflag, rval, nlen;
+#ifdef WITH_SSL
+       static const char *sslprot[] = { "TLS", "SSL" };
+       struct fetch_ssl *ssl;
+       size_t i;
+#endif
 
        aflag = rval = 0;
        fuser = pass = facct = NULL;
@@ -429,7 +451,34 @@ ftp_login(const char *host, const char *luser, const char 
*lpass)
                fuser = nuser;
        }
 
-       n = command("USER %s", fuser);
+       n = COMPLETE;
+#ifdef WITH_SSL
+       if (ftpssl && ftps_explicit) {
+               for (i = 0; i < sizeof(sslprot) / sizeof(sslprot[0]); i++) {
+                       n = command("AUTH %s", sslprot[i]);
+                       if (n == COMPLETE) {
+                               ssl = fetch_start_ssl(fetch_fileno(cout));
+                               if (ssl != NULL) {
+                                       fetch_set_ssl(cin, ssl);
+                                       fetch_set_ssl(cout, ssl);
+                               } else
+                                       n = ERROR;
+                               break;
+                       } else if (n != TRANSIENT && n != ERROR) {
+                               n = ERROR;
+                               break;
+                       }
+               }
+               if (n == COMPLETE) {
+                       n = command("PBSZ 0");
+                       if (n == COMPLETE) {
+                               n = command("PROT P");
+                       }
+               }
+       }
+#endif
+       if (n == COMPLETE)
+               n = command("USER %s", fuser);
        if (n == CONTINUE) {
                if (pass == NULL) {
                        p = getpass("Password: ");
-- 
1.8.2.3






Home | Main Index | Thread Index | Old Index