Module Name: src Committed By: martin Date: Mon Sep 12 17:08:13 UTC 2022
Modified Files: src/usr.bin/ftp [netbsd-8]: Makefile cmds.c complete.c domacro.c extern.h fetch.c ftp.1 ftp.c ftp_var.h main.c progressbar.c progressbar.h ssl.c ssl.h util.c version.h Log Message: Catch up to current, requested by christos in ticket #1763: usr.bin/ftp/Makefile up to 1.39 usr.bin/ftp/cmds.c up to 1.141 usr.bin/ftp/complete.c up to 1.47 usr.bin/ftp/domacro.c up to 1.23 usr.bin/ftp/extern.h up to 1.82 usr.bin/ftp/fetch.c up to 1.235 usr.bin/ftp/ftp.1 up to 1.147 usr.bin/ftp/ftp.c up to 1.174 usr.bin/ftp/ftp_var.h up to 1.86 usr.bin/ftp/main.c up to 1.128 usr.bin/ftp/progressbar.c up to 1.24 usr.bin/ftp/progressbar.h up to 1.9 usr.bin/ftp/ssl.c up to 1.12 usr.bin/ftp/ssl.h up to 1.5 usr.bin/ftp/util.c up to 1.164 usr.bin/ftp/version.h up to 1.94 ftp(1): validate address from PASV and LPSV response. ftp(1): use raw write(2) instead of fwrite(3) to avoid stream corruption because of the progress bar interrupts. Fixes for PR 56219 and PR 55857. PR 57003: Support relative redirects. To generate a diff of this commit: cvs rdiff -u -r1.37.2.2 -r1.37.2.3 src/usr.bin/ftp/Makefile cvs rdiff -u -r1.137.8.2 -r1.137.8.3 src/usr.bin/ftp/cmds.c cvs rdiff -u -r1.46.38.2 -r1.46.38.3 src/usr.bin/ftp/complete.c cvs rdiff -u -r1.22.38.2 -r1.22.38.3 src/usr.bin/ftp/domacro.c cvs rdiff -u -r1.80.24.2 -r1.80.24.3 src/usr.bin/ftp/extern.h cvs rdiff -u -r1.228.4.2 -r1.228.4.3 src/usr.bin/ftp/fetch.c cvs rdiff -u -r1.135.8.2 -r1.135.8.3 src/usr.bin/ftp/ftp.1 cvs rdiff -u -r1.167.6.2 -r1.167.6.3 src/usr.bin/ftp/ftp.c cvs rdiff -u -r1.84.8.2 -r1.84.8.3 src/usr.bin/ftp/ftp_var.h cvs rdiff -u -r1.123.8.2 -r1.123.8.3 src/usr.bin/ftp/main.c cvs rdiff -u -r1.22.24.2 -r1.22.24.3 src/usr.bin/ftp/progressbar.c cvs rdiff -u -r1.8.38.2 -r1.8.38.3 src/usr.bin/ftp/progressbar.h cvs rdiff -u -r1.5.8.2 -r1.5.8.3 src/usr.bin/ftp/ssl.c cvs rdiff -u -r1.3.8.2 -r1.3.8.3 src/usr.bin/ftp/ssl.h cvs rdiff -u -r1.158.22.2 -r1.158.22.3 src/usr.bin/ftp/util.c cvs rdiff -u -r1.87.8.2 -r1.87.8.3 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.37.2.2 src/usr.bin/ftp/Makefile:1.37.2.3 --- src/usr.bin/ftp/Makefile:1.37.2.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/Makefile Mon Sep 12 17:08:13 2022 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.37.2.2 2022/09/12 15:05:21 martin Exp $ +# $NetBSD: Makefile,v 1.37.2.3 2022/09/12 17:08:13 martin Exp $ # from: @(#)Makefile 8.2 (Berkeley) 4/3/94 .include <bsd.own.mk> @@ -8,6 +8,7 @@ USE_FORT?= yes # network client PROG= ftp SRCS= cmds.c cmdtab.c complete.c domacro.c fetch.c ftp.c main.c \ progressbar.c ruserpass.c util.c +SRCS+= ssl.c # Uncomment the following to provide defaults for gate-ftp operation # @@ -19,7 +20,6 @@ CPPFLAGS+=-DNO_EDITCOMPLETE -DNO_ABOUT - LDADD+= -ledit -lterminfo DPADD+= ${LIBEDIT} ${LIBTERMINFO} CPPFLAGS+= -DWITH_SSL -SRCS+=ssl.c LDADD+= -lssl -lcrypto DPADD+= ${LIBSSL} ${LIBCRYPTO} .endif @@ -31,4 +31,6 @@ CPPFLAGS+= -DINET6 cmds.o fetch.o: version.h main.o: ftp_var.h +CWARNFLAGS.gcc+= ${GCC_NO_FORMAT_OVERFLOW} + .include <bsd.prog.mk> Index: src/usr.bin/ftp/cmds.c diff -u src/usr.bin/ftp/cmds.c:1.137.8.2 src/usr.bin/ftp/cmds.c:1.137.8.3 --- src/usr.bin/ftp/cmds.c:1.137.8.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/cmds.c Mon Sep 12 17:08:13 2022 @@ -1,7 +1,7 @@ -/* $NetBSD: cmds.c,v 1.137.8.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: cmds.c,v 1.137.8.3 2022/09/12 17:08:13 martin Exp $ */ /*- - * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. + * Copyright (c) 1996-2021 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -96,7 +96,7 @@ #if 0 static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94"; #else -__RCSID("$NetBSD: cmds.c,v 1.137.8.2 2022/09/12 15:05:21 martin Exp $"); +__RCSID("$NetBSD: cmds.c,v 1.137.8.3 2022/09/12 17:08:13 martin Exp $"); #endif #endif /* not lint */ @@ -1131,7 +1131,7 @@ setdebug(int argc, char *argv[]) options |= SO_DEBUG; else options &= ~SO_DEBUG; - fprintf(ttyout, "Debugging %s (ftp_debug=%d).\n", onoff(ftp_debug), ftp_debug); + fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(ftp_debug), ftp_debug); code = ftp_debug > 0; } @@ -1158,7 +1158,8 @@ cd(int argc, char *argv[]) } if (r == COMPLETE) { dirchange = 1; - updateremotecwd(); + remotecwd[0] = '\0'; + remcwdvalid = 0; } } @@ -1544,9 +1545,9 @@ pwd(int argc, char *argv[]) UPRINTF("usage: %s\n", argv[0]); return; } - if (! remotecwd[0]) + if (!remcwdvalid || remotecwd[0] == '\0') updateremotecwd(); - if (! remotecwd[0]) + if (remotecwd[0] == '\0') fprintf(ttyout, "Unable to determine remote directory\n"); else { fprintf(ttyout, "Remote directory: %s\n", remotecwd); @@ -1775,6 +1776,18 @@ quit(int argc, char *argv[]) exit(0); } +void __dead +justquit(void) +{ + + quit(0, NULL); + /* + * quit is not __dead, but for our invocation it never will return, + * but some compilers are not smart enough to find this out. + */ + exit(0); +} + /* * Terminate session, but don't exit. * May be called with 0, NULL. @@ -2184,7 +2197,7 @@ LOOP: } break; } - /* intentional drop through */ + /* FALLTHROUGH */ default: *cp2++ = *cp1; break; @@ -2359,7 +2372,8 @@ cdup(int argc, char *argv[]) } if (r == COMPLETE) { dirchange = 1; - updateremotecwd(); + remotecwd[0] = '\0'; + remcwdvalid = 0; } } Index: src/usr.bin/ftp/complete.c diff -u src/usr.bin/ftp/complete.c:1.46.38.2 src/usr.bin/ftp/complete.c:1.46.38.3 --- src/usr.bin/ftp/complete.c:1.46.38.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/complete.c Mon Sep 12 17:08:13 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: complete.c,v 1.46.38.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: complete.c,v 1.46.38.3 2022/09/12 17:08:13 martin Exp $ */ /*- * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: complete.c,v 1.46.38.2 2022/09/12 15:05:21 martin Exp $"); +__RCSID("$NetBSD: complete.c,v 1.46.38.3 2022/09/12 17:08:13 martin Exp $"); #endif /* not lint */ /* @@ -99,11 +99,10 @@ complete_ambiguous(char *word, int list, } if (!list) { - matchlen = 0; lastmatch = words->sl_str[0]; matchlen = strlen(lastmatch); for (i = 1 ; i < words->sl_cur ; i++) { - for (j = wordlen ; j < strlen(words->sl_str[i]); j++) + for (j = wordlen; j < strlen(words->sl_str[i]); j++) if (lastmatch[j] != words->sl_str[i][j]) break; if (j < matchlen) Index: src/usr.bin/ftp/domacro.c diff -u src/usr.bin/ftp/domacro.c:1.22.38.2 src/usr.bin/ftp/domacro.c:1.22.38.3 --- src/usr.bin/ftp/domacro.c:1.22.38.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/domacro.c Mon Sep 12 17:08:13 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: domacro.c,v 1.22.38.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: domacro.c,v 1.22.38.3 2022/09/12 17:08:13 martin Exp $ */ /* * Copyright (c) 1985, 1993, 1994 @@ -34,7 +34,7 @@ #if 0 static char sccsid[] = "@(#)domacro.c 8.3 (Berkeley) 4/2/94"; #else -__RCSID("$NetBSD: domacro.c,v 1.22.38.2 2022/09/12 15:05:21 martin Exp $"); +__RCSID("$NetBSD: domacro.c,v 1.22.38.3 2022/09/12 17:08:13 martin Exp $"); #endif #endif /* not lint */ @@ -102,7 +102,7 @@ domacro(int argc, char *argv[]) } break; } - /* intentional drop through */ + /* FALLTHROUGH */ default: *cp2++ = *cp1; break; Index: src/usr.bin/ftp/extern.h diff -u src/usr.bin/ftp/extern.h:1.80.24.2 src/usr.bin/ftp/extern.h:1.80.24.3 --- src/usr.bin/ftp/extern.h:1.80.24.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/extern.h Mon Sep 12 17:08:13 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: extern.h,v 1.80.24.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: extern.h,v 1.80.24.3 2022/09/12 17:08:13 martin Exp $ */ /*- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. @@ -173,6 +173,7 @@ void pswitch(int); void put(int, char **); void pwd(int, char **); void quit(int, char **); +void justquit(void) __dead; void quote(int, char **); void quote1(const char *, int, char **); void recvrequest(const char *, const char *, const char *, @@ -242,7 +243,14 @@ void user(int, char **); int ftp_connect(int, const struct sockaddr *, socklen_t, int); int ftp_listen(int, int); int ftp_poll(struct pollfd *, int, int); +#ifndef SMALL void *ftp_malloc(size_t); StringList *ftp_sl_init(void); void ftp_sl_add(StringList *, char *); char *ftp_strdup(const char *); +#else +#define ftp_malloc(a) malloc(a); +#define ftp_sl_init() sl_init() +#define ftp_sl_add(a, b) sl_add((a), (b)) +#define ftp_strdup(a) strdup(a) +#endif Index: src/usr.bin/ftp/fetch.c diff -u src/usr.bin/ftp/fetch.c:1.228.4.2 src/usr.bin/ftp/fetch.c:1.228.4.3 --- src/usr.bin/ftp/fetch.c:1.228.4.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/fetch.c Mon Sep 12 17:08:13 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: fetch.c,v 1.228.4.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: fetch.c,v 1.228.4.3 2022/09/12 17:08:13 martin Exp $ */ /*- * Copyright (c) 1997-2015 The NetBSD Foundation, Inc. @@ -37,7 +37,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: fetch.c,v 1.228.4.2 2022/09/12 15:05:21 martin Exp $"); +__RCSID("$NetBSD: fetch.c,v 1.228.4.3 2022/09/12 17:08:13 martin Exp $"); #endif /* not lint */ /* @@ -106,12 +106,13 @@ __dead static void timeouthttp(int); static int auth_url(const char *, char **, const struct authinfo *); static void base64_encode(const unsigned char *, size_t, unsigned char *); #endif -static int go_fetch(const char *); +static int go_fetch(const char *, struct urlinfo *); static int fetch_ftp(const char *); -static int fetch_url(const char *, const char *, char *, char *); +static int fetch_url(const char *, const char *, char *, char *, + struct urlinfo *); static const char *match_token(const char **, const char *); static int parse_url(const char *, const char *, struct urlinfo *, - struct authinfo *); + struct authinfo *, struct urlinfo *); static void url_decode(char *); static void freeauthinfo(struct authinfo *); static void freeurlinfo(struct urlinfo *); @@ -138,6 +139,43 @@ static int redirect_loop; ((urltype) == HTTP_URL_T) #endif +/** + * fwrite(3) replacement that just uses write(2). Many stdio implementations + * don't handle interrupts properly and corrupt the output. We are taking + * alarm interrupts because of the progress bar. + * + * Assumes `fp' is pristine with no prior I/O calls on it. + */ +static size_t +maxwrite(const void *buf, size_t size, size_t nmemb, FILE *fp) +{ + const char *p = buf; + ssize_t nwr = 0; + ssize_t n; + int fd = fileno(fp); + + size *= nmemb; /* assume no overflow */ + + while (size > 0) { + if ((n = write(fd, p, size)) == -1) { + switch (errno) { + case EINTR: + case EAGAIN: +#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + continue; + default: + return nwr; + } + } + p += n; + nwr += n; + size -= n; + } + return nwr; +} + /* * Determine if token is the next word in buf (case insensitive). * If so, advance buf past the token and any trailing LWS, and @@ -237,7 +275,7 @@ auth_url(const char *challenge, char **r scheme = "Basic"; /* only support Basic authentication */ gotpass = NULL; - DPRINTF("auth_url: challenge `%s'\n", challenge); + DPRINTF("%s: challenge `%s'\n", __func__, challenge); if (! match_token(&cp, scheme)) { warnx("Unsupported authentication challenge `%s'", @@ -299,7 +337,7 @@ auth_url(const char *challenge, char **r *response = ftp_malloc(rlen); (void)strlcpy(*response, scheme, rlen); len = strlcat(*response, " ", rlen); - /* use `clen - 1' to not encode the trailing NUL */ + /* use `clen - 1' to not encode the trailing NUL */ base64_encode((unsigned char *)clear, clen - 1, (unsigned char *)*response + len); memset(clear, 0, clen); @@ -330,7 +368,7 @@ base64_encode(const unsigned char *clear | ((clear[i + 1] >> 4) & 0x0f)]; *(cp++) = enc[((clear[i + 1] << 2) & 0x3c) | ((clear[i + 2] >> 6) & 0x03)]; - *(cp++) = enc[((clear[i + 2] ) & 0x3f)]; + *(cp++) = enc[((clear[i + 2] ) & 0x3f)]; } *cp = '\0'; while (i-- > len) @@ -363,6 +401,42 @@ url_decode(char *url) *q = '\0'; } +static const char * +get_port(const struct urlinfo *ui) +{ + + switch(ui->utype) { + case HTTP_URL_T: + return httpport; + case FTP_URL_T: + return ftpport; + case FILE_URL_T: + return ""; +#ifdef WITH_SSL + case HTTPS_URL_T: + return httpsport; +#endif + default: + return NULL; + } +} + +static int +use_relative(const struct urlinfo *ui) +{ + if (ui == NULL) + return 0; + switch (ui->utype) { + case HTTP_URL_T: + case FILE_URL_T: +#ifdef WITH_SSL + case HTTPS_URL_T: +#endif + return 1; + default: + return 0; + } +} /* * Parse URL of form (per RFC 3986): @@ -398,7 +472,7 @@ url_decode(char *url) static int parse_url(const char *url, const char *desc, struct urlinfo *ui, - struct authinfo *auth) + struct authinfo *auth, struct urlinfo *rui) { const char *origurl, *tport; char *cp, *ep, *thost; @@ -409,29 +483,26 @@ parse_url(const char *url, const char *d DPRINTF("parse_url: %s `%s'\n", desc, url); origurl = url; - tport = NULL; if (STRNEQUAL(url, HTTP_URL)) { url += sizeof(HTTP_URL) - 1; ui->utype = HTTP_URL_T; ui->portnum = HTTP_PORT; - tport = httpport; } else if (STRNEQUAL(url, FTP_URL)) { url += sizeof(FTP_URL) - 1; ui->utype = FTP_URL_T; ui->portnum = FTP_PORT; - tport = ftpport; } else if (STRNEQUAL(url, FILE_URL)) { url += sizeof(FILE_URL) - 1; ui->utype = FILE_URL_T; - tport = ""; #ifdef WITH_SSL } else if (STRNEQUAL(url, HTTPS_URL)) { url += sizeof(HTTPS_URL) - 1; ui->utype = HTTPS_URL_T; ui->portnum = HTTPS_PORT; - tport = httpsport; #endif + } else if (rui != NULL) { + copyurlinfo(ui, rui); } else { warnx("Invalid %s `%s'", desc, url); cleanup_parse_url: @@ -440,6 +511,7 @@ parse_url(const char *url, const char *d return (-1); } + if (*url == '\0') return (0); @@ -504,7 +576,8 @@ parse_url(const char *url, const char *d #endif /* INET6 */ if ((cp = strchr(thost, ':')) != NULL) *cp++ = '\0'; - ui->host = thost; + if (*thost != '\0') + ui->host = thost; /* look for [:port] */ if (cp != NULL) { @@ -519,7 +592,9 @@ parse_url(const char *url, const char *d } ui->portnum = nport; tport = cp; - } + } else + tport = get_port(ui); + if (tport != NULL) ui->port = ftp_strdup(tport); @@ -530,8 +605,8 @@ parse_url(const char *url, const char *d ui->path = ftp_strdup(emptypath); } - DPRINTF("parse_url: user `%s' pass `%s' host %s port %s(%d) " - "path `%s'\n", + DPRINTF("%s: user `%s' pass `%s' host %s port %s(%d) " + "path `%s'\n", __func__, STRorNULL(auth->user), STRorNULL(auth->pass), STRorNULL(ui->host), STRorNULL(ui->port), ui->portnum ? ui->portnum : -1, STRorNULL(ui->path)); @@ -544,7 +619,7 @@ sigjmp_buf httpabort; static int ftp_socket(const struct urlinfo *ui, void **ssl) { - struct addrinfo hints, *res, *res0 = NULL; + struct addrinfo hints, *res, *res0 = NULL; int error; int s; const char *host = ui->host; @@ -649,7 +724,7 @@ handle_noproxy(const char *host, in_port if (*cp == '\0') continue; if ((np = strrchr(cp, ':')) != NULL) { - *np++ = '\0'; + *np++ = '\0'; np_port = strtoul(np, &ep, 10); if (*np == '\0' || *ep != '\0') continue; @@ -681,7 +756,7 @@ handle_proxy(const char *url, const char } initurlinfo(&pui); - if (parse_url(penv, "proxy URL", &pui, pauth) == -1) + if (parse_url(penv, "proxy URL", &pui, pauth, NULL) == -1) return -1; if ((!IS_HTTP_TYPE(pui.utype) && pui.utype != FTP_URL_T) || @@ -852,9 +927,9 @@ print_connect(FETCH *fin, const struct u } #endif -#define C_OK 0 -#define C_CLEANUP 1 -#define C_IMPROPER 2 +#define C_OK 0 +#define C_CLEANUP 1 +#define C_IMPROPER 2 static int getresponseline(FETCH *fin, char *buf, size_t buflen, int *len) @@ -953,7 +1028,7 @@ parse_posinfo(const char **cp, struct po static void do_auth(int hcode, const char *url, const char *penv, struct authinfo *wauth, struct authinfo *pauth, char **auth, const char *message, - volatile int *rval) + volatile int *rval, struct urlinfo *ui) { struct authinfo aauth; char *response; @@ -988,7 +1063,8 @@ do_auth(int hcode, const char *url, cons if (auth_url(*auth, &response, &aauth) == 0) { *rval = fetch_url(url, penv, hcode == 401 ? pauth->auth : response, - hcode == 401 ? response: wauth->auth); + hcode == 401 ? response : wauth->auth, + ui); memset(response, 0, strlen(response)); FREEPTR(response); } @@ -999,12 +1075,12 @@ static int negotiate_connection(FETCH *fin, const char *url, const char *penv, struct posinfo *pi, time_t *mtime, struct authinfo *wauth, struct authinfo *pauth, volatile int *rval, volatile int *ischunked, - char **auth) + char **auth, struct urlinfo *ui) { int len, hcode, rv; char buf[FTPBUFLEN], *ep; const char *cp, *token; - char *location, *message; + char *location, *message; *auth = message = location = NULL; @@ -1119,18 +1195,19 @@ negotiate_connection(FETCH *fin, const c fprintf(ttyout, "Redirected via %s\n", location); *rval = fetch_url(url, location, - pauth->auth, wauth->auth); + pauth->auth, wauth->auth, ui); } else { if (verbose) fprintf(ttyout, "Redirected to %s\n", location); - *rval = go_fetch(location); + *rval = go_fetch(location, ui); } goto cleanup_fetch_url; #ifndef NO_AUTH case 401: case 407: - do_auth(hcode, url, penv, wauth, pauth, auth, message, rval); + do_auth(hcode, url, penv, wauth, pauth, auth, message, rval, + ui); goto cleanup_fetch_url; #endif default: @@ -1195,7 +1272,7 @@ connectmethod(FETCH *fin, const char *ur message = ftp_strdup(ep); break; } - + for (;;) { int len; if (getresponseline(fin, buf, sizeof(buf), &len) != C_OK) @@ -1224,7 +1301,8 @@ connectmethod(FETCH *fin, const char *ur break; #ifndef NO_AUTH case 407: - do_auth(hcode, url, penv, wauth, pauth, auth, message, rval); + do_auth(hcode, url, penv, wauth, pauth, auth, message, rval, + ui); goto cleanup_fetch_url; #endif default: @@ -1262,7 +1340,8 @@ out: * is still open (e.g, ftp xfer with trailing /) */ static int -fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) +fetch_url(const char *url, const char *proxyenv, char *proxyauth, + char *wwwauth, struct urlinfo *rui) { sigfunc volatile oldint; sigfunc volatile oldpipe; @@ -1271,18 +1350,18 @@ fetch_url(const char *url, const char *p int volatile s; struct stat sb; int volatile isproxy; - int volatile rval, ischunked; + int volatile rval, ischunked; size_t flen; static size_t bufsize; static char *xferbuf; const char *cp; char *ep; - char *auth; + char *volatile auth; char *volatile savefile; char *volatile location; char *volatile message; char *volatile decodedpath; - struct authinfo wauth, pauth; + struct authinfo wauth, pauth; struct posinfo pi; off_t hashbytes; int (*volatile closefunc)(FILE *); @@ -1295,7 +1374,7 @@ fetch_url(const char *url, const char *p DPRINTF("%s: `%s' proxyenv `%s'\n", __func__, url, STRorNULL(penv)); - oldquit = oldalrm = oldint = oldpipe = NULL; + oldquit = oldalrm = oldint = oldpipe = SIG_ERR; closefunc = NULL; fin = NULL; fout = NULL; @@ -1315,7 +1394,7 @@ fetch_url(const char *url, const char *p if (sigsetjmp(httpabort, 1)) goto cleanup_fetch_url; - if (parse_url(url, "URL", &ui, &wauth) == -1) + if (parse_url(url, "URL", &ui, &wauth, rui) == -1) goto cleanup_fetch_url; copyurlinfo(&oui, &ui); @@ -1331,7 +1410,7 @@ fetch_url(const char *url, const char *p rval = fetch_ftp(url); goto cleanup_fetch_url; } - if (!IS_HTTP_TYPE(ui.utype) || outfile == NULL) { + if (!IS_HTTP_TYPE(ui.utype) || outfile == NULL) { warnx("Invalid URL (no file after host) `%s'", url); goto cleanup_fetch_url; } @@ -1386,7 +1465,8 @@ fetch_url(const char *url, const char *p filesize = sb.st_size; } if (restart_point) { - if (lseek(fetch_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; @@ -1460,7 +1540,8 @@ fetch_url(const char *url, const char *p #ifdef WITH_SSL if (isproxy && oui.utype == HTTPS_URL_T) { switch (connectmethod(fin, url, penv, &oui, &ui, - &wauth, &pauth, &auth, &hasleading, &rval)) { + &wauth, &pauth, __UNVOLATILE(&auth), &hasleading, + &rval)) { case C_CLEANUP: goto cleanup_fetch_url; case C_IMPROPER: @@ -1496,7 +1577,8 @@ fetch_url(const char *url, const char *p alarmtimer(0); switch (negotiate_connection(fin, url, penv, &pi, - &mtime, &wauth, &pauth, &rval, &ischunked, &auth)) { + &mtime, &wauth, &pauth, &rval, &ischunked, + __UNVOLATILE(&auth), &ui)) { case C_OK: break; case C_CLEANUP: @@ -1570,9 +1652,9 @@ fetch_url(const char *url, const char *p bytes = 0; hashbytes = mark; - if (oldalrm) { + if (oldalrm != SIG_ERR) { (void)xsignal(SIGALRM, oldalrm); - oldalrm = NULL; + oldalrm = SIG_ERR; } progressmeter(-1); @@ -1603,7 +1685,7 @@ fetch_url(const char *url, const char *p } /* - * XXX: Work around bug in Apache 1.3.9 and + * XXX: Work around bug in Apache 1.3.9 and * 1.3.11, which incorrectly put trailing * space after the chunk-size. */ @@ -1638,13 +1720,17 @@ fetch_url(const char *url, const char *p if (ischunked) bufrem = MIN(chunksize, bufrem); while (bufrem > 0) { + size_t nr = MIN((off_t)bufsize, bufrem); flen = fetch_read(xferbuf, sizeof(char), - MIN((off_t)bufsize, bufrem), fin); - if (flen <= 0) + nr, fin); + if (flen == 0) { + if (fetch_error(fin)) + goto chunkerror; goto chunkdone; + } bytes += flen; bufrem -= flen; - if (fwrite(xferbuf, sizeof(char), flen, fout) + if (maxwrite(xferbuf, sizeof(char), flen, fout) != flen) { warn("Writing `%s'", savefile); goto cleanup_fetch_url; @@ -1692,7 +1778,7 @@ fetch_url(const char *url, const char *p } while (ischunked); /* XXX: deal with optional trailer & CRLF here? */ - +chunkerror: if (hash && !progress && bytes > 0) { if (bytes < mark) (void)putc('#', ttyout); @@ -1730,14 +1816,14 @@ fetch_url(const char *url, const char *p warnx("Improper response from `%s:%s'", ui.host, ui.port); cleanup_fetch_url: - if (oldint) + if (oldint != SIG_ERR) (void)xsignal(SIGINT, oldint); - if (oldpipe) + if (oldpipe != SIG_ERR) (void)xsignal(SIGPIPE, oldpipe); - if (oldalrm) + if (oldalrm != SIG_ERR) (void)xsignal(SIGALRM, oldalrm); - if (oldquit) - (void)xsignal(SIGQUIT, oldpipe); + if (oldquit != SIG_ERR) + (void)xsignal(SIGQUIT, oldquit); if (fin != NULL) fetch_close(fin); else if (s != -1) @@ -1807,10 +1893,10 @@ fetch_ftp(const char *url) char dirbuf[4]; int dirhasglob, filehasglob, rval, transtype, xargc; int oanonftp, oautologin; - struct authinfo auth; + struct authinfo auth; struct urlinfo ui; - DPRINTF("fetch_ftp: `%s'\n", url); + DPRINTF("%s: `%s'\n", __func__, url); dir = file = NULL; rval = 1; transtype = TYPE_I; @@ -1819,7 +1905,7 @@ fetch_ftp(const char *url) initauthinfo(&auth, NULL); if (STRNEQUAL(url, FTP_URL)) { - if ((parse_url(url, "URL", &ui, &auth) == -1) || + if ((parse_url(url, "URL", &ui, &auth, NULL) == -1) || (auth.user != NULL && *auth.user == '\0') || EMPTYSTRING(ui.host)) { warnx("Invalid URL `%s'", url); @@ -1831,7 +1917,8 @@ fetch_ftp(const char *url) */ /* check for trailing ';type=[aid]' */ - if (! EMPTYSTRING(ui.path) && (cp = strrchr(ui.path, ';')) != NULL) { + if (! EMPTYSTRING(ui.path) + && (cp = strrchr(ui.path, ';')) != NULL) { if (strcasecmp(cp, ";type=a") == 0) transtype = TYPE_A; else if (strcasecmp(cp, ";type=i") == 0) @@ -1873,12 +1960,12 @@ fetch_ftp(const char *url) * If we are dealing with classic `[user@]host:[path]' syntax, * then a path of the form `/file' (resulting from input of the * form `host:/file') means that we should do "CWD /" before - * retrieving the file. So we set dir="/" and file="file". + * retrieving the file. So we set dir="/" and file="file". * * But if we are dealing with URLs like `ftp://host/path' then * a path of the form `/file' (resulting from a URL of the form * `ftp://host//file') means that we should do `CWD ' (with an - * empty argument) before retrieving the file. So we set + * empty argument) before retrieving the file. So we set * dir="" and file="file". * * If the path does not contain / at all, we set dir=NULL. @@ -1909,8 +1996,8 @@ fetch_ftp(const char *url) url_decode(file); /* but still don't url_decode(dir) */ } - DPRINTF("fetch_ftp: user `%s' pass `%s' host %s port %s " - "path `%s' dir `%s' file `%s'\n", + DPRINTF("%s: user `%s' pass `%s' host %s port %s " + "path `%s' dir `%s' file `%s'\n", __func__, STRorNULL(auth.user), STRorNULL(auth.pass), STRorNULL(ui.host), STRorNULL(ui.port), STRorNULL(ui.path), STRorNULL(dir), STRorNULL(file)); @@ -1959,7 +2046,7 @@ fetch_ftp(const char *url) setbinary(1, xargv); break; default: - errx(1, "fetch_ftp: unknown transfer type %d", transtype); + errx(1, "%s: unknown transfer type %d", __func__, transtype); } /* @@ -1981,7 +2068,7 @@ fetch_ftp(const char *url) * (urltype is FTP_URL_T), then RFC 3986 says we need to * send a separate CWD command for each unescaped "/" * in the path, and we have to interpret %hex escaping - * *after* we find the slashes. It's possible to get + * *after* we find the slashes. It's possible to get * empty components here, (from multiple adjacent * slashes in the path) and RFC 3986 says that we should * still do `CWD ' (with a null argument) in such cases. @@ -2024,7 +2111,7 @@ fetch_ftp(const char *url) * "CWD /", "CWD foo", "CWD bar", "RETR file" * ftp://host/%2Ffoo/bar/file dir="%2Ffoo/bar" * "CWD /foo", "CWD bar", "RETR file" - * ftp://host/%2Ffoo%2Fbar/file dir="%2Ffoo%2Fbar" + * ftp://host/%2Ffoo%2Fbar/file dir="%2Ffoo%2Fbar" * "CWD /foo/bar", "RETR file" * ftp://host/%2Ffoo%2Fbar%2Ffile dir=NULL * "RETR /foo/bar/file" @@ -2041,7 +2128,7 @@ fetch_ftp(const char *url) url_decode(dir); } else nextpart = NULL; - DPRINTF("fetch_ftp: dir `%s', nextpart `%s'\n", + DPRINTF("%s: dir `%s', nextpart `%s'\n", __func__, STRorNULL(dir), STRorNULL(nextpart)); if (ui.utype == FTP_URL_T || *dir != '\0') { (void)strlcpy(cmdbuf, "cd", sizeof(cmdbuf)); @@ -2097,14 +2184,15 @@ fetch_ftp(const char *url) mget(xargc, xargv); interactive = ointeractive; } else { - if (outfile == NULL) { + char *destfile = outfile; + if (destfile == NULL) { cp = strrchr(file, '/'); /* find savefile */ if (cp != NULL) - outfile = cp + 1; + destfile = cp + 1; else - outfile = file; + destfile = file; } - xargv[2] = (char *)outfile; + xargv[2] = (char *)destfile; xargv[3] = NULL; xargc++; if (restartautofetch) @@ -2135,7 +2223,7 @@ fetch_ftp(const char *url) * is still open (e.g, ftp xfer with trailing /) */ static int -go_fetch(const char *url) +go_fetch(const char *url, struct urlinfo *rui) { char *proxyenv; char *p; @@ -2184,7 +2272,7 @@ go_fetch(const char *url) || STRNEQUAL(url, HTTPS_URL) #endif || STRNEQUAL(url, FILE_URL)) - return (fetch_url(url, NULL, NULL, NULL)); + return (fetch_url(url, NULL, NULL, NULL, rui)); /* * If it contains "://" but does not begin with ftp:// @@ -2199,13 +2287,20 @@ go_fetch(const char *url) errx(1, "Unsupported URL scheme `%.*s'", (int)(p - url), url); /* + * Refer to previous urlinfo if provided. This makes relative + * redirects work. + */ + if (use_relative(rui)) + return fetch_url(url, NULL, NULL, NULL, rui); + + /* * Try FTP URL-style and host:file arguments next. * If ftpproxy is set with an FTP URL, use fetch_url() - * Othewise, use fetch_ftp(). + * Otherwise, use fetch_ftp(). */ proxyenv = getoptionvalue("ftp_proxy"); if (!EMPTYSTRING(proxyenv) && STRNEQUAL(url, FTP_URL)) - return (fetch_url(url, NULL, NULL, NULL)); + return (fetch_url(url, NULL, NULL, NULL, rui)); return (fetch_ftp(url)); } @@ -2248,10 +2343,11 @@ auto_fetch(int argc, char *argv[]) redirect_loop = 0; if (!anonftp) anonftp = 2; /* Handle "automatic" transfers. */ - rval = go_fetch(argv[argpos]); + rval = go_fetch(argv[argpos], NULL); if (outfile != NULL && strcmp(outfile, "-") != 0 - && outfile[0] != '|') - outfile = NULL; + && outfile[0] != '|') { + FREEPTR(outfile); + } if (rval > 0) rval = argpos + 1; } @@ -2286,7 +2382,7 @@ auto_put(int argc, char **argv, const ch pathsep = NULL; rval = 1; - DPRINTF("auto_put: target `%s'\n", uploadserver); + DPRINTF("%s: target `%s'\n", __func__, uploadserver); path = ftp_strdup(uploadserver); len = strlen(path); @@ -2295,7 +2391,7 @@ auto_put(int argc, char **argv, const ch * make sure we always pass a directory to auto_fetch */ if (argc > 1) { /* more than one file to upload */ - len = strlen(uploadserver) + 2; /* path + "/" + "\0" */ + len = strlen(uploadserver) + 2; /* path + "/" + "\0" */ free(path); path = (char *)ftp_malloc(len); (void)strlcpy(path, uploadserver, len); @@ -2319,7 +2415,7 @@ auto_put(int argc, char **argv, const ch uargc++; } } - DPRINTF("auto_put: URL `%s' argv[2] `%s'\n", + DPRINTF("%s: URL `%s' argv[2] `%s'\n", __func__, path, STRorNULL(uargv[2])); /* connect and cwd */ Index: src/usr.bin/ftp/ftp.1 diff -u src/usr.bin/ftp/ftp.1:1.135.8.2 src/usr.bin/ftp/ftp.1:1.135.8.3 --- src/usr.bin/ftp/ftp.1:1.135.8.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/ftp.1 Mon Sep 12 17:08:13 2022 @@ -1,6 +1,6 @@ -.\" $NetBSD: ftp.1,v 1.135.8.2 2022/09/12 15:05:21 martin Exp $ +.\" $NetBSD: ftp.1,v 1.135.8.3 2022/09/12 17:08:13 martin Exp $ .\" -.\" Copyright (c) 1996-2015 The NetBSD Foundation, Inc. +.\" Copyright (c) 1996-2021 The NetBSD Foundation, Inc. .\" All rights reserved. .\" .\" This code is derived from software contributed to The NetBSD Foundation @@ -57,7 +57,7 @@ .\" .\" @(#)ftp.1 8.3 (Berkeley) 10/9/94 .\" -.Dd April 24, 2015 +.Dd August 29, 2022 .Dt FTP 1 .Os .Sh NAME @@ -65,7 +65,7 @@ .Nd Internet file transfer program .Sh SYNOPSIS .Nm -.Op Fl 46AadefginpRtVv +.Op Fl 46AadefginpRtVv? .Op Fl N Ar netrc .Op Fl o Ar output .Op Fl P Ar port @@ -84,12 +84,11 @@ .Xc .Oc .Ek -.Op Fl x Ar xferbufsize +.Op Fl x Ar xfersize .Bk -words .\" [[user@]host [port]] .Oo -.Oo Ar user Ns Li \&@ Oc Ns Ar host -.Op Ar port +.Oo Ar user Ns Li \&@ Oc Ns Ar host Oo Ar port Oc .Oc .Ek .Bk -words @@ -122,7 +121,7 @@ .Ar host Oo Li \&: Ar port Oc .Li / Ar path .Op Li / -.Op Li ;type= Ar X +.Op Li ;type= Ar type .Oc .Sm on .Ek @@ -139,12 +138,24 @@ .Oc .Sm on .Ek -.Op Ar \&.\&.\&. +.Bk -words +.\" [https://[user[:password]@]host[:port]/path] +.Sm off +.Oo +.Li https:// +.Oo Ar user +.Op Li \&: Ar password +.Li \&@ Oc +.Ar host Oo Li \&: Ar port Oc +.Li / Ar path +.Oc +.Sm on +.Ek +.Ar \&... .Nm .Bk -words -.Fl u Ar URL Ar file +.Fl u Ar url Ar .Ek -.Op Ar \&.\&.\&. .Sh DESCRIPTION .Nm is the user interface to the Internet standard File Transfer Protocol. @@ -173,10 +184,13 @@ Forces .Nm to only use IPv6 addresses. .It Fl A -Force active mode ftp. +Force active mode +.Tn FTP . By default, .Nm -will try to use passive mode ftp and fall back to active mode +will try to use passive mode +.Tn FTP +and fall back to active mode if passive is not supported by the server. This option causes .Nm @@ -284,12 +298,14 @@ bytes/second. Refer to .Ic rate for more information. -.It Fl u Ar URL file Op \&.\&.\&. +.It Fl u Ar url Ar Upload files on the command line to -.Ar URL +.Ar url where -.Ar URL -is one of the ftp URL types as supported by auto-fetch +.Ar url +is one of the +.Sq Li ftp:// +URL types as supported by auto-fetch (with an optional target filename for single file uploads), and .Ar file is one or more local files to be uploaded. @@ -312,12 +328,14 @@ Forces .Nm to show all responses from the remote server, as well as report on data transfer statistics. -.It Fl x Ar xferbufsize +.It Fl x Ar xfersize Set the size of the socket send and receive buffers to -.Ar xferbufsize . +.Ar xfersize . Refer to .Ic xferbuf for more information. +.It Fl ? +Display help to stdout, and exit. .El .Pp The client host with which @@ -334,7 +352,7 @@ from the user. When .Nm is awaiting commands from the user the prompt -.Ql ftp\*[Gt] +.Ql ftp> is provided to the user. The following commands are recognized by @@ -442,6 +460,16 @@ when an ascii type transfer is made, the distinguished from a record delimiter only when .Ic \&cr is off. +.It Ic debug Op Ar debug-value +Toggle debugging mode. +If an optional +.Ar debug-value +is specified it is used to set the debugging level. +When debugging is on, +.Nm +prints each command sent to the remote machine, preceded +by the string +.Ql \-\-> . .It Ic delete Ar remote-file Delete the file .Ar remote-file @@ -477,18 +505,16 @@ Toggle command line editing, and context completion. This is automatically enabled if input is from a terminal, and disabled otherwise. -.It Ic epsv epsv4 epsv6 +.It Ic epsv , epsv4 , epsv6 Toggle the use of the extended .Dv EPSV and .Dv EPRT commands on all IP, IPv4, and IPv6 connections respectively. First try -.Dv EPSV / -.Dv EPRT , +.Dv EPSV Ns \^/\^ Ns Dv EPRT , and then -.Dv PASV / -.Dv PORT . +.Dv PASV Ns \^/\^ Ns Dv PORT . This is enabled by default. If an extended command fails then this option will be temporarily disabled for the duration of the current connection, or until @@ -519,19 +545,11 @@ format is .It Ic ftp Ar host Op Ar port A synonym for .Ic open . -.It Ic ftp_debug Op Ar ftp_debug-value -Toggle debugging mode. -If an optional -.Ar ftp_debug-value -is specified it is used to set the debugging level. -When debugging is on, -.Nm -prints each command sent to the remote machine, preceded -by the string -.Ql \-\-\*[Gt] . .It Ic gate Op Ar host Op Ar port Toggle gate-ftp mode, which used to connect through the -TIS FWTK and Gauntlet ftp proxies. +TIS FWTK and Gauntlet +.Tn FTP +proxies. This will not be permitted if the gate-ftp server hasn't been set (either explicitly by the user, or from the .Ev FTPSERVER @@ -589,9 +607,11 @@ each remote file name is expanded separately on the remote machine and the lists are not merged. Expansion of a directory name is likely to be different from expansion of the name of an ordinary file: -the exact result depends on the foreign operating system and ftp server, +the exact result depends on the foreign operating system and +.Tn FTP +server, and can be previewed by doing -.Ql mls remote-files \- +.Sq Li mls remote-files \- . Note: .Ic mget , .Ic mput @@ -672,32 +692,32 @@ To invoke a macro, use the command (see above). .Pp The macro processor interprets -.Sq $ +.Ql $ and -.Sq \e +.Ql \e as special characters. A -.Sq $ +.Ql $ followed by a number (or numbers) is replaced by the corresponding argument on the macro invocation command line. A -.Sq $ +.Ql $ followed by an -.Sq i +.Ql i signals the macro processor that the executing macro is to be looped. On the first pass -.Dq $i +.Ql $i is replaced by the first argument on the macro invocation command line, on the second pass it is replaced by the second argument, and so on. A -.Sq \e +.Ql \e followed by any character is replaced by that character. Use the -.Sq \e +.Ql \e to prevent special treatment of the -.Sq $ . +.Ql $ . .It Ic mdelete Op Ar remote-files Delete the .Ar remote-files @@ -732,7 +752,7 @@ Files are transferred into the local wor which can be changed with .Ql lcd directory ; new local directories can be created with -.Ql "\&! mkdir directory" . +.Sq Li "\&! mkdir directory" . .It Ic mkdir Ar directory-name Make a directory on the remote machine. .It Ic mls Ar remote-files local-file @@ -755,7 +775,7 @@ Display the contents of in a machine-parsable form, using .Dv MLSD . The format of display can be changed with -.Sq "remopts mlst ..." . +.Sq Li "remopts mlst ..." . .It Ic mlst Op Ar remote-path Display the details about .Ar remote-path @@ -763,7 +783,7 @@ Display the details about in a machine-parsable form, using .Dv MLST . The format of display can be changed with -.Sq "remopts mlst ..." . +.Sq Li "remopts mlst ..." . .It Ic mode Ar mode-name Set the file transfer .Ic mode @@ -809,7 +829,7 @@ If the file does not exist on the current system, the remote file is considered .Ic newer . Otherwise, this command is identical to -.Ar get . +.Ic get . .It Ic nlist Op Ar remote-path Op Ar local-file A synonym for .Ic ls . @@ -834,7 +854,8 @@ The mapping follows the pattern set by .Ar inpattern and .Ar outpattern . -.Op Ar Inpattern +.Pp +.Ar inpattern is a template for incoming filenames (which may have already been processed according to the .Ic ntrans @@ -843,16 +864,16 @@ and settings). Variable templating is accomplished by including the sequences -.Dq $1 , -.Dq $2 , -\&... -.Dq $9 +.Ql $1 , +.Ql $2 , +\&...\|, +.Ql $9 in .Ar inpattern . Use -.Sq \e +.Ql \e to prevent this special treatment of the -.Sq $ +.Ql $ character. All other characters are treated literally, and are used to determine the .Ic nmap @@ -860,53 +881,72 @@ All other characters are treated literal variable values. For example, given .Ar inpattern -$1.$2 and the remote file name "mydata.data", $1 would have the value -"mydata", and $2 would have the value "data". +.Sq Li $1.$2 +and the remote file name +.Sq Li mydata.data , +.Ql $1 +would have the value +.Sq Li mydata , +and +.Ql $2 +would have the value +.Sq Li data . +.Pp The .Ar outpattern determines the resulting mapped filename. The sequences -.Dq $1 , -.Dq $2 , -\&... -.Dq $9 +.Ql $1 , +.Ql $2 , +\&...\|, +.Ql $9 are replaced by any value resulting from the .Ar inpattern template. The sequence -.Dq $0 +.Ql $0 is replaced by the original filename. Additionally, the sequence .Dq Op Ar seq1 , Ar seq2 is replaced by -.Op Ar seq1 +.Ar seq1 if .Ar seq1 is not a null string; otherwise it is replaced by .Ar seq2 . For example, the command .Pp -.Bd -literal -offset indent -compact -nmap $1.$2.$3 [$1,$2].[$2,file] -.Ed +.Dl nmap $1.$2.$3 [$1,$2].[$2,file] .Pp would yield -the output filename "myfile.data" for input filenames "myfile.data" and -"myfile.data.old", "myfile.file" for the input filename "myfile", and -"myfile.myfile" for the input filename ".myfile". +the output filename +.Sq Li myfile.data +for input filenames +.Sq Li myfile.data +and +.Sq Li myfile.data.old , +.Sq Li myfile.file +for the input filename +.Sq Li myfile , +and +.Sq Li myfile.myfile +for the input filename +.Sq Li "\&.myfile" . Spaces may be included in .Ar outpattern , as in the example: -.Dl nmap $1 sed "s/ *$//" \*[Gt] $1 +.Pp +.Dl nmap $1 sed "s/ *$//" > $1 +.Pp Use the -.Sq \e +.Ql \e character to prevent special treatment of the -.Sq $ , -.Sq \&[ , -.Sq \&] , +.Ql $ , +.Ql \&[ , +.Ql \&] , and -.Sq \&, +.Ql \&, characters. .It Ic ntrans Op Ar inchars Op Ar outchars Set or unset the filename character translation mechanism. @@ -1027,7 +1067,7 @@ The progress bar will be disabled for a as .Sq Fl or a command that starts with -.Sq \&| . +.Ql \&| . Refer to .Sx FILE NAMING CONVENTIONS for more information. @@ -1081,7 +1121,9 @@ Any other response will answer .Sq yes to the current file. .It Ic proxy Ar ftp-command -Execute an ftp command on a secondary control connection. +Execute an +.Tn FTP +command on a secondary control connection. This command allows simultaneous connection to two remote .Tn FTP servers for transferring files between the two servers. @@ -1090,11 +1132,13 @@ The first command should be an .Ic open , to establish the secondary control connection. -Enter the command "proxy ?" to see other +Enter the command +.Sq Li "proxy ?" +to see other .Tn FTP commands executable on the secondary connection. The following commands behave differently when prefaced by -.Ic proxy : +.Ic proxy\^ : .Ic open will not define new macros during the auto-login process, .Ic close @@ -1139,7 +1183,7 @@ machine. .It Ic quit A synonym for .Ic bye . -.It Ic quote Ar arg1 arg2 ... +.It Ic quote Op Ar arg ... The arguments specified are sent, verbatim, to the remote .Tn FTP server. @@ -1153,7 +1197,7 @@ is 0, disable the throttle. .Pp .Ar direction may be one of: -.Bl -tag -width "all" -offset indent -compact +.Bl -tag -width ".Cm all" -offset indent -compact .It Cm all Both directions. .It Cm get @@ -1166,7 +1210,7 @@ Outgoing transfers. can be modified on the fly by .Ar increment bytes (default: 1024) each time a given signal is received: -.Bl -tag -width "SIGUSR1" -offset indent +.Bl -tag -width ".Dv SIGUSR1" -offset indent .It Dv SIGUSR1 Increment .Ar maximum @@ -1224,7 +1268,7 @@ to Remote .Tn FTP commands known to support options include: -.Sq MLST +.Dv MLST (used for .Dv MLSD and @@ -1277,10 +1321,16 @@ local filename for a .Ic get or .Ic mget -command, a ".1" is appended to the name. +command, a +.Ql \&.1 +is appended to the name. If the resulting name matches another existing file, -a ".2" is appended to the original name. -If this process continues up to ".99", an error +a +.Ql \&.2 +is appended to the original name. +If this process continues up to +.Ql .99 , +an error message is printed, and the transfer does not take place. The generated unique filename will be reported. Note that @@ -1358,7 +1408,7 @@ Defaults to Defaults to .Ev $FTPRPROMPT . .El -.It Ic site Ar arg1 arg2 ... +.It Ic site Op Ar arg ... The arguments specified are sent, verbatim, to the remote .Tn FTP server as a @@ -1479,7 +1529,7 @@ A synonym for .Pp Command arguments which have embedded spaces may be quoted with quote -.Sq \&" +.Ql \&\(dq marks. .Pp Commands which toggle settings can take an explicit @@ -1514,7 +1564,7 @@ If receives a .Dv SIGINFO (see the -.Dq status +.Cm status argument of .Xr stty 1 ) or @@ -1544,7 +1594,7 @@ contains a glob character and globbing i (see .Ic glob ) , then the equivalent of -.Ql mget path +.Sq Li mget path is performed. .Pp If the directory component of @@ -1557,10 +1607,10 @@ of in the current directory. Otherwise, the full remote name is used as the local name, relative to the local root directory. -.\" ftp://[user[:password]@]host[:port]/path[/][;type=X] +.\" ftp://[user[:password]@]host[:port]/path[/][;type=type] .It Li ftp:// Ns Oo Ar user Ns Oo Ns Li \&: Ns Ar password Oc Ns Li \&@ Oc \ Ns Ar host Ns Oo Li \&: Ns Ar port Oc Ns Li / Ns Ar path Ns Oo Li / Oc \ -Ns Oo Li ;type= Ns Ar X Oc +Ns Oo Li ;type= Ns Ar type Oc An .Tn FTP URL, retrieved using the @@ -1583,9 +1633,9 @@ In this case, use if supplied, otherwise prompt the user for one. .Pp If a suffix of -.Sq ;type=A +.Sq Li \&;type=A or -.Sq ;type=I +.Sq Li \&;type=I is supplied, then the transfer type will take place as ascii or binary (respectively). The default transfer type is binary. @@ -1596,7 +1646,7 @@ In order to be compliant with interprets the .Ar path part of an -.Dq ftp:// +.Sq Li ftp:// auto-fetch URL as follows: .Bl -bullet .It @@ -1676,20 +1726,20 @@ user. If the .Pa / directory is required, use a leading path of -.Dq %2F . +.Sq Li \&%2F . If a user's home directory is required (and the remote server supports the syntax), use a leading path of -.Dq %7Euser/ . +.Sq Li \&%7E Ns Ar user Ns Li / . For example, to retrieve .Pa /etc/motd from -.Sq localhost +.Sq Li localhost as the user -.Sq myname +.Sq Li myname with the password -.Sq mypass , +.Sq Li mypass , use -.Dq ftp://myname:mypass@localhost/%2fetc/motd +.Sq Li ftp://myname:mypass@localhost/%2fetc/motd .It The exact .Ic cd @@ -1697,11 +1747,11 @@ and .Ic get commands can be controlled by careful choice of where to use -.Sq / +.Sq Li / and where to use -.Sq %2F +.Sq Li \&%2F (or -.Sq %2f ) . +.Sq Li %2f ) . For example, the following URLs correspond to the equivalents of the indicated commands: .Bl -tag -width "ftp://host/%2Fdir1%2Fdir2%2Ffile" @@ -1748,9 +1798,9 @@ If authorization is required to retrieve .Ar path , and -.Sq user +.Ar user (and optionally -.Sq password ) +.Ar 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 \ @@ -1770,9 +1820,9 @@ If authorization is required to retrieve .Ar path , and -.Sq user +.Ar user (and optionally -.Sq password ) +.Ar password\^ ) is in the URL, use them for the first attempt to authenticate. There is currently no certificate validation and verification. .\" file:///path @@ -1815,7 +1865,7 @@ is recommended, to avoid writing to unex If a classic format or an .Tn FTP URL format has a trailing -.Sq / +.Ql / or an empty .Ar path component, then @@ -1847,9 +1897,9 @@ proxies will be restarted. For .Tn FTP , this is implemented by using -.Nm reget +.Ic reget instead of -.Nm get . +.Ic get . For .Tn HTTP , this is implemented by using the @@ -1863,7 +1913,7 @@ to enter a username and password to auth When specifying IPv6 numeric addresses in a URL, you need to surround the address in square brackets. E.g.: -.Dq ftp://[::1]:21/ . +.Sq Li ftp://[::1]:21/ . This is because colons are used in IPv6 numeric address as well as being the separator for the port number. .Sh ABORTING A FILE TRANSFER @@ -1886,7 +1936,9 @@ sending the requested file. .Pp If the terminal interrupt key sequence is used whilst .Nm -is awaiting a reply from the remote server for the ABOR processing, +is awaiting a reply from the remote server for the +.Dv ABOR +processing, then the connection will be closed. This is different from the traditional behaviour (which ignores the terminal interrupt during this phase), but is considered more useful. @@ -1899,13 +1951,13 @@ commands are processed according to the If the file name .Sq Fl is specified, the -.Ar stdin +.Va stdin (for reading) or -.Ar stdout +.Va stdout (for writing) is used. .It If the first character of the file name is -.Sq \&| , +.Ql \&| , the remainder of the argument is interpreted as a shell command. .Nm @@ -1915,13 +1967,12 @@ with the argument supplied, and reads (w (stdin). If the shell command includes spaces, the argument must be quoted; e.g. -.Dq Qq Li \&| ls\ \-lt . +.Sq Li \(dq|\~ls\~\-lt\(dq . A particularly useful example of this mechanism is: -.Dq Li dir \&"\&" \&|more . +.Sq Li dir\~\(dq\(dq\~|more . .It -Failing the above checks, if -.Dq globbing +Failing the above checks, if globbing is enabled, local file names are expanded according to the rules used in the .Xr csh 1 ; @@ -1932,7 +1983,7 @@ If the .Nm command expects a single local file (e.g. .Ic put ) , -only the first filename generated by the "globbing" operation is used. +only the first filename generated by the globbing operation is used. .It For .Ic mget @@ -2133,7 +2184,7 @@ The .Xr editline 3 library is configured with a .Pa .editrc -file - refer to +file \(em refer to .Xr editrc 5 for more information. .Pp @@ -2150,7 +2201,7 @@ By default, this is bound to the TAB key By default, .Nm displays a command line prompt of -.Dq "ftp\*[Gt] " +.Sq Li ftp>\~ to the user. This can be changed with the .Ic "set prompt" @@ -2167,42 +2218,42 @@ information: .It Li \&%/ The current remote working directory. .\" %c[[0]n], %.[[0]n] -.It \&%c Ns Oo Oo Li 0 Oc Ns Ar n Oc , Ns Li \&%. Ns Oo Oo Li 0 Oc Ns Ar n Oc +.It Li \&%c Ns Oo Oo Li 0 Oc Ns Ar n Oc , Li \&%. Ns Oo Oo Li 0 Oc Ns Ar n Oc The trailing component of the current remote working directory, or -.Em n +.Ar n trailing components if a digit -.Em n +.Ar n is given. If -.Em n +.Ar n begins with -.Sq 0 , +.Ql 0 , the number of skipped components precede the trailing component(s) in the format .\" ``/<number>trailing'' .Do .Sm off -.Li / Li \*[Lt] Va number Li \*[Gt] -.Va trailing +.Li / Li < Ar number Li > +.Ar trailing .Sm on .Dc (for -.Sq \&%c ) +.Ql \&%c ) or .\" ``...trailing'' -.Dq Li \&... Ns Va trailing +.Dq Li \&... Ns Ar trailing (for -.Sq \&%. ) . +.Ql \&%. ) . .It Li \&%M The remote host name. .It Li \&%m -The remote host name, up to the first -.Sq \&. . +The remote host name, up to the first dot +.Ql \&. . .It Li \&%n The remote user name. .It Li \&%% -A single -.Sq % . +A single percent character +.Ql % . .El .Sh ENVIRONMENT .Nm @@ -2213,7 +2264,7 @@ Password to send in an anonymous .Tn FTP transfer. Defaults to -.Dq Li `whoami`@ . +.Dq Li \&\`whoami\`@ . .It Ev FTPMODE Overrides the default operation mode. Support values are: @@ -2234,14 +2285,13 @@ only .It Ev FTPPROMPT Command-line prompt to use. Defaults to -.Dq "ftp\*[Gt] " . +.Sq Li ftp>\~ . Refer to .Sx COMMAND LINE PROMPT for more information. .It Ev FTPRPROMPT Command-line right side prompt to use. -Defaults to -.Dq "" . +Defaults to empty string. Refer to .Sx COMMAND LINE PROMPT for more information. @@ -2254,9 +2304,9 @@ Port to use when connecting to gate-ftp .Ic gate is enabled. Default is port returned by a -.Fn getservbyname +.Xr getservbyname 3 lookup of -.Dq ftpgate/tcp . +.Dq Li ftpgate/tcp . .It Ev FTPUSERAGENT The value to send for the .Tn HTTP @@ -2270,6 +2320,8 @@ file, if one exists. An alternate location of the .Pa .netrc file. +.It Ev NO_CERT_VERIFY +Don't verify SSL certificates. .It Ev PAGER Used by various commands to display files. Defaults to @@ -2304,9 +2356,9 @@ If .Dq unsafe URL characters are required in the username or password (for example -.Sq @ +.Ql @ or -.Sq / ) , +.Ql / ) , encode them with .Li RFC 3986 .Sq Li \&% Ns Ar XX @@ -2323,10 +2375,22 @@ may be incompatible with other programs .Em NOTE : this is not used for interactive sessions, only for command-line fetches. +.It Ev https_proxy +URL of +.Tn HTTPS +proxy to use when making +.Tn HTTPS +URL requests. +.Pp +See +.Ev http_proxy +for further notes about proxy use. .It Ev no_proxy A space or comma separated list of hosts (or domains) for which proxying is not to be used. -Each entry may have an optional trailing ":port", which restricts +Each entry may have an optional trailing +.Sq Li \&: Ns Ar port , +which restricts the matching to connections to that port. .El .Sh EXTENDED PASSIVE MODE AND FIREWALLS Index: src/usr.bin/ftp/ftp.c diff -u src/usr.bin/ftp/ftp.c:1.167.6.2 src/usr.bin/ftp/ftp.c:1.167.6.3 --- src/usr.bin/ftp/ftp.c:1.167.6.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/ftp.c Mon Sep 12 17:08:13 2022 @@ -1,7 +1,7 @@ -/* $NetBSD: ftp.c,v 1.167.6.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: ftp.c,v 1.167.6.3 2022/09/12 17:08:13 martin Exp $ */ /*- - * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. + * Copyright (c) 1996-2021 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -92,7 +92,7 @@ #if 0 static char sccsid[] = "@(#)ftp.c 8.6 (Berkeley) 10/27/94"; #else -__RCSID("$NetBSD: ftp.c,v 1.167.6.2 2022/09/12 15:05:21 martin Exp $"); +__RCSID("$NetBSD: ftp.c,v 1.167.6.3 2022/09/12 17:08:13 martin Exp $"); #endif #endif /* not lint */ @@ -280,6 +280,11 @@ hookup(const char *host, const char *por goto bad; } + if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, + (void *)&on, sizeof(on)) == -1) { + DWARN("setsockopt %s (ignored)", "SO_KEEPALIVE"); + } + if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *)&on, sizeof(on)) == -1) { DWARN("setsockopt %s (ignored)", "SO_OOBINLINE"); @@ -320,6 +325,17 @@ cmdtimeout(int notused) errno = oerrno; } +static int +issighandler(sigfunc func) +{ + return (func != SIG_IGN && + func != SIG_DFL && +#ifdef SIG_HOLD + func != SIG_HOLD && +#endif + func != SIG_ERR); +} + /*VARARGS*/ int command(const char *fmt, ...) @@ -359,8 +375,9 @@ command(const char *fmt, ...) (void)fflush(cout); cpend = 1; r = getreply(!strcmp(fmt, "QUIT")); - if (abrtflag && oldsigint != SIG_IGN) + if (abrtflag && issighandler(oldsigint)) { (*oldsigint)(SIGINT); + } (void)xsignal(SIGINT, oldsigint); return (r); } @@ -510,11 +527,14 @@ getreply(int expecteof) (void)xsignal(SIGALRM, oldsigalrm); if (code == 421 || originalcode == 421) lostpeer(0); - if (abrtflag && oldsigint != cmdabort && oldsigint != SIG_IGN) + if (abrtflag && oldsigint != cmdabort && + issighandler(oldsigint)) { (*oldsigint)(SIGINT); + } if (timeoutflag && oldsigalrm != cmdtimeout && - oldsigalrm != SIG_IGN) + issighandler(oldsigalrm)) { (*oldsigalrm)(SIGINT); + } return (n - '0'); } } @@ -578,7 +598,7 @@ abortxfer(int notused) /* * Read data from infd & write to outfd, using buf/bufsize as the temporary - * buffer, dealing with short writes. + * buffer, dealing with short reads or writes. * If rate_limit != 0, rate-limit the transfer. * If hash_interval != 0, fputc('c', ttyout) every hash_interval bytes. * Updates global variables: bytes. @@ -612,15 +632,25 @@ copy_bytes(int infd, int outfd, char *bu bufrem = bufchunk; while (bufrem > 0) { inc = read(infd, buf, MIN((off_t)bufsize, bufrem)); - if (inc <= 0) + if (inc < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + goto copy_done; + } else if (inc == 0) { goto copy_done; + } bytes += inc; bufrem -= inc; bufp = buf; while (inc > 0) { outc = write(outfd, bufp, inc); - if (outc < 0) + if (outc < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } goto copy_done; + } inc -= outc; bufp += outc; } @@ -670,7 +700,7 @@ sendrequest(const char *cmd, const char FILE *volatile dout; int (*volatile closefunc)(FILE *); sigfunc volatile oldintr; - sigfunc volatile oldintp; + sigfunc volatile oldpipe; off_t volatile hashbytes; int hash_interval; const char *lmode; @@ -697,8 +727,8 @@ sendrequest(const char *cmd, const char if (curtype != type) changetype(type, 0); closefunc = NULL; - oldintr = NULL; - oldintp = NULL; + oldintr = SIG_ERR; + oldpipe = SIG_ERR; lmode = "w"; if (sigsetjmp(xferabort, 1)) { while (cpend) @@ -712,7 +742,7 @@ sendrequest(const char *cmd, const char fin = stdin; progress = 0; } else if (*local == '|') { - oldintp = xsignal(SIGPIPE, SIG_IGN); + oldpipe = xsignal(SIGPIPE, SIG_IGN); fin = popen(local + 1, "r"); if (fin == NULL) { warn("Can't execute `%s'", local + 1); @@ -786,7 +816,9 @@ sendrequest(const char *cmd, const char } progressmeter(-1); - oldintp = xsignal(SIGPIPE, SIG_IGN); + if (oldpipe == SIG_ERR) { + oldpipe = xsignal(SIGPIPE, SIG_IGN); + } hash_interval = (hash && (!progress || filesize < 0)) ? mark : 0; switch (curtype) { @@ -855,7 +887,7 @@ sendrequest(const char *cmd, const char abort: (void)xsignal(SIGINT, oldintr); - oldintr = NULL; + oldintr = SIG_ERR; if (!cpend) { code = -1; goto cleanupsend; @@ -874,10 +906,10 @@ sendrequest(const char *cmd, const char ptransfer(0); cleanupsend: - if (oldintr) + if (oldintr != SIG_ERR) (void)xsignal(SIGINT, oldintr); - if (oldintp) - (void)xsignal(SIGPIPE, oldintp); + if (oldpipe != SIG_ERR) + (void)xsignal(SIGPIPE, oldpipe); if (data >= 0) { (void)close(data); data = -1; @@ -899,7 +931,7 @@ recvrequest(const char *cmd, const char FILE *volatile din; int (*volatile closefunc)(FILE *); sigfunc volatile oldintr; - sigfunc volatile oldintp; + sigfunc volatile oldpipe; int c, d; int volatile is_retr; int volatile tcrflag; @@ -935,8 +967,8 @@ recvrequest(const char *cmd, const char return; } closefunc = NULL; - oldintr = NULL; - oldintp = NULL; + oldintr = SIG_ERR; + oldpipe = SIG_ERR; tcrflag = !crflag && is_retr; if (sigsetjmp(xferabort, 1)) { while (cpend) @@ -1017,7 +1049,7 @@ recvrequest(const char *cmd, const char progress = 0; preserve = 0; } else if (!ignorespecial && *local == '|') { - oldintp = xsignal(SIGPIPE, SIG_IGN); + oldpipe = xsignal(SIGPIPE, SIG_IGN); fout = popen(local + 1, "w"); if (fout == NULL) { warn("Can't execute `%s'", local+1); @@ -1183,10 +1215,10 @@ recvrequest(const char *cmd, const char ptransfer(0); cleanuprecv: - if (oldintr) + if (oldintr != SIG_ERR) (void)xsignal(SIGINT, oldintr); - if (oldintp) - (void)xsignal(SIGPIPE, oldintp); + if (oldpipe != SIG_ERR) + (void)xsignal(SIGPIPE, oldpipe); if (data >= 0) { (void)close(data); data = -1; @@ -1349,13 +1381,11 @@ initconn(void) if (data_addr.su_family != AF_INET) { fputs( "Passive mode AF mismatch. Shouldn't happen!\n", ttyout); - error = 1; goto bad; } if (code / 10 == 22 && code != 227) { fputs("wrong server: return code must be 227\n", ttyout); - error = 1; goto bad; } error = sscanf(pasv, "%u,%u,%u,%u,%u,%u", @@ -1364,21 +1394,24 @@ initconn(void) if (error != 6) { fputs( "Passive mode address scan failure. Shouldn't happen!\n", ttyout); - error = 1; goto bad; } - error = 0; memset(&data_addr, 0, sizeof(data_addr)); data_addr.su_family = AF_INET; data_addr.su_len = sizeof(struct sockaddr_in); data_addr.si_su.su_sin.sin_addr.s_addr = htonl(pack4(addr, 0)); data_addr.su_port = htons(pack2(port, 0)); + if (data_addr.si_su.su_sin.sin_addr.s_addr != + hisctladdr.si_su.su_sin.sin_addr.s_addr) { + fputs("Passive mode address mismatch.\n", + ttyout); + goto bad; + } } else if (strcmp(pasvcmd, "LPSV") == 0) { if (code / 10 == 22 && code != 228) { fputs("wrong server: return code must be 228\n", ttyout); - error = 1; goto bad; } switch (data_addr.su_family) { @@ -1391,23 +1424,26 @@ initconn(void) if (error != 9) { fputs( "Passive mode address scan failure. Shouldn't happen!\n", ttyout); - error = 1; goto bad; } if (af != 4 || hal != 4 || pal != 2) { fputs( "Passive mode AF mismatch. Shouldn't happen!\n", ttyout); - error = 1; goto bad; } - error = 0; memset(&data_addr, 0, sizeof(data_addr)); data_addr.su_family = AF_INET; data_addr.su_len = sizeof(struct sockaddr_in); data_addr.si_su.su_sin.sin_addr.s_addr = htonl(pack4(addr, 0)); data_addr.su_port = htons(pack2(port, 0)); + if (data_addr.si_su.su_sin.sin_addr.s_addr != + hisctladdr.si_su.su_sin.sin_addr.s_addr) { + fputs("Passive mode address mismatch.\n", + ttyout); + goto bad; + } break; #ifdef INET6 case AF_INET6: @@ -1423,17 +1459,14 @@ initconn(void) if (error != 21) { fputs( "Passive mode address scan failure. Shouldn't happen!\n", ttyout); - error = 1; goto bad; } if (af != 6 || hal != 16 || pal != 2) { fputs( "Passive mode AF mismatch. Shouldn't happen!\n", ttyout); - error = 1; goto bad; } - error = 0; memset(&data_addr, 0, sizeof(data_addr)); data_addr.su_family = AF_INET6; data_addr.su_len = sizeof(struct sockaddr_in6); @@ -1445,10 +1478,19 @@ initconn(void) } } data_addr.su_port = htons(pack2(port, 0)); + if (memcmp( + &data_addr.si_su.su_sin6.sin6_addr, + &hisctladdr.si_su.su_sin6.sin6_addr, + sizeof(data_addr.si_su.su_sin6.sin6_addr))) { + fputs("Passive mode address mismatch.\n", + ttyout); + goto bad; + } break; #endif default: - error = 1; + fputs("Unknown passive mode AF.\n", ttyout); + goto bad; } } else if (strcmp(pasvcmd, "EPSV") == 0) { char delim[4]; @@ -1457,20 +1499,17 @@ initconn(void) if (code / 10 == 22 && code != 229) { fputs("wrong server: return code must be 229\n", ttyout); - error = 1; goto bad; } if (sscanf(pasv, "%c%c%c%d%c", &delim[0], &delim[1], &delim[2], &port[1], &delim[3]) != 5) { fputs("parse error!\n", ttyout); - error = 1; goto bad; } if (delim[0] != delim[1] || delim[0] != delim[2] || delim[0] != delim[3]) { fputs("parse error!\n", ttyout); - error = 1; goto bad; } data_addr = hisctladdr; @@ -1553,8 +1592,8 @@ initconn(void) result = COMPLETE + 1; break; } - /* FALLTHROUGH */ #ifdef INET6 + /* FALLTHROUGH */ case AF_INET6: if (!epsv6 || epsv6bad) { result = COMPLETE + 1; @@ -1856,7 +1895,7 @@ proxtrans(const char *cmd, const char *l int volatile secndflag; const char *volatile cmd2; - oldintr = NULL; + oldintr = SIG_ERR; secndflag = 0; if (strcmp(cmd, "RETR")) cmd2 = "RETR"; @@ -2047,7 +2086,7 @@ gunique(const char *local) * needs to get back to a known state. */ static void -abort_squared(int dummy) +abort_squared(int signo) { char msgbuf[100]; size_t len; @@ -2057,7 +2096,7 @@ abort_squared(int dummy) len = strlcpy(msgbuf, "\nremote abort aborted; closing connection.\n", sizeof(msgbuf)); write(fileno(ttyout), msgbuf, len); - lostpeer(0); + lostpeer(signo); siglongjmp(xferabort, 1); } Index: src/usr.bin/ftp/ftp_var.h diff -u src/usr.bin/ftp/ftp_var.h:1.84.8.2 src/usr.bin/ftp/ftp_var.h:1.84.8.3 --- src/usr.bin/ftp/ftp_var.h:1.84.8.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/ftp_var.h Mon Sep 12 17:08:13 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: ftp_var.h,v 1.84.8.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: ftp_var.h,v 1.84.8.3 2022/09/12 17:08:13 martin Exp $ */ /*- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. @@ -269,6 +269,7 @@ GLOBAL int unix_server; /* server is uni GLOBAL int unix_proxy; /* proxy is unix, can use binary for ascii */ GLOBAL char localcwd[MAXPATHLEN]; /* local dir */ GLOBAL char remotecwd[MAXPATHLEN]; /* remote dir */ +GLOBAL int remcwdvalid; /* remotecwd has been updated */ GLOBAL char *username; /* name of user logged in as. (dynamic) */ GLOBAL sa_family_t family; /* address family to use for connections */ @@ -340,7 +341,7 @@ extern struct option optiontab[]; #define DPRINTF(...) (void)0 #define DWARN(...) (void)0 #else -#define DWFTP(a) do a; while (/*CONSTCOND*/0) +#define DWFTP(a) do a; while (0) #define DPRINTF(...) DWFTP(if (ftp_debug) (void)fprintf(ttyout, __VA_ARGS__)) #define DWARN(...) DWFTP(if (ftp_debug) warn(__VA_ARGS__)) #endif Index: src/usr.bin/ftp/main.c diff -u src/usr.bin/ftp/main.c:1.123.8.2 src/usr.bin/ftp/main.c:1.123.8.3 --- src/usr.bin/ftp/main.c:1.123.8.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/main.c Mon Sep 12 17:08:13 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: main.c,v 1.123.8.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: main.c,v 1.123.8.3 2022/09/12 17:08:13 martin Exp $ */ /*- * Copyright (c) 1996-2015 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.123.8.2 2022/09/12 15:05:21 martin Exp $"); +__RCSID("$NetBSD: main.c,v 1.123.8.3 2022/09/12 17:08:13 martin Exp $"); #endif #endif /* not lint */ @@ -130,7 +130,8 @@ __RCSID("$NetBSD: main.c,v 1.123.8.2 202 #define NO_PROXY "no_proxy" /* env var with list of non-proxied * hosts, comma or space separated */ -__dead static void usage(void); +static int usage(void); +static int usage_help(void); static void setupoption(const char *, const char *, const char *); int @@ -266,7 +267,7 @@ main(int volatile argc, char **volatile } } - while ((ch = getopt(argc, argv, "46AadefginN:o:pP:q:r:Rs:tT:u:vVx:")) != -1) { + while ((ch = getopt(argc, argv, ":46AadefginN:o:pP:q:r:Rs:tT:u:vVx:")) != -1) { switch (ch) { case '4': family = AF_INET; @@ -324,7 +325,7 @@ main(int volatile argc, char **volatile break; case 'o': - outfile = optarg; + outfile = ftp_strdup(optarg); if (strcmp(outfile, "-") == 0) ttyout = stderr; break; @@ -378,15 +379,15 @@ main(int volatile argc, char **volatile if (*cp == '\0') { warnx("Bad throttle value `%s'", optarg); - usage(); - /* NOTREACHED */ + return usage(); } targv[targc++] = cp; if (targc >= 5) break; } - if (parserate(targc, targv, 1) == -1) - usage(); + if (parserate(targc, targv, 1) == -1) { + return usage(); + } free(oac); break; } @@ -415,8 +416,19 @@ main(int volatile argc, char **volatile rcvbuf_size = sndbuf_size; break; + case '?': + if (optopt == '?') { + return usage_help(); + } + warnx("-%c: unknown option", optopt); + return usage(); + + case ':': + warnx("-%c: missing argument", optopt); + return usage(); + default: - usage(); + errx(1, "unimplemented option -%c", ch); } } /* set line buffering on ttyout */ @@ -464,7 +476,6 @@ main(int volatile argc, char **volatile if (localhome == NULL && !EMPTYSTRING(pw->pw_dir)) localhome = ftp_strdup(pw->pw_dir); localname = ftp_strdup(pw->pw_name); - anonuser = localname; } if (netrc[0] == '\0' && localhome != NULL) { if (strlcpy(netrc, localhome, sizeof(netrc)) >= sizeof(netrc) || @@ -573,8 +584,9 @@ main(int volatile argc, char **volatile retry_connect = 0; /* connected, stop hiding msgs */ } } - if (isupload) - usage(); + if (isupload) { + return usage(); + } #ifndef NO_EDITCOMPLETE controlediting(); @@ -661,7 +673,7 @@ cmdscanner(void) case -2: /* error */ if (fromatty) putc('\n', ttyout); - quit(0, NULL); + justquit(); /* NOTREACHED */ case -3: /* too long; try again */ fputs("Sorry, input line is too long.\n", @@ -683,7 +695,7 @@ cmdscanner(void) if (buf == NULL || num == 0) { if (fromatty) putc('\n', ttyout); - quit(0, NULL); + justquit(); } if (num >= sizeof(line)) { fputs("Sorry, input line is too long.\n", @@ -837,7 +849,6 @@ slurpstring(void) slrflag++; INC_CHKCURSOR(stringbase); return ((*sb == '!') ? bangstr : dollarstr); - /* NOTREACHED */ case 1: slrflag++; altarg = stringbase; @@ -966,7 +977,7 @@ help(int argc, char *argv[]) cmd = argv[0]; isusage = (strcmp(cmd, "usage") == 0); if (argc == 0 || (isusage && argc == 1)) { - UPRINTF("usage: %s [command [...]]\n", cmd); + UPRINTF("usage: %s [command ...]\n", cmd); return; } if (argc == 1) { @@ -1045,20 +1056,69 @@ setupoption(const char *name, const char set_option(name, value ? value : defaultvalue, 0); } -void -usage(void) +static void +synopsis(FILE * stream) { - const char *progname = getprogname(); + const char * progname = getprogname(); - (void)fprintf(stderr, -"usage: %s [-46AadefginpRtVv] [-N netrc] [-o outfile] [-P port] [-q quittime]\n" -" [-r retry] [-s srcaddr] [-T dir,max[,inc]] [-x xferbufsize]\n" -" [[user@]host [port]] [host:path[/]] [file:///file]\n" -" [ftp://[user[:pass]@]host[:port]/path[/]]\n" -" [http://[user[:pass]@]host[:port]/path] [...]\n" + fprintf(stream, +"usage: %s [-46AadefginpRtVv] [-N NETRC] [-o OUTPUT] [-P PORT] [-q QUITTIME]\n" +" [-r RETRY] [-s SRCADDR] [-T DIR,MAX[,INC]] [-x XFERSIZE]\n" +" [[USER@]HOST [PORT]]\n" +" [[USER@]HOST:[PATH][/]]\n" +" [file:///PATH]\n" +" [ftp://[USER[:PASSWORD]@]HOST[:PORT]/PATH[/][;type=TYPE]]\n" +" [http://[USER[:PASSWORD]@]HOST[:PORT]/PATH]\n" #ifdef WITH_SSL -" [https://[user[:pass]@]host[:port]/path] [...]\n" +" [https://[USER[:PASSWORD]@]HOST[:PORT]/PATH]\n" #endif -" %s -u URL file [...]\n", progname, progname); - exit(1); +" ...\n" +" %s -u URL FILE ...\n" +" %s -?\n", + progname, progname, progname); +} + +static int +usage_help(void) +{ + synopsis(stdout); +#ifndef NO_USAGE + printf( +" -4 Only use IPv4 addresses\n" +" -6 Only use IPv6 addresses\n" +" -A Force active mode\n" +" -a Use anonymous login\n" +" -d Enable debugging\n" +" -e Disable command-line editing\n" +" -f Force cache reload for FTP or HTTP proxy transfers\n" +" -g Disable file name globbing\n" +" -i Disable interactive prompt during multiple file transfers\n" +" -N NETRC Use NETRC instead of ~/.netrc\n" +" -n Disable auto-login\n" +" -o OUTPUT Save auto-fetched files to OUTPUT\n" +" -P PORT Use port PORT\n" +" -p Force passive mode\n" +" -q QUITTIME Quit if connection stalls for QUITTIME seconds\n" +" -R Restart non-proxy auto-fetch\n" +" -r RETRY Retry failed connection attempts after RETRY seconds\n" +" -s SRCADDR Use source address SRCADDR\n" +" -t Enable packet tracing\n" +" -T DIR,MAX[,INC]\n" +" Set maximum transfer rate for direction DIR to MAX bytes/s,\n" +" with optional increment INC bytes/s\n" +" -u URL URL to upload file arguments to\n" +" -V Disable verbose and progress\n" +" -v Enable verbose and progress\n" +" -x XFERSIZE Set socket send and receive size to XFERSIZE\n" +" -? Display this help and exit\n" + ); +#endif + return EXIT_SUCCESS; +} + +static int +usage(void) +{ + synopsis(stderr); + return EXIT_FAILURE; } Index: src/usr.bin/ftp/progressbar.c diff -u src/usr.bin/ftp/progressbar.c:1.22.24.2 src/usr.bin/ftp/progressbar.c:1.22.24.3 --- src/usr.bin/ftp/progressbar.c:1.22.24.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/progressbar.c Mon Sep 12 17:08:13 2022 @@ -1,7 +1,7 @@ -/* $NetBSD: progressbar.c,v 1.22.24.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: progressbar.c,v 1.22.24.3 2022/09/12 17:08:13 martin Exp $ */ /*- - * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. + * Copyright (c) 1997-2021 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -31,7 +31,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: progressbar.c,v 1.22.24.2 2022/09/12 15:05:21 martin Exp $"); +__RCSID("$NetBSD: progressbar.c,v 1.22.24.3 2022/09/12 17:08:13 martin Exp $"); #endif /* not lint */ /* @@ -87,6 +87,7 @@ updateprogressmeter(int dummy) /* * List of order of magnitude suffixes, per IEC 60027-2. */ +#if !defined(NO_PROGRESS) || !defined(STANDALONE_PROGRESS) static const char * const suffixes[] = { "", /* 2^0 (byte) */ "KiB", /* 2^10 Kibibyte */ @@ -102,6 +103,7 @@ static const char * const suffixes[] = { #endif }; #define NSUFFIXES (int)(sizeof(suffixes) / sizeof(suffixes[0])) +#endif /* * Display a transfer progress bar if progress is non-zero. @@ -139,8 +141,10 @@ progressmeter(int flag) * these appropriately. */ #endif +#if !defined(NO_PROGRESS) || !defined(STANDALONE_PROGRESS) size_t len; char buf[256]; /* workspace for progress bar */ +#endif #ifndef NO_PROGRESS #define BAROVERHEAD 45 /* non `*' portion of progress bar */ /* @@ -189,7 +193,7 @@ progressmeter(int flag) if (quit_time > 0 || progress) { #endif /* !STANDALONE_PROGRESS */ if (flag == -1) { - (void)xsignal_restart(SIGALRM, updateprogressmeter, 1); + (void)xsignal(SIGALRM, updateprogressmeter); alarmtimer(1); /* set alarm timer for 1 Hz */ } else if (flag == 1) { alarmtimer(0); @@ -400,73 +404,21 @@ alarmtimer(int wait) setitimer(ITIMER_REAL, &itv, NULL); } - /* - * Install a POSIX signal handler, allowing the invoker to set whether - * the signal should be restartable or not + * Install a non-restartable POSIX signal handler. */ sigfunc -xsignal_restart(int sig, sigfunc func, int restartable) +xsignal(int sig, sigfunc func) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); -#if defined(SA_RESTART) /* 4.4BSD, Posix(?), SVR4 */ - act.sa_flags = restartable ? SA_RESTART : 0; -#elif defined(SA_INTERRUPT) /* SunOS 4.x */ - act.sa_flags = restartable ? 0 : SA_INTERRUPT; -#else -#error "system must have SA_RESTART or SA_INTERRUPT" + act.sa_flags = 0; +#if defined(SA_INTERRUPT) /* SunOS 4.x */ + act.sa_flags = SA_INTERRUPT; #endif if (sigaction(sig, &act, &oact) < 0) return (SIG_ERR); return (oact.sa_handler); } - -/* - * Install a signal handler with the `restartable' flag set dependent upon - * which signal is being set. (This is a wrapper to xsignal_restart()) - */ -sigfunc -xsignal(int sig, sigfunc func) -{ - int restartable; - - /* - * Some signals print output or change the state of the process. - * There should be restartable, so that reads and writes are - * not affected. Some signals should cause program flow to change; - * these signals should not be restartable, so that the system call - * will return with EINTR, and the program will go do something - * different. If the signal handler calls longjmp() or siglongjmp(), - * it doesn't matter if it's restartable. - */ - - switch(sig) { -#ifdef SIGINFO - case SIGINFO: -#endif - case SIGQUIT: - case SIGUSR1: - case SIGUSR2: - case SIGWINCH: - restartable = 1; - break; - - case SIGALRM: - case SIGINT: - case SIGPIPE: - restartable = 0; - break; - - default: - /* - * This is unpleasant, but I don't know what would be better. - * Right now, this "can't happen" - */ - errx(1, "xsignal_restart: called with signal %d", sig); - } - - return(xsignal_restart(sig, func, restartable)); -} Index: src/usr.bin/ftp/progressbar.h diff -u src/usr.bin/ftp/progressbar.h:1.8.38.2 src/usr.bin/ftp/progressbar.h:1.8.38.3 --- src/usr.bin/ftp/progressbar.h:1.8.38.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/progressbar.h Mon Sep 12 17:08:13 2022 @@ -1,7 +1,7 @@ -/* $NetBSD: progressbar.h,v 1.8.38.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: progressbar.h,v 1.8.38.3 2022/09/12 17:08:13 martin Exp $ */ /*- - * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. + * Copyright (c) 1996-2021 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -68,7 +68,6 @@ int foregroundproc(void); void alarmtimer(int); void progressmeter(int); sigfunc xsignal(int, sigfunc); -sigfunc xsignal_restart(int, sigfunc, int); #ifndef STANDALONE_PROGRESS void psummary(int); Index: src/usr.bin/ftp/ssl.c diff -u src/usr.bin/ftp/ssl.c:1.5.8.2 src/usr.bin/ftp/ssl.c:1.5.8.3 --- src/usr.bin/ftp/ssl.c:1.5.8.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/ssl.c Mon Sep 12 17:08:13 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: ssl.c,v 1.5.8.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: ssl.c,v 1.5.8.3 2022/09/12 17:08:13 martin Exp $ */ /*- * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav @@ -34,12 +34,17 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: ssl.c,v 1.5.8.2 2022/09/12 15:05:21 martin Exp $"); +__RCSID("$NetBSD: ssl.c,v 1.5.8.3 2022/09/12 17:08:13 martin Exp $"); #endif +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <time.h> #include <unistd.h> -#include <fcntl.h> #include <sys/param.h> #include <sys/select.h> @@ -47,11 +52,14 @@ __RCSID("$NetBSD: ssl.c,v 1.5.8.2 2022/0 #include <netinet/tcp.h> #include <netinet/in.h> + +#ifdef WITH_SSL #include <openssl/crypto.h> #include <openssl/x509.h> #include <openssl/pem.h> #include <openssl/ssl.h> #include <openssl/err.h> +#endif #include "ssl.h" @@ -74,7 +82,9 @@ struct fetch_connect { int issock; int iserr; int iseof; +#ifdef WITH_SSL SSL *ssl; /* SSL handle */ +#endif }; /* @@ -87,6 +97,7 @@ fetch_writev(struct fetch_connect *conn, struct timeval now, timeout, delta; fd_set writefds; ssize_t len, total; + int fd = conn->sd; int r; if (quit_time > 0) { @@ -97,8 +108,8 @@ fetch_writev(struct fetch_connect *conn, total = 0; while (iovcnt > 0) { - while (quit_time > 0 && !FD_ISSET(conn->sd, &writefds)) { - FD_SET(conn->sd, &writefds); + while (quit_time > 0 && !FD_ISSET(fd, &writefds)) { + FD_SET(fd, &writefds); gettimeofday(&now, NULL); delta.tv_sec = timeout.tv_sec - now.tv_sec; delta.tv_usec = timeout.tv_usec - now.tv_usec; @@ -111,7 +122,7 @@ fetch_writev(struct fetch_connect *conn, return -1; } errno = 0; - r = select(conn->sd + 1, NULL, &writefds, NULL, &delta); + r = select(fd + 1, NULL, &writefds, NULL, &delta); if (r == -1) { if (errno == EINTR) continue; @@ -119,10 +130,12 @@ fetch_writev(struct fetch_connect *conn, } } errno = 0; +#ifdef WITH_SSL if (conn->ssl != NULL) len = SSL_write(conn->ssl, iov->iov_base, iov->iov_len); else - len = writev(conn->sd, iov, iovcnt); +#endif + len = writev(fd, iov, iovcnt); if (len == 0) { /* we consider a short write a failure */ /* XXX perhaps we shouldn't in the SSL case */ @@ -130,7 +143,7 @@ fetch_writev(struct fetch_connect *conn, return -1; } if (len < 0) { - if (errno == EINTR) + if (errno == EINTR || errno == EAGAIN) continue; return -1; } @@ -148,11 +161,8 @@ fetch_writev(struct fetch_connect *conn, return total; } -/* - * Write to a connection w/ timeout - */ -static int -fetch_write(struct fetch_connect *conn, const char *str, size_t len) +static ssize_t +fetch_write(const void *str, size_t len, struct fetch_connect *conn) { struct iovec iov[1]; @@ -181,7 +191,7 @@ fetch_printf(struct fetch_connect *conn, return -1; } - r = fetch_write(conn, msg, len); + r = fetch_write(msg, len, conn); free(msg); return r; } @@ -210,15 +220,16 @@ fetch_clearerr(struct fetch_connect *con int fetch_flush(struct fetch_connect *conn) { - int v; if (conn->issock) { + int fd = conn->sd; + int v; #ifdef TCP_NOPUSH v = 0; - setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &v, sizeof(v)); + setsockopt(fd, IPPROTO_TCP, TCP_NOPUSH, &v, sizeof(v)); #endif v = 1; - setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); } return 0; } @@ -271,44 +282,44 @@ fetch_fdopen(int sd, const char *fmode) int fetch_close(struct fetch_connect *conn) { - int rv = 0; + if (conn == NULL) + return 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; + fetch_flush(conn); +#ifdef WITH_SSL + SSL_free(conn->ssl); +#endif + close(conn->sd); + free(conn->cache.buf); + free(conn->buf); + free(conn); + return 0; } +#define FETCH_WRITE_WAIT -3 #define FETCH_READ_WAIT -2 #define FETCH_READ_ERROR -1 +#ifdef WITH_SSL 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; - } + if (rlen >= 0) + return rlen; + + switch (SSL_get_error(ssl, rlen)) { + case SSL_ERROR_WANT_READ: + return FETCH_READ_WAIT; + case SSL_ERROR_WANT_WRITE: + return FETCH_WRITE_WAIT; + default: ERR_print_errors_fp(ttyout); return FETCH_READ_ERROR; } - return rlen; } +#endif /* WITH_SSL */ static ssize_t fetch_nonssl_read(int sd, void *buf, size_t len) @@ -316,7 +327,7 @@ fetch_nonssl_read(int sd, void *buf, siz ssize_t rlen; rlen = read(sd, buf, len); - if (rlen < 0) { + if (rlen == -1) { if (errno == EAGAIN || errno == EINTR) return FETCH_READ_WAIT; return FETCH_READ_ERROR; @@ -347,14 +358,49 @@ fetch_cache_data(struct fetch_connect *c return 0; } -ssize_t +static int +fetch_wait(struct fetch_connect *conn, ssize_t rlen, struct timeval *timeout) +{ + struct timeval now, delta; + int fd = conn->sd; + fd_set fds; + + FD_ZERO(&fds); + while (!FD_ISSET(fd, &fds)) { + FD_SET(fd, &fds); + if (quit_time > 0) { + gettimeofday(&now, NULL); + if (!timercmp(timeout, &now, >)) { + fprintf(ttyout, "\r\n%s: transfer aborted" + " because stalled for %lu sec.\r\n", + getprogname(), (unsigned long)quit_time); + errno = ETIMEDOUT; + conn->iserr = ETIMEDOUT; + return -1; + } + timersub(timeout, &now, &delta); + } + errno = 0; + if (select(fd + 1, + rlen == FETCH_READ_WAIT ? &fds : NULL, + rlen == FETCH_WRITE_WAIT ? &fds : NULL, + NULL, quit_time > 0 ? &delta : NULL) < 0) { + if (errno == EINTR) + continue; + conn->iserr = errno; + return -1; + } + } + return 0; +} + +size_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; + struct timeval timeout; if (quit_time > 0) { gettimeofday(&timeout, NULL); @@ -402,40 +448,31 @@ fetch_read(void *ptr, size_t size, size_ * In the non-SSL case, it may improve performance (very * slightly) when reading small amounts of data. */ +#ifdef WITH_SSL if (conn->ssl != NULL) rlen = fetch_ssl_read(conn->ssl, buf, len); else +#endif rlen = fetch_nonssl_read(conn->sd, buf, len); - if (rlen == 0) { + switch (rlen) { + case 0: + conn->iseof = 1; + return total; + case FETCH_READ_ERROR: + conn->iserr = errno; + if (errno == EINTR) + fetch_cache_data(conn, start, total); + return 0; + case FETCH_READ_WAIT: + case FETCH_WRITE_WAIT: + if (fetch_wait(conn, rlen, &timeout) == -1) + return 0; break; - } else if (rlen > 0) { + default: 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; - } + break; } } return total; @@ -450,7 +487,7 @@ char * fetch_getln(char *str, int size, struct fetch_connect *conn) { size_t tmpsize; - ssize_t len; + size_t len; char c; if (conn->buf == NULL) { @@ -473,13 +510,12 @@ fetch_getln(char *str, int size, struct 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; + if (conn->iserr) + return NULL; + if (conn->iseof) + break; + abort(); } conn->buf[conn->buflen++] = c; if (conn->buflen == conn->bufsize) { @@ -531,8 +567,8 @@ fetch_getline(struct fetch_connect *conn } 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') + size_t rlen = fetch_read(&c, sizeof(c), 1, conn); + if (rlen == 0 || c == '\n') break; } if (errormsg) @@ -545,12 +581,15 @@ fetch_getline(struct fetch_connect *conn return len; } +#ifdef WITH_SSL void * fetch_start_ssl(int sock, const char *servername) { SSL *ssl; SSL_CTX *ctx; + X509_VERIFY_PARAM *param; int ret, ssl_err; + int verify = 0; // getenv("NO_CERT_VERIFY") == NULL; /* Init the SSL library and context */ if (!SSL_library_init()){ @@ -562,6 +601,10 @@ fetch_start_ssl(int sock, const char *se ctx = SSL_CTX_new(SSLv23_client_method()); SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + if (verify) { + SSL_CTX_set_default_verify_paths(ctx); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); + } ssl = SSL_new(ctx); if (ssl == NULL){ @@ -569,6 +612,19 @@ fetch_start_ssl(int sock, const char *se SSL_CTX_free(ctx); return NULL; } + + if (verify) { + param = SSL_get0_param(ssl); + if (!X509_VERIFY_PARAM_set1_host(param, servername, + strlen(servername))) { + fprintf(ttyout, "SSL verification setup failed\n"); + return NULL; + } + + /* Enable peer verification, (using the default callback) */ + SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL); + } + SSL_set_fd(ssl, sock); if (!SSL_set_tlsext_host_name(ssl, __UNCONST(servername))) { fprintf(ttyout, "SSL hostname setting failed\n"); @@ -605,10 +661,13 @@ fetch_start_ssl(int sock, const char *se return ssl; } +#endif /* WITH_SSL */ void fetch_set_ssl(struct fetch_connect *conn, void *ssl) { +#ifdef WITH_SSL conn->ssl = ssl; +#endif } Index: src/usr.bin/ftp/ssl.h diff -u src/usr.bin/ftp/ssl.h:1.3.8.2 src/usr.bin/ftp/ssl.h:1.3.8.3 --- src/usr.bin/ftp/ssl.h:1.3.8.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/ssl.h Mon Sep 12 17:08:13 2022 @@ -1,7 +1,7 @@ -/* $NetBSD: ssl.h,v 1.3.8.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: ssl.h,v 1.3.8.3 2022/09/12 17:08:13 martin Exp $ */ /*- - * Copyright (c) 2012 The NetBSD Foundation, Inc. + * Copyright (c) 2012-2021 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,7 +25,6 @@ * 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; @@ -38,26 +37,8 @@ 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_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, const char *); - -#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 */ Index: src/usr.bin/ftp/util.c diff -u src/usr.bin/ftp/util.c:1.158.22.2 src/usr.bin/ftp/util.c:1.158.22.3 --- src/usr.bin/ftp/util.c:1.158.22.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/util.c Mon Sep 12 17:08:13 2022 @@ -1,7 +1,7 @@ -/* $NetBSD: util.c,v 1.158.22.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: util.c,v 1.158.22.3 2022/09/12 17:08:13 martin Exp $ */ /*- - * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. + * Copyright (c) 1997-2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -64,7 +64,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: util.c,v 1.158.22.2 2022/09/12 15:05:21 martin Exp $"); +__RCSID("$NetBSD: util.c,v 1.158.22.3 2022/09/12 17:08:13 martin Exp $"); #endif /* not lint */ /* @@ -171,7 +171,7 @@ parse_feat(const char *fline) * work-around broken ProFTPd servers that can't * even obey RFC 2389. */ - while (*fline && isspace((int)*fline)) + while (*fline && isspace((unsigned char)*fline)) fline++; if (strcasecmp(fline, "MDTM") == 0) @@ -324,9 +324,10 @@ intr(int signo) /* * Signal handler for lost connections; cleanup various elements of * the connection state, and call cleanuppeer() to finish it off. + * This function is not signal safe, so exit if called by a signal. */ void -lostpeer(int dummy) +lostpeer(int signo) { int oerrno = errno; @@ -356,6 +357,9 @@ lostpeer(int dummy) proxflag = 0; pswitch(0); cleanuppeer(); + if (signo) { + errx(1, "lostpeer due to signal %d", signo); + } errno = oerrno; } @@ -478,7 +482,8 @@ ftp_login(const char *host, const char * } } updatelocalcwd(); - updateremotecwd(); + remotecwd[0] = '\0'; + remcwdvalid = 0; cleanup_ftp_login: FREEPTR(fuser); @@ -615,7 +620,7 @@ remglob(char *argv[], int doswitch, cons * return value. Can't control multiple values being expanded from the * expression, we return only the first. * Returns NULL on error, or a pointer to a buffer containing the filename - * that's the caller's responsiblity to free(3) when finished with. + * that's the caller's responsibility to free(3) when finished with. */ char * globulize(const char *pattern) @@ -726,7 +731,7 @@ remotemodtime(const char *file, int nois *frac++ = '\0'; if (strlen(timestr) == 15 && strncmp(timestr, "191", 3) == 0) { /* - * XXX: Workaround for lame ftpd's that return + * XXX: Workaround for buggy ftp servers that return * `19100' instead of `2000' */ fprintf(ttyout, @@ -835,6 +840,7 @@ updateremotecwd(void) size_t i; char *cp; + remcwdvalid = 1; /* whether it works or not, we are done */ overbose = verbose; ocode = code; if (ftp_debug == 0) @@ -1174,6 +1180,8 @@ formatbuf(char *buf, size_t len, const c case '/': case '.': case 'c': + if (connected && !remcwdvalid) + updateremotecwd(); p2 = connected ? remotecwd : ""; updirs = pdirs = 0; @@ -1487,6 +1495,7 @@ ftp_poll(struct pollfd *fds, int nfds, i return poll(fds, nfds, timeout); } +#ifndef SMALL /* * malloc() with inbuilt error checking */ @@ -1541,3 +1550,4 @@ ftp_strdup(const char *str) err(1, "Unable to allocate memory for string copy"); return (s); } +#endif Index: src/usr.bin/ftp/version.h diff -u src/usr.bin/ftp/version.h:1.87.8.2 src/usr.bin/ftp/version.h:1.87.8.3 --- src/usr.bin/ftp/version.h:1.87.8.2 Mon Sep 12 15:05:21 2022 +++ src/usr.bin/ftp/version.h Mon Sep 12 17:08:13 2022 @@ -1,7 +1,7 @@ -/* $NetBSD: version.h,v 1.87.8.2 2022/09/12 15:05:21 martin Exp $ */ +/* $NetBSD: version.h,v 1.87.8.3 2022/09/12 17:08:13 martin Exp $ */ /*- - * Copyright (c) 1999-2015 The NetBSD Foundation, Inc. + * Copyright (c) 1999-2021 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -34,5 +34,5 @@ #endif #ifndef FTP_VERSION -#define FTP_VERSION "20150912" +#define FTP_VERSION "20210826" #endif