Module Name: othersrc Committed By: lukem Date: Sat Jul 4 09:59:07 UTC 2020
Modified Files: othersrc/usr.bin/tnftp/src: cmds.c complete.c domacro.c extern.h fetch.c ftp.1 ftp.c ftp_var.h main.c progressbar.c ssl.c ssl.h util.c version.h Log Message: Merge NetBSD-ftp 20200608 To generate a diff of this commit: cvs rdiff -u -r1.18 -r1.19 othersrc/usr.bin/tnftp/src/cmds.c cvs rdiff -u -r1.10 -r1.11 othersrc/usr.bin/tnftp/src/complete.c cvs rdiff -u -r1.8 -r1.9 othersrc/usr.bin/tnftp/src/domacro.c cvs rdiff -u -r1.13 -r1.14 othersrc/usr.bin/tnftp/src/extern.h cvs rdiff -u -r1.23 -r1.24 othersrc/usr.bin/tnftp/src/fetch.c \ othersrc/usr.bin/tnftp/src/util.c cvs rdiff -u -r1.15 -r1.16 othersrc/usr.bin/tnftp/src/ftp.1 \ othersrc/usr.bin/tnftp/src/progressbar.c cvs rdiff -u -r1.21 -r1.22 othersrc/usr.bin/tnftp/src/ftp.c cvs rdiff -u -r1.12 -r1.13 othersrc/usr.bin/tnftp/src/ftp_var.h cvs rdiff -u -r1.20 -r1.21 othersrc/usr.bin/tnftp/src/main.c cvs rdiff -u -r1.3 -r1.4 othersrc/usr.bin/tnftp/src/ssl.c \ othersrc/usr.bin/tnftp/src/ssl.h cvs rdiff -u -r1.7 -r1.8 othersrc/usr.bin/tnftp/src/version.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: othersrc/usr.bin/tnftp/src/cmds.c diff -u othersrc/usr.bin/tnftp/src/cmds.c:1.18 othersrc/usr.bin/tnftp/src/cmds.c:1.19 --- othersrc/usr.bin/tnftp/src/cmds.c:1.18 Sun May 5 11:17:30 2013 +++ othersrc/usr.bin/tnftp/src/cmds.c Sat Jul 4 09:59:07 2020 @@ -1,5 +1,5 @@ -/* $NetBSD: cmds.c,v 1.18 2013/05/05 11:17:30 lukem Exp $ */ -/* from NetBSD: cmds.c,v 1.135 2012/12/22 16:57:09 christos Exp */ +/* $NetBSD: cmds.c,v 1.19 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: cmds.c,v 1.140 2019/02/06 07:56:42 martin Exp */ /*- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. @@ -101,7 +101,7 @@ #if 0 static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94"; #else -__RCSID(" NetBSD: cmds.c,v 1.135 2012/12/22 16:57:09 christos Exp "); +__RCSID(" NetBSD: cmds.c,v 1.140 2019/02/06 07:56:42 martin Exp "); #endif #endif /* not lint */ @@ -1165,7 +1165,8 @@ cd(int argc, char *argv[]) } if (r == COMPLETE) { dirchange = 1; - updateremotecwd(); + remotecwd[0] = '\0'; + remcwdvalid = 0; } } @@ -1551,9 +1552,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); @@ -1782,6 +1783,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. @@ -1974,15 +1987,15 @@ dotrans(char *dst, size_t dlen, const ch char *cp2 = dst; size_t i, ostop; - for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) + for (ostop = 0; ntout[ostop] && ostop < sizeof(ntout); ostop++) continue; for (cp1 = src; *cp1; cp1++) { int found = 0; - for (i = 0; *(ntin + i) && i < 16; i++) { - if (*cp1 == *(ntin + i)) { + for (i = 0; i < sizeof(ntin) && ntin[i]; i++) { + if (*cp1 == ntin[i]) { found++; if (i < ostop) { - *cp2++ = *(ntout + i); + *cp2++ = ntout[i]; if (cp2 - dst >= (ptrdiff_t)(dlen - 1)) goto out; } @@ -2191,7 +2204,7 @@ LOOP: } break; } - /* intentional drop through */ + /* FALLTHROUGH */ default: *cp2++ = *cp1; break; @@ -2366,7 +2379,8 @@ cdup(int argc, char *argv[]) } if (r == COMPLETE) { dirchange = 1; - updateremotecwd(); + remotecwd[0] = '\0'; + remcwdvalid = 0; } } Index: othersrc/usr.bin/tnftp/src/complete.c diff -u othersrc/usr.bin/tnftp/src/complete.c:1.10 othersrc/usr.bin/tnftp/src/complete.c:1.11 --- othersrc/usr.bin/tnftp/src/complete.c:1.10 Wed May 20 12:53:47 2009 +++ othersrc/usr.bin/tnftp/src/complete.c Sat Jul 4 09:59:07 2020 @@ -1,5 +1,5 @@ -/* $NetBSD: complete.c,v 1.10 2009/05/20 12:53:47 lukem Exp $ */ -/* from NetBSD: complete.c,v 1.46 2009/04/12 10:18:52 lukem Exp */ +/* $NetBSD: complete.c,v 1.11 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: complete.c,v 1.47 2019/01/28 12:04:16 christos Exp */ /*- * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. @@ -36,7 +36,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID(" NetBSD: complete.c,v 1.46 2009/04/12 10:18:52 lukem Exp "); +__RCSID(" NetBSD: complete.c,v 1.47 2019/01/28 12:04:16 christos Exp "); #endif /* not lint */ /* @@ -106,11 +106,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: othersrc/usr.bin/tnftp/src/domacro.c diff -u othersrc/usr.bin/tnftp/src/domacro.c:1.8 othersrc/usr.bin/tnftp/src/domacro.c:1.9 --- othersrc/usr.bin/tnftp/src/domacro.c:1.8 Wed May 20 12:53:47 2009 +++ othersrc/usr.bin/tnftp/src/domacro.c Sat Jul 4 09:59:07 2020 @@ -1,5 +1,5 @@ -/* $NetBSD: domacro.c,v 1.8 2009/05/20 12:53:47 lukem Exp $ */ -/* from NetBSD: domacro.c,v 1.22 2009/04/12 10:18:52 lukem Exp */ +/* $NetBSD: domacro.c,v 1.9 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: domacro.c,v 1.23 2019/02/03 03:19:29 mrg Exp */ /* * Copyright (c) 1985, 1993, 1994 @@ -39,7 +39,7 @@ #if 0 static char sccsid[] = "@(#)domacro.c 8.3 (Berkeley) 4/2/94"; #else -__RCSID(" NetBSD: domacro.c,v 1.22 2009/04/12 10:18:52 lukem Exp "); +__RCSID(" NetBSD: domacro.c,v 1.23 2019/02/03 03:19:29 mrg Exp "); #endif #endif /* not lint */ @@ -109,7 +109,7 @@ domacro(int argc, char *argv[]) } break; } - /* intentional drop through */ + /* FALLTHROUGH */ default: *cp2++ = *cp1; break; Index: othersrc/usr.bin/tnftp/src/extern.h diff -u othersrc/usr.bin/tnftp/src/extern.h:1.13 othersrc/usr.bin/tnftp/src/extern.h:1.14 --- othersrc/usr.bin/tnftp/src/extern.h:1.13 Sun May 5 11:17:30 2013 +++ othersrc/usr.bin/tnftp/src/extern.h Sat Jul 4 09:59:07 2020 @@ -1,5 +1,5 @@ -/* $NetBSD: extern.h,v 1.13 2013/05/05 11:17:30 lukem Exp $ */ -/* from NetBSD: extern.h,v 1.80 2012/07/04 06:09:37 is Exp */ +/* $NetBSD: extern.h,v 1.14 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: extern.h,v 1.82 2019/06/22 23:40:53 christos Exp */ /*- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. @@ -174,6 +174,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 *, @@ -243,7 +244,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: othersrc/usr.bin/tnftp/src/fetch.c diff -u othersrc/usr.bin/tnftp/src/fetch.c:1.23 othersrc/usr.bin/tnftp/src/fetch.c:1.24 --- othersrc/usr.bin/tnftp/src/fetch.c:1.23 Sun Dec 13 14:08:09 2015 +++ othersrc/usr.bin/tnftp/src/fetch.c Sat Jul 4 09:59:07 2020 @@ -1,5 +1,5 @@ -/* $NetBSD: fetch.c,v 1.23 2015/12/13 14:08:09 tron Exp $ */ -/* from NetBSD: fetch.c,v 1.207 2015/09/12 19:38:42 wiz Exp */ +/* $NetBSD: fetch.c,v 1.24 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: fetch.c,v 1.231 2019/04/04 00:36:09 christos Exp */ /*- * Copyright (c) 1997-2015 The NetBSD Foundation, Inc. @@ -42,7 +42,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID(" NetBSD: fetch.c,v 1.207 2015/09/12 19:38:42 wiz Exp "); +__RCSID(" NetBSD: fetch.c,v 1.231 2019/04/04 00:36:09 christos Exp "); #endif /* not lint */ /* @@ -81,27 +81,47 @@ __RCSID(" NetBSD: fetch.c,v 1.207 2015/0 typedef enum { UNKNOWN_URL_T=-1, HTTP_URL_T, -#ifdef WITH_SSL HTTPS_URL_T, -#endif FTP_URL_T, FILE_URL_T, CLASSIC_URL_T } url_t; +struct authinfo { + char *auth; + char *user; + char *pass; +}; + +struct urlinfo { + char *host; + char *port; + char *path; + url_t utype; + in_port_t portnum; +}; + +struct posinfo { + off_t rangestart; + off_t rangeend; + off_t entitylen; +}; + __dead static void aborthttp(int); __dead static void timeouthttp(int); #ifndef NO_AUTH -static int auth_url(const char *, char **, const char *, const char *); +static 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 fetch_ftp(const char *); static int fetch_url(const char *, const char *, char *, char *); static const char *match_token(const char **, const char *); -static int parse_url(const char *, const char *, url_t *, char **, - char **, char **, char **, in_port_t *, char **); +static int parse_url(const char *, const char *, struct urlinfo *, + struct authinfo *); static void url_decode(char *); +static void freeauthinfo(struct authinfo *); +static void freeurlinfo(struct urlinfo *); static int redirect_loop; @@ -153,6 +173,54 @@ match_token(const char **buf, const char return orig; } +static void +initposinfo(struct posinfo *pi) +{ + pi->rangestart = pi->rangeend = pi->entitylen = -1; +} + +static void +initauthinfo(struct authinfo *ai, char *auth) +{ + ai->auth = auth; + ai->user = ai->pass = 0; +} + +static void +freeauthinfo(struct authinfo *a) +{ + FREEPTR(a->user); + if (a->pass != NULL) + memset(a->pass, 0, strlen(a->pass)); + FREEPTR(a->pass); +} + +static void +initurlinfo(struct urlinfo *ui) +{ + ui->host = ui->port = ui->path = 0; + ui->utype = UNKNOWN_URL_T; + ui->portnum = 0; +} + +static void +copyurlinfo(struct urlinfo *dui, struct urlinfo *sui) +{ + dui->host = ftp_strdup(sui->host); + dui->port = ftp_strdup(sui->port); + dui->path = ftp_strdup(sui->path); + dui->utype = sui->utype; + dui->portnum = sui->portnum; +} + +static void +freeurlinfo(struct urlinfo *ui) +{ + FREEPTR(ui->host); + FREEPTR(ui->port); + FREEPTR(ui->path); +} + #ifndef NO_AUTH /* * Generate authorization response based on given authentication challenge. @@ -160,8 +228,7 @@ match_token(const char **buf, const char * Sets response to a malloc(3)ed string; caller should free. */ static int -auth_url(const char *challenge, char **response, const char *guser, - const char *gpass) +auth_url(const char *challenge, char **response, const struct authinfo *auth) { const char *cp, *scheme, *errormsg; char *ep, *clear, *realm; @@ -205,8 +272,8 @@ auth_url(const char *challenge, char **r } fprintf(ttyout, "Username for `%s': ", realm); - if (guser != NULL) { - (void)strlcpy(uuser, guser, sizeof(uuser)); + if (auth->user != NULL) { + (void)strlcpy(uuser, auth->user, sizeof(uuser)); fprintf(ttyout, "%s\n", uuser); } else { (void)fflush(ttyout); @@ -215,8 +282,8 @@ auth_url(const char *challenge, char **r goto cleanup_auth_url; } } - if (gpass != NULL) - upass = gpass; + if (auth->pass != NULL) + upass = auth->pass; else { gotpass = getpass("Password: "); if (gotpass == NULL) { @@ -236,7 +303,7 @@ auth_url(const char *challenge, char **r /* scheme + " " + enc + "\0" */ rlen = strlen(scheme) + 1 + (clen + 2) * 4 / 3 + 1; - *response = (char *)ftp_malloc(rlen); + *response = ftp_malloc(rlen); (void)strlcpy(*response, scheme, rlen); len = strlcat(*response, " ", rlen); /* use `clen - 1' to not encode the trailing NUL */ @@ -335,57 +402,48 @@ url_decode(char *url) * "ftp://host/dir/file" "dir/file" * "ftp://host//dir/file" "/dir/file" */ + static int -parse_url(const char *url, const char *desc, url_t *utype, - char **uuser, char **pass, char **host, char **port, - in_port_t *portnum, char **path) +parse_url(const char *url, const char *desc, struct urlinfo *ui, + struct authinfo *auth) { const char *origurl, *tport; char *cp, *ep, *thost; size_t len; - if (url == NULL || desc == NULL || utype == NULL || uuser == NULL - || pass == NULL || host == NULL || port == NULL || portnum == NULL - || path == NULL) + if (url == NULL || desc == NULL || ui == NULL || auth == NULL) errx(1, "parse_url: invoked with NULL argument!"); DPRINTF("parse_url: %s `%s'\n", desc, url); origurl = url; - *utype = UNKNOWN_URL_T; - *uuser = *pass = *host = *port = *path = NULL; - *portnum = 0; tport = NULL; if (STRNEQUAL(url, HTTP_URL)) { url += sizeof(HTTP_URL) - 1; - *utype = HTTP_URL_T; - *portnum = HTTP_PORT; + ui->utype = HTTP_URL_T; + ui->portnum = HTTP_PORT; tport = httpport; } else if (STRNEQUAL(url, FTP_URL)) { url += sizeof(FTP_URL) - 1; - *utype = FTP_URL_T; - *portnum = FTP_PORT; + ui->utype = FTP_URL_T; + ui->portnum = FTP_PORT; tport = ftpport; } else if (STRNEQUAL(url, FILE_URL)) { url += sizeof(FILE_URL) - 1; - *utype = FILE_URL_T; + ui->utype = FILE_URL_T; + tport = ""; #ifdef WITH_SSL } else if (STRNEQUAL(url, HTTPS_URL)) { url += sizeof(HTTPS_URL) - 1; - *utype = HTTPS_URL_T; - *portnum = HTTPS_PORT; + ui->utype = HTTPS_URL_T; + ui->portnum = HTTPS_PORT; tport = httpsport; #endif } else { warnx("Invalid %s `%s'", desc, url); cleanup_parse_url: - FREEPTR(*uuser); - if (*pass != NULL) - memset(*pass, 0, strlen(*pass)); - FREEPTR(*pass); - FREEPTR(*host); - FREEPTR(*port); - FREEPTR(*path); + freeauthinfo(auth); + freeurlinfo(ui); return (-1); } @@ -400,26 +458,26 @@ parse_url(const char *url, const char *d 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 (ui->utype == FTP_URL_T) /* skip first / for ftp URLs */ ep++; - *path = ftp_strdup(ep); + ui->path = ftp_strdup(ep); } cp = strchr(thost, '@'); /* look for user[:pass]@ in URLs */ if (cp != NULL) { - if (*utype == FTP_URL_T) + if (ui->utype == FTP_URL_T) anonftp = 0; /* disable anonftp */ - *uuser = thost; + auth->user = thost; *cp = '\0'; thost = ftp_strdup(cp + 1); - cp = strchr(*uuser, ':'); + cp = strchr(auth->user, ':'); if (cp != NULL) { *cp = '\0'; - *pass = ftp_strdup(cp + 1); + auth->pass = ftp_strdup(cp + 1); } - url_decode(*uuser); - if (*pass) - url_decode(*pass); + url_decode(auth->user); + if (auth->pass) + url_decode(auth->pass); } #ifdef INET6 @@ -453,7 +511,7 @@ parse_url(const char *url, const char *d #endif /* INET6 */ if ((cp = strchr(thost, ':')) != NULL) *cp++ = '\0'; - *host = thost; + ui->host = thost; /* look for [:port] */ if (cp != NULL) { @@ -466,30 +524,742 @@ parse_url(const char *url, const char *d cp, desc, origurl); goto cleanup_parse_url; } - *portnum = nport; + ui->portnum = nport; tport = cp; } if (tport != NULL) - *port = ftp_strdup(tport); - if (*path == NULL) { + ui->port = ftp_strdup(tport); + if (ui->path == NULL) { const char *emptypath = "/"; - if (*utype == FTP_URL_T) /* skip first / for ftp URLs */ + if (ui->utype == FTP_URL_T) /* skip first / for ftp URLs */ emptypath++; - *path = ftp_strdup(emptypath); + ui->path = ftp_strdup(emptypath); } DPRINTF("parse_url: user `%s' pass `%s' host %s port %s(%d) " "path `%s'\n", - STRorNULL(*uuser), STRorNULL(*pass), - STRorNULL(*host), STRorNULL(*port), - *portnum ? *portnum : -1, STRorNULL(*path)); + STRorNULL(auth->user), STRorNULL(auth->pass), + STRorNULL(ui->host), STRorNULL(ui->port), + ui->portnum ? ui->portnum : -1, STRorNULL(ui->path)); return (0); } sigjmp_buf httpabort; +static int +ftp_socket(const struct urlinfo *ui, void **ssl) +{ + struct addrinfo hints, *res, *res0 = NULL; + int error; + int s; + const char *host = ui->host; + const char *port = ui->port; + + if (ui->utype != HTTPS_URL_T) + ssl = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = 0; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + error = getaddrinfo(host, port, &hints, &res0); + if (error) { + warnx("Can't LOOKUP `%s:%s': %s", host, port, + (error == EAI_SYSTEM) ? strerror(errno) + : gai_strerror(error)); + return -1; + } + + if (res0->ai_canonname) + host = res0->ai_canonname; + + s = -1; + if (ssl) + *ssl = NULL; + for (res = res0; res; res = res->ai_next) { + char hname[NI_MAXHOST], sname[NI_MAXSERV]; + + ai_unmapped(res); + if (getnameinfo(res->ai_addr, res->ai_addrlen, + hname, sizeof(hname), sname, sizeof(sname), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + strlcpy(hname, "?", sizeof(hname)); + strlcpy(sname, "?", sizeof(sname)); + } + + if (verbose && res0->ai_next) { +#ifdef INET6 + if(res->ai_family == AF_INET6) { + fprintf(ttyout, "Trying [%s]:%s ...\n", + hname, sname); + } else { +#endif + fprintf(ttyout, "Trying %s:%s ...\n", + hname, sname); +#ifdef INET6 + } +#endif + } + + s = socket(res->ai_family, SOCK_STREAM, res->ai_protocol); + if (s < 0) { + warn( + "Can't create socket for connection to " + "`%s:%s'", hname, sname); + continue; + } + + if (ftp_connect(s, res->ai_addr, res->ai_addrlen, + verbose || !res->ai_next) < 0) { + close(s); + s = -1; + continue; + } + +#ifdef WITH_SSL + if (ssl) { + if ((*ssl = fetch_start_ssl(s, host)) == NULL) { + close(s); + s = -1; + continue; + } + } +#endif + break; + } + if (res0) + freeaddrinfo(res0); + return s; +} + +static int +handle_noproxy(const char *host, in_port_t portnum) +{ + + char *cp, *ep, *np, *np_copy, *np_iter, *no_proxy; + unsigned long np_port; + size_t hlen, plen; + int isproxy = 1; + + /* check URL against list of no_proxied sites */ + no_proxy = getoptionvalue("no_proxy"); + if (EMPTYSTRING(no_proxy)) + return isproxy; + + np_iter = np_copy = ftp_strdup(no_proxy); + hlen = strlen(host); + while ((cp = strsep(&np_iter, " ,")) != NULL) { + if (*cp == '\0') + continue; + if ((np = strrchr(cp, ':')) != NULL) { + *np++ = '\0'; + np_port = strtoul(np, &ep, 10); + if (*np == '\0' || *ep != '\0') + continue; + if (np_port != portnum) + continue; + } + plen = strlen(cp); + if (hlen < plen) + continue; + if (strncasecmp(host + hlen - plen, cp, plen) == 0) { + isproxy = 0; + break; + } + } + FREEPTR(np_copy); + return isproxy; +} + +static int +handle_proxy(const char *url, const char *penv, struct urlinfo *ui, + struct authinfo *pauth) +{ + struct urlinfo pui; + + if (isipv6addr(ui->host) && strchr(ui->host, '%') != NULL) { + warnx("Scoped address notation `%s' disallowed via web proxy", + ui->host); + return -1; + } + + initurlinfo(&pui); + if (parse_url(penv, "proxy URL", &pui, pauth) == -1) + return -1; + + if ((!IS_HTTP_TYPE(pui.utype) && pui.utype != FTP_URL_T) || + EMPTYSTRING(pui.host) || + (! EMPTYSTRING(pui.path) && strcmp(pui.path, "/") != 0)) { + warnx("Malformed proxy URL `%s'", penv); + freeurlinfo(&pui); + return -1; + } + + FREEPTR(pui.path); + pui.path = ftp_strdup(url); + + freeurlinfo(ui); + *ui = pui; + + return 0; +} + +static void +print_host(FETCH *fin, const struct urlinfo *ui) +{ + char *h, *p; + + if (strchr(ui->host, ':') == NULL) { + fetch_printf(fin, "Host: %s", ui->host); + } else { + /* + * strip off IPv6 scope identifier, since it is + * local to the node + */ + h = ftp_strdup(ui->host); + if (isipv6addr(h) && (p = strchr(h, '%')) != NULL) + *p = '\0'; + + fetch_printf(fin, "Host: [%s]", h); + free(h); + } + + if ((ui->utype == HTTP_URL_T && ui->portnum != HTTP_PORT) || + (ui->utype == HTTPS_URL_T && ui->portnum != HTTPS_PORT)) + fetch_printf(fin, ":%u", ui->portnum); + fetch_printf(fin, "\r\n"); +} + +static void +print_agent(FETCH *fin) +{ + const char *useragent; + if ((useragent = getenv("FTPUSERAGENT")) != NULL) { + fetch_printf(fin, "User-Agent: %s\r\n", useragent); + } else { + fetch_printf(fin, "User-Agent: %s/%s\r\n", + FTP_PRODUCT, FTP_VERSION); + } +} + +static void +print_cache(FETCH *fin, int isproxy) +{ + fetch_printf(fin, isproxy ? + "Pragma: no-cache\r\n" : + "Cache-Control: no-cache\r\n"); +} + +static int +print_get(FETCH *fin, int hasleading, int isproxy, const struct urlinfo *oui, + const struct urlinfo *ui) +{ + const char *leading = hasleading ? ", " : " ("; + + if (isproxy) { + if (verbose) { + fprintf(ttyout, "%svia %s:%u", leading, + ui->host, ui->portnum); + leading = ", "; + hasleading++; + } + fetch_printf(fin, "GET %s HTTP/1.0\r\n", ui->path); + print_host(fin, oui); + return hasleading; + } + + fetch_printf(fin, "GET %s HTTP/1.1\r\n", ui->path); + print_host(fin, ui); + fetch_printf(fin, "Accept: */*\r\n"); + fetch_printf(fin, "Connection: close\r\n"); + if (restart_point) { + fputs(leading, ttyout); + fetch_printf(fin, "Range: bytes=" LLF "-\r\n", + (LLT)restart_point); + fprintf(ttyout, "restarting at " LLF, (LLT)restart_point); + hasleading++; + } + return hasleading; +} + +static void +getmtime(const char *cp, time_t *mtime) +{ + struct tm parsed; + const char *t; + + memset(&parsed, 0, sizeof(parsed)); + t = parse_rfc2616time(&parsed, cp); + + if (t == NULL) + return; + + parsed.tm_isdst = -1; + if (*t == '\0') + *mtime = timegm(&parsed); + +#ifndef NO_DEBUG + if (ftp_debug && *mtime != -1) { + fprintf(ttyout, "parsed time as: %s", + rfc2822time(localtime(mtime))); + } +#endif +} + +static int +print_proxy(FETCH *fin, int hasleading, const char *wwwauth, + const char *proxyauth) +{ + const char *leading = hasleading ? ", " : " ("; + + if (wwwauth) { + if (verbose) { + fprintf(ttyout, "%swith authorization", leading); + hasleading++; + } + fetch_printf(fin, "Authorization: %s\r\n", wwwauth); + } + if (proxyauth) { + if (verbose) { + fprintf(ttyout, "%swith proxy authorization", leading); + hasleading++; + } + fetch_printf(fin, "Proxy-Authorization: %s\r\n", proxyauth); + } + return hasleading; +} + +#ifdef WITH_SSL +static void +print_connect(FETCH *fin, const struct urlinfo *ui) +{ + char hname[NI_MAXHOST], *p; + const char *h; + + if (isipv6addr(ui->host)) { + /* + * strip off IPv6 scope identifier, + * since it is local to the node + */ + if ((p = strchr(ui->host, '%')) == NULL) + snprintf(hname, sizeof(hname), "[%s]", ui->host); + else + snprintf(hname, sizeof(hname), "[%.*s]", + (int)(p - ui->host), ui->host); + h = hname; + } else + h = ui->host; + + fetch_printf(fin, "CONNECT %s:%d HTTP/1.1\r\n", h, ui->portnum); + fetch_printf(fin, "Host: %s:%d\r\n", h, ui->portnum); +} +#endif + +#define C_OK 0 +#define C_CLEANUP 1 +#define C_IMPROPER 2 + +static int +getresponseline(FETCH *fin, char *buf, size_t buflen, int *len) +{ + const char *errormsg; + + alarmtimer(quit_time ? quit_time : 60); + *len = fetch_getline(fin, buf, buflen, &errormsg); + alarmtimer(0); + if (*len < 0) { + if (*errormsg == '\n') + errormsg++; + warnx("Receiving HTTP reply: %s", errormsg); + return C_CLEANUP; + } + while (*len > 0 && (ISLWS(buf[*len-1]))) + buf[--*len] = '\0'; + + if (*len) + DPRINTF("%s: received `%s'\n", __func__, buf); + return C_OK; +} + +static int +getresponse(FETCH *fin, char **cp, size_t buflen, int *hcode) +{ + int len, rv; + char *ep, *buf = *cp; + + *hcode = 0; + if ((rv = getresponseline(fin, buf, buflen, &len)) != C_OK) + return rv; + + /* Determine HTTP response code */ + *cp = strchr(buf, ' '); + if (*cp == NULL) + return C_IMPROPER; + + (*cp)++; + + *hcode = strtol(*cp, &ep, 10); + if (*ep != '\0' && !isspace((unsigned char)*ep)) + return C_IMPROPER; + + return C_OK; +} + +static int +parse_posinfo(const char **cp, struct posinfo *pi) +{ + char *ep; + if (!match_token(cp, "bytes")) + return -1; + + if (**cp == '*') + (*cp)++; + else { + pi->rangestart = STRTOLL(*cp, &ep, 10); + if (pi->rangestart < 0 || *ep != '-') + return -1; + *cp = ep + 1; + pi->rangeend = STRTOLL(*cp, &ep, 10); + if (pi->rangeend < 0 || pi->rangeend < pi->rangestart) + return -1; + *cp = ep; + } + if (**cp != '/') + return -1; + (*cp)++; + if (**cp == '*') + (*cp)++; + else { + pi->entitylen = STRTOLL(*cp, &ep, 10); + if (pi->entitylen < 0) + return -1; + *cp = ep; + } + if (**cp != '\0') + return -1; + +#ifndef NO_DEBUG + if (ftp_debug) { + fprintf(ttyout, "parsed range as: "); + if (pi->rangestart == -1) + fprintf(ttyout, "*"); + else + fprintf(ttyout, LLF "-" LLF, (LLT)pi->rangestart, + (LLT)pi->rangeend); + fprintf(ttyout, "/" LLF "\n", (LLT)pi->entitylen); + } +#endif + return 0; +} + +#ifndef NO_AUTH +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) +{ + struct authinfo aauth; + char *response; + + if (hcode == 401) + aauth = *wauth; + else + aauth = *pauth; + + if (verbose || aauth.auth == NULL || + aauth.user == NULL || aauth.pass == NULL) + fprintf(ttyout, "%s\n", message); + if (EMPTYSTRING(*auth)) { + warnx("No authentication challenge provided by server"); + return; + } + + if (aauth.auth != NULL) { + char reply[10]; + + fprintf(ttyout, "Authorization failed. Retry (y/n)? "); + if (get_line(stdin, reply, sizeof(reply), NULL) < 0) { + return; + } + if (tolower((unsigned char)reply[0]) != 'y') + return; + + aauth.user = NULL; + aauth.pass = NULL; + } + + if (auth_url(*auth, &response, &aauth) == 0) { + *rval = fetch_url(url, penv, + hcode == 401 ? pauth->auth : response, + hcode == 401 ? response: wauth->auth); + memset(response, 0, strlen(response)); + FREEPTR(response); + } +} +#endif + +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) +{ + int len, hcode, rv; + char buf[FTPBUFLEN], *ep; + const char *cp, *token; + char *location, *message; + + *auth = message = location = NULL; + + /* Read the response */ + ep = buf; + switch (getresponse(fin, &ep, sizeof(buf), &hcode)) { + case C_CLEANUP: + goto cleanup_fetch_url; + case C_IMPROPER: + goto improper; + case C_OK: + message = ftp_strdup(ep); + break; + } + + /* Read the rest of the header. */ + + for (;;) { + if ((rv = getresponseline(fin, buf, sizeof(buf), &len)) != C_OK) + goto cleanup_fetch_url; + if (len == 0) + break; + + /* + * Look for some headers + */ + + cp = buf; + + if (match_token(&cp, "Content-Length:")) { + filesize = STRTOLL(cp, &ep, 10); + if (filesize < 0 || *ep != '\0') + goto improper; + DPRINTF("%s: parsed len as: " LLF "\n", + __func__, (LLT)filesize); + + } else if (match_token(&cp, "Content-Range:")) { + if (parse_posinfo(&cp, pi) == -1) + goto improper; + if (! restart_point) { + warnx( + "Received unexpected Content-Range header"); + goto cleanup_fetch_url; + } + + } else if (match_token(&cp, "Last-Modified:")) { + getmtime(cp, mtime); + + } else if (match_token(&cp, "Location:")) { + location = ftp_strdup(cp); + DPRINTF("%s: parsed location as `%s'\n", + __func__, cp); + + } else if (match_token(&cp, "Transfer-Encoding:")) { + if (match_token(&cp, "binary")) { + warnx( + "Bogus transfer encoding `binary' (fetching anyway)"); + continue; + } + if (! (token = match_token(&cp, "chunked"))) { + warnx( + "Unsupported transfer encoding `%s'", + token); + goto cleanup_fetch_url; + } + (*ischunked)++; + DPRINTF("%s: using chunked encoding\n", + __func__); + + } else if (match_token(&cp, "Proxy-Authenticate:") + || match_token(&cp, "WWW-Authenticate:")) { + if (! (token = match_token(&cp, "Basic"))) { + DPRINTF("%s: skipping unknown auth " + "scheme `%s'\n", __func__, token); + continue; + } + FREEPTR(*auth); + *auth = ftp_strdup(token); + DPRINTF("%s: parsed auth as `%s'\n", + __func__, cp); + } + + } + /* finished parsing header */ + + switch (hcode) { + case 200: + break; + case 206: + if (! restart_point) { + warnx("Not expecting partial content header"); + goto cleanup_fetch_url; + } + break; + case 300: + case 301: + case 302: + case 303: + case 305: + case 307: + if (EMPTYSTRING(location)) { + warnx( + "No redirection Location provided by server"); + goto cleanup_fetch_url; + } + if (redirect_loop++ > 5) { + warnx("Too many redirections requested"); + goto cleanup_fetch_url; + } + if (hcode == 305) { + if (verbose) + fprintf(ttyout, "Redirected via %s\n", + location); + *rval = fetch_url(url, location, + pauth->auth, wauth->auth); + } else { + if (verbose) + fprintf(ttyout, "Redirected to %s\n", + location); + *rval = go_fetch(location); + } + goto cleanup_fetch_url; +#ifndef NO_AUTH + case 401: + case 407: + do_auth(hcode, url, penv, wauth, pauth, auth, message, rval); + goto cleanup_fetch_url; +#endif + default: + if (message) + warnx("Error retrieving file `%s'", message); + else + warnx("Unknown error retrieving file"); + goto cleanup_fetch_url; + } + rv = C_OK; + goto out; + +cleanup_fetch_url: + rv = C_CLEANUP; + goto out; +improper: + rv = C_IMPROPER; + goto out; +out: + FREEPTR(message); + FREEPTR(location); + return rv; +} /* end of ftp:// or http:// specific setup */ + +#ifdef WITH_SSL +static int +connectmethod(FETCH *fin, const char *url, const char *penv, + struct urlinfo *oui, struct urlinfo *ui, struct authinfo *wauth, + struct authinfo *pauth, char **auth, int *hasleading, volatile int *rval) +{ + void *ssl; + int hcode, rv; + const char *cp; + char buf[FTPBUFLEN], *ep; + char *message = NULL; + + print_connect(fin, oui); + + print_agent(fin); + *hasleading = print_proxy(fin, *hasleading, NULL, pauth->auth); + + if (verbose && *hasleading) + fputs(")\n", ttyout); + *hasleading = 0; + + fetch_printf(fin, "\r\n"); + if (fetch_flush(fin) == EOF) { + warn("Writing HTTP request"); + alarmtimer(0); + goto cleanup_fetch_url; + } + alarmtimer(0); + + /* Read the response */ + ep = buf; + switch (getresponse(fin, &ep, sizeof(buf), &hcode)) { + case C_CLEANUP: + goto cleanup_fetch_url; + case C_IMPROPER: + goto improper; + case C_OK: + message = ftp_strdup(ep); + break; + } + + for (;;) { + int len; + if (getresponseline(fin, buf, sizeof(buf), &len) != C_OK) + goto cleanup_fetch_url; + if (len == 0) + break; + + cp = buf; + if (match_token(&cp, "Proxy-Authenticate:")) { + const char *token; + if (!(token = match_token(&cp, "Basic"))) { + DPRINTF( + "%s: skipping unknown auth scheme `%s'\n", + __func__, token); + continue; + } + FREEPTR(*auth); + *auth = ftp_strdup(token); + DPRINTF("%s: parsed auth as " "`%s'\n", __func__, cp); + } + } + + /* finished parsing header */ + switch (hcode) { + case 200: + break; +#ifndef NO_AUTH + case 407: + do_auth(hcode, url, penv, wauth, pauth, auth, message, rval); + goto cleanup_fetch_url; +#endif + default: + if (message) + warnx("Error proxy connect " "`%s'", message); + else + warnx("Unknown error proxy " "connect"); + goto cleanup_fetch_url; + } + + if ((ssl = fetch_start_ssl(fetch_fileno(fin), oui->host)) == NULL) + goto cleanup_fetch_url; + fetch_set_ssl(fin, ssl); + + rv = C_OK; + goto out; +improper: + rv = C_IMPROPER; + goto out; +cleanup_fetch_url: + rv = C_CLEANUP; + goto out; +out: + FREEPTR(message); + return rv; +} +#endif + /* * Retrieve URL, via a proxy if necessary, using HTTP. * If proxyenv is set, use that for the proxy, otherwise try ftp_proxy or @@ -501,44 +1271,34 @@ sigjmp_buf httpabort; static int fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) { - struct addrinfo hints, *res, *res0 = NULL; - int error; sigfunc volatile oldint; sigfunc volatile oldpipe; sigfunc volatile oldalrm; sigfunc volatile oldquit; int volatile s; struct stat sb; - int volatile ischunked; int volatile isproxy; - int volatile rval; - int volatile hcode; - int len; + int volatile rval, ischunked; size_t flen; static size_t bufsize; static char *xferbuf; - const char *cp, *token; + const char *cp; char *ep; - char buf[FTPBUFLEN]; - const char *errormsg; - char *volatile savefile; char *volatile auth; + char *volatile savefile; char *volatile location; char *volatile message; - char *uuser, *pass, *host, *port, *path; char *volatile decodedpath; - char *puser, *ppass, *useragent; - off_t hashbytes, rangestart, rangeend, entitylen; + struct authinfo wauth, pauth; + struct posinfo pi; + off_t hashbytes; int (*volatile closefunc)(FILE *); FETCH *volatile fin; FILE *volatile fout; const char *volatile penv = proxyenv; + struct urlinfo ui, oui; time_t mtime; - url_t urltype; - in_port_t portnum; -#ifdef WITH_SSL - void *ssl; -#endif + void *ssl = NULL; DPRINTF("%s: `%s' proxyenv `%s'\n", __func__, url, STRorNULL(penv)); @@ -549,35 +1309,42 @@ fetch_url(const char *url, const char *p s = -1; savefile = NULL; auth = location = message = NULL; - ischunked = isproxy = hcode = 0; + ischunked = isproxy = 0; rval = 1; - uuser = pass = host = path = decodedpath = puser = ppass = NULL; + + initurlinfo(&ui); + initurlinfo(&oui); + initauthinfo(&wauth, wwwauth); + initauthinfo(&pauth, proxyauth); + + decodedpath = NULL; if (sigsetjmp(httpabort, 1)) goto cleanup_fetch_url; - if (parse_url(url, "URL", &urltype, &uuser, &pass, &host, &port, - &portnum, &path) == -1) + if (parse_url(url, "URL", &ui, &wauth) == -1) goto cleanup_fetch_url; - if (urltype == FILE_URL_T && ! EMPTYSTRING(host) - && strcasecmp(host, "localhost") != 0) { + copyurlinfo(&oui, &ui); + + if (ui.utype == FILE_URL_T && ! EMPTYSTRING(ui.host) + && strcasecmp(ui.host, "localhost") != 0) { warnx("No support for non local file URL `%s'", url); goto cleanup_fetch_url; } - if (EMPTYSTRING(path)) { - if (urltype == FTP_URL_T) { + if (EMPTYSTRING(ui.path)) { + if (ui.utype == FTP_URL_T) { rval = fetch_ftp(url); goto cleanup_fetch_url; } - if (!IS_HTTP_TYPE(urltype) || outfile == NULL) { + if (!IS_HTTP_TYPE(ui.utype) || outfile == NULL) { warnx("Invalid URL (no file after host) `%s'", url); goto cleanup_fetch_url; } } - decodedpath = ftp_strdup(path); + decodedpath = ftp_strdup(ui.path); url_decode(decodedpath); if (outfile) @@ -588,10 +1355,16 @@ fetch_url(const char *url, const char *p savefile = ftp_strdup(cp + 1); else savefile = ftp_strdup(decodedpath); + /* + * Use the first URL we requested not the name after a + * possible redirect, but careful to save it because our + * "safety" check is the match to outfile. + */ + outfile = ftp_strdup(savefile); } DPRINTF("%s: savefile `%s'\n", __func__, savefile); if (EMPTYSTRING(savefile)) { - if (urltype == FTP_URL_T) { + if (ui.utype == FTP_URL_T) { rval = fetch_ftp(url); goto cleanup_fetch_url; } @@ -602,13 +1375,13 @@ fetch_url(const char *url, const char *p restart_point = 0; filesize = -1; - rangestart = rangeend = entitylen = -1; + initposinfo(&pi); mtime = -1; if (restartautofetch) { if (stat(savefile, &sb) == 0) restart_point = sb.st_size; } - if (urltype == FILE_URL_T) { /* file:// URLs */ + if (ui.utype == FILE_URL_T) { /* file:// URLs */ direction = "copied"; fin = fetch_open(decodedpath, "r"); if (fin == NULL) { @@ -637,184 +1410,43 @@ fetch_url(const char *url, const char *p rcvbuf_size = 8 * 1024; /* XXX */ } } else { /* ftp:// or http:// URLs */ - const char *leading; int hasleading; if (penv == NULL) { #ifdef WITH_SSL - if (urltype == HTTPS_URL_T) + if (ui.utype == HTTPS_URL_T) penv = getoptionvalue("https_proxy"); #endif - if (penv == NULL && IS_HTTP_TYPE(urltype)) + if (penv == NULL && IS_HTTP_TYPE(ui.utype)) penv = getoptionvalue("http_proxy"); - else if (urltype == FTP_URL_T) + else if (ui.utype == FTP_URL_T) penv = getoptionvalue("ftp_proxy"); } direction = "retrieved"; if (! EMPTYSTRING(penv)) { /* use proxy */ - url_t purltype; - char *phost, *ppath; - char *pport, *no_proxy; - in_port_t pportnum; - - isproxy = 1; - - /* check URL against list of no_proxied sites */ - no_proxy = getoptionvalue("no_proxy"); - if (! EMPTYSTRING(no_proxy)) { - char *np, *np_copy, *np_iter; - unsigned long np_port; - size_t hlen, plen; - - np_iter = np_copy = ftp_strdup(no_proxy); - hlen = strlen(host); - while ((cp = strsep(&np_iter, " ,")) != NULL) { - if (*cp == '\0') - continue; - if ((np = strrchr(cp, ':')) != NULL) { - *np++ = '\0'; - np_port = strtoul(np, &ep, 10); - if (*np == '\0' || *ep != '\0') - continue; - if (np_port != portnum) - continue; - } - plen = strlen(cp); - if (hlen < plen) - continue; - if (strncasecmp(host + hlen - plen, - cp, plen) == 0) { - isproxy = 0; - break; - } - } - FREEPTR(np_copy); - if (isproxy == 0 && urltype == FTP_URL_T) { - rval = fetch_ftp(url); - goto cleanup_fetch_url; - } + + isproxy = handle_noproxy(ui.host, ui.portnum); + + if (isproxy == 0 && ui.utype == FTP_URL_T) { + rval = fetch_ftp(url); + goto cleanup_fetch_url; } if (isproxy) { if (restart_point) { - warnx("Can't restart via proxy URL `%s'", + warnx( + "Can't restart via proxy URL `%s'", penv); goto cleanup_fetch_url; } - if (parse_url(penv, "proxy URL", &purltype, - &puser, &ppass, &phost, &pport, &pportnum, - &ppath) == -1) - goto cleanup_fetch_url; - - if ((!IS_HTTP_TYPE(purltype) - && purltype != FTP_URL_T) || - EMPTYSTRING(phost) || - (! EMPTYSTRING(ppath) - && strcmp(ppath, "/") != 0)) { - warnx("Malformed proxy URL `%s'", penv); - FREEPTR(phost); - FREEPTR(pport); - FREEPTR(ppath); - goto cleanup_fetch_url; - } - if (isipv6addr(host) && - strchr(host, '%') != NULL) { - warnx( -"Scoped address notation `%s' disallowed via web proxy", - host); - FREEPTR(phost); - FREEPTR(pport); - FREEPTR(ppath); + if (handle_proxy(url, penv, &ui, &pauth) < 0) goto cleanup_fetch_url; - } - - FREEPTR(host); - host = phost; - FREEPTR(port); - port = pport; - FREEPTR(path); - path = ftp_strdup(url); - FREEPTR(ppath); - urltype = purltype; } } /* ! EMPTYSTRING(penv) */ - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = 0; - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - error = getaddrinfo(host, port, &hints, &res0); - if (error) { - warnx("Can't LOOKUP `%s:%s': %s", host, port, - (error == EAI_SYSTEM) ? strerror(errno) - : gai_strerror(error)); - goto cleanup_fetch_url; - } - if (res0->ai_canonname) - host = res0->ai_canonname; - - s = -1; -#ifdef WITH_SSL - ssl = NULL; -#endif - for (res = res0; res; res = res->ai_next) { - char hname[NI_MAXHOST], sname[NI_MAXSERV]; - - ai_unmapped(res); - if (getnameinfo(res->ai_addr, res->ai_addrlen, - hname, sizeof(hname), sname, sizeof(sname), - NI_NUMERICHOST | NI_NUMERICSERV) != 0) { - strlcpy(hname, "?", sizeof(hname)); - strlcpy(sname, "?", sizeof(sname)); - } - - if (verbose && res0->ai_next) { -#ifdef INET6 - if(res->ai_family == AF_INET6) { - fprintf(ttyout, "Trying [%s]:%s ...\n", - hname, sname); - } else { -#endif - fprintf(ttyout, "Trying %s:%s ...\n", - hname, sname); -#ifdef INET6 - } -#endif - } - - s = socket(res->ai_family, SOCK_STREAM, - res->ai_protocol); - if (s < 0) { - warn( - "Can't create socket for connection to " - "`%s:%s'", hname, sname); - continue; - } - - if (ftp_connect(s, res->ai_addr, res->ai_addrlen, - verbose || !res->ai_next) < 0) { - close(s); - s = -1; - continue; - } - -#ifdef WITH_SSL - if (urltype == HTTPS_URL_T) { - if ((ssl = fetch_start_ssl(s, host)) == NULL) { - close(s); - s = -1; - continue; - } - } -#endif - - /* success */ - break; - } - + s = ftp_socket(&ui, &ssl); if (s < 0) { - warnx("Can't connect to `%s:%s'", host, port); + warnx("Can't connect to `%s:%s'", ui.host, ui.port); goto cleanup_fetch_url; } @@ -830,84 +1462,39 @@ fetch_url(const char *url, const char *p */ if (verbose) fprintf(ttyout, "Requesting %s\n", url); - leading = " ("; - hasleading = 0; - if (isproxy) { - if (verbose) { - fprintf(ttyout, "%svia %s:%s", leading, - host, port); - leading = ", "; - hasleading++; - } - fetch_printf(fin, "GET %s HTTP/1.0\r\n", path); - if (flushcache) - fetch_printf(fin, "Pragma: no-cache\r\n"); - } else { - fetch_printf(fin, "GET %s HTTP/1.1\r\n", path); - if (strchr(host, ':')) { - char *h, *p; - /* - * strip off IPv6 scope identifier, since it is - * local to the node - */ - h = ftp_strdup(host); - if (isipv6addr(h) && - (p = strchr(h, '%')) != NULL) { - *p = '\0'; - } - fetch_printf(fin, "Host: [%s]", h); - free(h); - } else - fetch_printf(fin, "Host: %s", host); + hasleading = 0; #ifdef WITH_SSL - if ((urltype == HTTP_URL_T && portnum != HTTP_PORT) || - (urltype == HTTPS_URL_T && portnum != HTTPS_PORT)) -#else - if (portnum != HTTP_PORT) -#endif - fetch_printf(fin, ":%u", portnum); - fetch_printf(fin, "\r\n"); - fetch_printf(fin, "Accept: */*\r\n"); - fetch_printf(fin, "Connection: close\r\n"); - if (restart_point) { - fputs(leading, ttyout); - fetch_printf(fin, "Range: bytes=" LLF "-\r\n", - (LLT)restart_point); - fprintf(ttyout, "restarting at " LLF, - (LLT)restart_point); - leading = ", "; - hasleading++; + if (isproxy && oui.utype == HTTPS_URL_T) { + switch (connectmethod(fin, url, penv, &oui, &ui, + &wauth, &pauth, __UNVOLATILE(&auth), &hasleading, + &rval)) { + case C_CLEANUP: + goto cleanup_fetch_url; + case C_IMPROPER: + goto improper; + case C_OK: + break; + default: + abort(); } - if (flushcache) - fetch_printf(fin, "Cache-Control: no-cache\r\n"); - } - if ((useragent=getenv("FTPUSERAGENT")) != NULL) { - fetch_printf(fin, "User-Agent: %s\r\n", useragent); - } else { - fetch_printf(fin, "User-Agent: %s/%s\r\n", - FTP_PRODUCT, FTP_VERSION); } - if (wwwauth) { - if (verbose) { - fprintf(ttyout, "%swith authorization", - leading); - leading = ", "; - hasleading++; - } - fetch_printf(fin, "Authorization: %s\r\n", wwwauth); - } - if (proxyauth) { - if (verbose) { - fprintf(ttyout, - "%swith proxy authorization", leading); - leading = ", "; - hasleading++; - } - fetch_printf(fin, "Proxy-Authorization: %s\r\n", proxyauth); +#endif + + hasleading = print_get(fin, hasleading, isproxy, &oui, &ui); + + if (flushcache) + print_cache(fin, isproxy); + + print_agent(fin); + hasleading = print_proxy(fin, hasleading, wauth.auth, + auth ? NULL : pauth.auth); + if (hasleading) { + hasleading = 0; + if (verbose) + fputs(")\n", ttyout); } - if (verbose && hasleading) - fputs(")\n", ttyout); + fetch_printf(fin, "\r\n"); if (fetch_flush(fin) == EOF) { warn("Writing HTTP request"); @@ -916,257 +1503,19 @@ fetch_url(const char *url, const char *p } alarmtimer(0); - /* Read the response */ - alarmtimer(quit_time ? quit_time : 60); - len = fetch_getline(fin, buf, sizeof(buf), &errormsg); - alarmtimer(0); - if (len < 0) { - if (*errormsg == '\n') - errormsg++; - warnx("Receiving HTTP reply: %s", errormsg); - goto cleanup_fetch_url; - } - while (len > 0 && (ISLWS(buf[len-1]))) - buf[--len] = '\0'; - DPRINTF("%s: received `%s'\n", __func__, buf); - - /* Determine HTTP response code */ - cp = strchr(buf, ' '); - if (cp == NULL) - goto improper; - else - cp++; - hcode = strtol(cp, &ep, 10); - if (*ep != '\0' && !isspace((unsigned char)*ep)) - goto improper; - message = ftp_strdup(cp); - - /* Read the rest of the header. */ - while (1) { - alarmtimer(quit_time ? quit_time : 60); - len = fetch_getline(fin, buf, sizeof(buf), &errormsg); - alarmtimer(0); - if (len < 0) { - if (*errormsg == '\n') - errormsg++; - warnx("Receiving HTTP reply: %s", errormsg); - goto cleanup_fetch_url; - } - while (len > 0 && (ISLWS(buf[len-1]))) - buf[--len] = '\0'; - if (len == 0) - break; - DPRINTF("%s: received `%s'\n", __func__, buf); - - /* - * Look for some headers - */ - - cp = buf; - - if (match_token(&cp, "Content-Length:")) { - filesize = STRTOLL(cp, &ep, 10); - if (filesize < 0 || *ep != '\0') - goto improper; - DPRINTF("%s: parsed len as: " LLF "\n", - __func__, (LLT)filesize); - - } else if (match_token(&cp, "Content-Range:")) { - if (! match_token(&cp, "bytes")) - goto improper; - - if (*cp == '*') - cp++; - else { - rangestart = STRTOLL(cp, &ep, 10); - if (rangestart < 0 || *ep != '-') - goto improper; - cp = ep + 1; - rangeend = STRTOLL(cp, &ep, 10); - if (rangeend < 0 || rangeend < rangestart) - goto improper; - cp = ep; - } - if (*cp != '/') - goto improper; - cp++; - if (*cp == '*') - cp++; - else { - entitylen = STRTOLL(cp, &ep, 10); - if (entitylen < 0) - goto improper; - cp = ep; - } - if (*cp != '\0') - goto improper; - -#ifndef NO_DEBUG - if (ftp_debug) { - fprintf(ttyout, "parsed range as: "); - if (rangestart == -1) - fprintf(ttyout, "*"); - else - fprintf(ttyout, LLF "-" LLF, - (LLT)rangestart, - (LLT)rangeend); - fprintf(ttyout, "/" LLF "\n", (LLT)entitylen); - } -#endif - if (! restart_point) { - warnx( - "Received unexpected Content-Range header"); - goto cleanup_fetch_url; - } - - } else if (match_token(&cp, "Last-Modified:")) { - struct tm parsed; - const char *t; - - memset(&parsed, 0, sizeof(parsed)); - t = parse_rfc2616time(&parsed, cp); - if (t != NULL) { - parsed.tm_isdst = -1; - if (*t == '\0') - mtime = timegm(&parsed); -#ifndef NO_DEBUG - if (ftp_debug && mtime != -1) { - fprintf(ttyout, - "parsed time as: %s", - rfc2822time(localtime(&mtime))); - } -#endif - } - - } else if (match_token(&cp, "Location:")) { - location = ftp_strdup(cp); - DPRINTF("%s: parsed location as `%s'\n", - __func__, cp); - - } else if (match_token(&cp, "Transfer-Encoding:")) { - if (match_token(&cp, "binary")) { - warnx( - "Bogus transfer encoding `binary' (fetching anyway)"); - continue; - } - if (! (token = match_token(&cp, "chunked"))) { - warnx( - "Unsupported transfer encoding `%s'", - token); - goto cleanup_fetch_url; - } - ischunked++; - DPRINTF("%s: using chunked encoding\n", - __func__); - - } else if (match_token(&cp, "Proxy-Authenticate:") - || match_token(&cp, "WWW-Authenticate:")) { - if (! (token = match_token(&cp, "Basic"))) { - DPRINTF("%s: skipping unknown auth " - "scheme `%s'\n", __func__, token); - continue; - } - FREEPTR(auth); - auth = ftp_strdup(token); - DPRINTF("%s: parsed auth as `%s'\n", - __func__, cp); - } - - } - /* finished parsing header */ - - switch (hcode) { - case 200: + switch (negotiate_connection(fin, url, penv, &pi, + &mtime, &wauth, &pauth, &rval, &ischunked, + __UNVOLATILE(&auth))) { + case C_OK: break; - case 206: - if (! restart_point) { - warnx("Not expecting partial content header"); - goto cleanup_fetch_url; - } - break; - case 300: - case 301: - case 302: - case 303: - case 305: - case 307: - if (EMPTYSTRING(location)) { - warnx( - "No redirection Location provided by server"); - goto cleanup_fetch_url; - } - if (redirect_loop++ > 5) { - warnx("Too many redirections requested"); - goto cleanup_fetch_url; - } - if (hcode == 305) { - if (verbose) - fprintf(ttyout, "Redirected via %s\n", - location); - rval = fetch_url(url, location, - proxyauth, wwwauth); - } else { - if (verbose) - fprintf(ttyout, "Redirected to %s\n", - location); - rval = go_fetch(location); - } - goto cleanup_fetch_url; -#ifndef NO_AUTH - case 401: - case 407: - { - char **authp; - char *auser, *apass; - - if (hcode == 401) { - authp = &wwwauth; - auser = uuser; - apass = pass; - } else { - authp = &proxyauth; - auser = puser; - apass = ppass; - } - if (verbose || *authp == NULL || - auser == NULL || apass == NULL) - fprintf(ttyout, "%s\n", message); - if (EMPTYSTRING(auth)) { - warnx( - "No authentication challenge provided by server"); - goto cleanup_fetch_url; - } - if (*authp != NULL) { - char reply[10]; - - fprintf(ttyout, - "Authorization failed. Retry (y/n)? "); - if (get_line(stdin, reply, sizeof(reply), NULL) - < 0) { - goto cleanup_fetch_url; - } - if (tolower((unsigned char)reply[0]) != 'y') - goto cleanup_fetch_url; - auser = NULL; - apass = NULL; - } - if (auth_url(auth, authp, auser, apass) == 0) { - rval = fetch_url(url, penv, - proxyauth, wwwauth); - memset(*authp, 0, strlen(*authp)); - FREEPTR(*authp); - } + case C_CLEANUP: goto cleanup_fetch_url; - } -#endif + case C_IMPROPER: + goto improper; default: - if (message) - warnx("Error retrieving file `%s'", message); - else - warnx("Unknown error retrieving file"); - goto cleanup_fetch_url; + abort(); } - } /* end of ftp:// or http:// specific setup */ + } /* Open the output file. */ @@ -1188,18 +1537,19 @@ fetch_url(const char *url, const char *p } } if (fout == NULL) { - if ((rangeend != -1 && rangeend <= restart_point) || - (rangestart == -1 && filesize != -1 && filesize <= restart_point)) { + if ((pi.rangeend != -1 && pi.rangeend <= restart_point) || + (pi.rangestart == -1 && + filesize != -1 && filesize <= restart_point)) { /* already done */ if (verbose) fprintf(ttyout, "already done\n"); rval = 0; goto cleanup_fetch_url; } - if (restart_point && rangestart != -1) { - if (entitylen != -1) - filesize = entitylen; - if (rangestart != restart_point) { + if (restart_point && pi.rangestart != -1) { + if (pi.entitylen != -1) + filesize = pi.entitylen; + if (pi.rangestart != restart_point) { warnx( "Size of `%s' differs from save file `%s'", url, savefile); @@ -1297,10 +1647,14 @@ 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) @@ -1351,7 +1705,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); @@ -1386,7 +1740,7 @@ fetch_url(const char *url, const char *p goto cleanup_fetch_url; improper: - warnx("Improper response from `%s:%s'", host, port); + warnx("Improper response from `%s:%s'", ui.host, ui.port); cleanup_fetch_url: if (oldint) @@ -1403,22 +1757,13 @@ fetch_url(const char *url, const char *p close(s); if (closefunc != NULL && fout != NULL) (*closefunc)(fout); - if (res0) - freeaddrinfo(res0); if (savefile != outfile) FREEPTR(savefile); - FREEPTR(uuser); - if (pass != NULL) - memset(pass, 0, strlen(pass)); - FREEPTR(pass); - FREEPTR(host); - FREEPTR(port); - FREEPTR(path); + freeurlinfo(&ui); + freeurlinfo(&oui); + freeauthinfo(&wauth); + freeauthinfo(&pauth); FREEPTR(decodedpath); - FREEPTR(puser); - if (ppass != NULL) - memset(ppass, 0, strlen(ppass)); - FREEPTR(ppass); FREEPTR(auth); FREEPTR(location); FREEPTR(message); @@ -1470,26 +1815,26 @@ static int fetch_ftp(const char *url) { char *cp, *xargv[5], rempath[MAXPATHLEN]; - char *host, *path, *dir, *file, *uuser, *pass; - char *port; + char *dir, *file; char cmdbuf[MAXPATHLEN]; char dirbuf[4]; int dirhasglob, filehasglob, rval, transtype, xargc; int oanonftp, oautologin; - in_port_t portnum; - url_t urltype; + struct authinfo auth; + struct urlinfo ui; DPRINTF("fetch_ftp: `%s'\n", url); - host = path = dir = file = uuser = pass = NULL; - port = NULL; + dir = file = NULL; rval = 1; transtype = TYPE_I; + initurlinfo(&ui); + initauthinfo(&auth, NULL); + if (STRNEQUAL(url, FTP_URL)) { - if ((parse_url(url, "URL", &urltype, &uuser, &pass, - &host, &port, &portnum, &path) == -1) || - (uuser != NULL && *uuser == '\0') || - EMPTYSTRING(host)) { + if ((parse_url(url, "URL", &ui, &auth) == -1) || + (auth.user != NULL && *auth.user == '\0') || + EMPTYSTRING(ui.host)) { warnx("Invalid URL `%s'", url); goto cleanup_fetch_ftp; } @@ -1499,7 +1844,7 @@ fetch_ftp(const char *url) */ /* check for trailing ';type=[aid]' */ - if (! EMPTYSTRING(path) && (cp = strrchr(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) @@ -1516,26 +1861,26 @@ fetch_ftp(const char *url) *cp = 0; } } else { /* classic style `[user@]host:[file]' */ - urltype = CLASSIC_URL_T; - host = ftp_strdup(url); - cp = strchr(host, '@'); + ui.utype = CLASSIC_URL_T; + ui.host = ftp_strdup(url); + cp = strchr(ui.host, '@'); if (cp != NULL) { *cp = '\0'; - uuser = host; + auth.user = ui.host; anonftp = 0; /* disable anonftp */ - host = ftp_strdup(cp + 1); + ui.host = ftp_strdup(cp + 1); } - cp = strchr(host, ':'); + cp = strchr(ui.host, ':'); if (cp != NULL) { *cp = '\0'; - path = ftp_strdup(cp + 1); + ui.path = ftp_strdup(cp + 1); } } - if (EMPTYSTRING(host)) + if (EMPTYSTRING(ui.host)) goto cleanup_fetch_ftp; /* Extract the file and (if present) directory name. */ - dir = path; + dir = ui.path; if (! EMPTYSTRING(dir)) { /* * If we are dealing with classic `[user@]host:[path]' syntax, @@ -1560,7 +1905,7 @@ fetch_ftp(const char *url) * resulting from an URL of the form `ftp://host//file'.) */ cp = strrchr(dir, '/'); - if (cp == dir && urltype == CLASSIC_URL_T) { + if (cp == dir && ui.utype == CLASSIC_URL_T) { file = cp + 1; (void)strlcpy(dirbuf, "/", sizeof(dirbuf)); dir = dirbuf; @@ -1573,18 +1918,19 @@ fetch_ftp(const char *url) } } else dir = NULL; - if (urltype == FTP_URL_T && file != NULL) { + if (ui.utype == FTP_URL_T && file != NULL) { 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", - STRorNULL(uuser), STRorNULL(pass), - STRorNULL(host), STRorNULL(port), - STRorNULL(path), STRorNULL(dir), STRorNULL(file)); + STRorNULL(auth.user), STRorNULL(auth.pass), + STRorNULL(ui.host), STRorNULL(ui.port), + STRorNULL(ui.path), STRorNULL(dir), STRorNULL(file)); dirhasglob = filehasglob = 0; - if (doglob && urltype == CLASSIC_URL_T) { + if (doglob && + (ui.utype == CLASSIC_URL_T || ui.utype == FTP_URL_T)) { if (! EMPTYSTRING(dir) && strpbrk(dir, "*?[]{}") != NULL) dirhasglob = 1; if (! EMPTYSTRING(file) && strpbrk(file, "*?[]{}") != NULL) @@ -1598,11 +1944,11 @@ fetch_ftp(const char *url) anonftp = oanonftp; (void)strlcpy(cmdbuf, getprogname(), sizeof(cmdbuf)); xargv[0] = cmdbuf; - xargv[1] = host; + xargv[1] = ui.host; xargv[2] = NULL; xargc = 2; - if (port) { - xargv[2] = port; + if (ui.port) { + xargv[2] = ui.port; xargv[3] = NULL; xargc = 3; } @@ -1612,9 +1958,9 @@ fetch_ftp(const char *url) setpeer(xargc, xargv); autologin = oautologin; if ((connected == 0) || - (connected == 1 && !ftp_login(host, uuser, pass))) { + (connected == 1 && !ftp_login(ui.host, auth.user, auth.pass))) { warnx("Can't connect or login to host `%s:%s'", - host, port ? port : "?"); + ui.host, ui.port ? ui.port : "?"); goto cleanup_fetch_ftp; } @@ -1699,7 +2045,7 @@ fetch_ftp(const char *url) * Note that we don't need `dir' after this point. */ do { - if (urltype == FTP_URL_T) { + if (ui.utype == FTP_URL_T) { nextpart = strchr(dir, '/'); if (nextpart) { *nextpart = '\0'; @@ -1710,7 +2056,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 (ui.utype == FTP_URL_T || *dir != '\0') { (void)strlcpy(cmdbuf, "cd", sizeof(cmdbuf)); xargv[0] = cmdbuf; xargv[1] = dir; @@ -1764,14 +2110,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) @@ -1784,13 +2131,8 @@ fetch_ftp(const char *url) rval = 0; cleanup_fetch_ftp: - FREEPTR(port); - FREEPTR(host); - FREEPTR(path); - FREEPTR(uuser); - if (pass) - memset(pass, 0, strlen(pass)); - FREEPTR(pass); + freeurlinfo(&ui); + freeauthinfo(&auth); return (rval); } @@ -1922,8 +2264,9 @@ auto_fetch(int argc, char *argv[]) anonftp = 2; /* Handle "automatic" transfers. */ rval = go_fetch(argv[argpos]); if (outfile != NULL && strcmp(outfile, "-") != 0 - && outfile[0] != '|') - outfile = NULL; + && outfile[0] != '|') { + FREEPTR(outfile); + } if (rval > 0) rval = argpos + 1; } Index: othersrc/usr.bin/tnftp/src/util.c diff -u othersrc/usr.bin/tnftp/src/util.c:1.23 othersrc/usr.bin/tnftp/src/util.c:1.24 --- othersrc/usr.bin/tnftp/src/util.c:1.23 Sun May 5 11:51:43 2013 +++ othersrc/usr.bin/tnftp/src/util.c Sat Jul 4 09:59:07 2020 @@ -1,8 +1,8 @@ -/* $NetBSD: util.c,v 1.23 2013/05/05 11:51:43 lukem Exp $ */ -/* from NetBSD: util.c,v 1.158 2013/02/19 23:29:15 dsl Exp */ +/* $NetBSD: util.c,v 1.24 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: util.c,v 1.161 2020/06/08 01:33:27 lukem 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 @@ -69,7 +69,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID(" NetBSD: util.c,v 1.158 2013/02/19 23:29:15 dsl Exp "); +__RCSID(" NetBSD: util.c,v 1.161 2020/06/08 01:33:27 lukem Exp "); #endif /* not lint */ /* @@ -331,9 +331,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; @@ -363,6 +364,9 @@ lostpeer(int dummy) proxflag = 0; pswitch(0); cleanuppeer(); + if (signo) { + errx(1, "lostpeer due to signal %d", signo); + } errno = oerrno; } @@ -485,7 +489,8 @@ ftp_login(const char *host, const char * } } updatelocalcwd(); - updateremotecwd(); + remotecwd[0] = '\0'; + remcwdvalid = 0; cleanup_ftp_login: FREEPTR(fuser); @@ -846,6 +851,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) @@ -1185,6 +1191,8 @@ formatbuf(char *buf, size_t len, const c case '/': case '.': case 'c': + if (connected && !remcwdvalid) + updateremotecwd(); p2 = connected ? remotecwd : ""; updirs = pdirs = 0; @@ -1556,6 +1564,7 @@ ftp_poll(struct pollfd *fds, int nfds, i #endif } +#ifndef SMALL /* * malloc() with inbuilt error checking */ @@ -1610,3 +1619,4 @@ ftp_strdup(const char *str) err(1, "Unable to allocate memory for string copy"); return (s); } +#endif Index: othersrc/usr.bin/tnftp/src/ftp.1 diff -u othersrc/usr.bin/tnftp/src/ftp.1:1.15 othersrc/usr.bin/tnftp/src/ftp.1:1.16 --- othersrc/usr.bin/tnftp/src/ftp.1:1.15 Sun Oct 4 04:53:26 2015 +++ othersrc/usr.bin/tnftp/src/ftp.1 Sat Jul 4 09:59:07 2020 @@ -1,5 +1,5 @@ -.\" $NetBSD: ftp.1,v 1.15 2015/10/04 04:53:26 lukem Exp $ -.\" from NetBSD: ftp.1,v 1.135 2015/04/23 23:31:23 lukem Exp +.\" $NetBSD: ftp.1,v 1.16 2020/07/04 09:59:07 lukem Exp $ +.\" from NetBSD: ftp.1,v 1.136 2017/07/03 21:34:57 wiz Exp .\" .\" Copyright (c) 1996-2015 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -335,7 +335,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 @@ -529,7 +529,7 @@ When debugging is on, .Nm prints each command sent to the remote machine, preceded by the string -.Ql \-\-\*[Gt] . +.Ql \-\-> . .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. @@ -898,7 +898,7 @@ the output filename "myfile.data" for in Spaces may be included in .Ar outpattern , as in the example: -.Dl nmap $1 sed "s/ *$//" \*[Gt] $1 +.Dl nmap $1 sed "s/ *$//" > $1 Use the .Sq \e character to prevent special treatment @@ -2151,7 +2151,7 @@ By default, this is bound to the TAB key By default, .Nm displays a command line prompt of -.Dq "ftp\*[Gt] " +.Dq "ftp> " to the user. This can be changed with the .Ic "set prompt" @@ -2183,7 +2183,7 @@ the format .\" ``/<number>trailing'' .Do .Sm off -.Li / Li \*[Lt] Va number Li \*[Gt] +.Li / Li < Va number Li > .Va trailing .Sm on .Dc @@ -2235,7 +2235,7 @@ only .It Ev FTPPROMPT Command-line prompt to use. Defaults to -.Dq "ftp\*[Gt] " . +.Dq "ftp> " . Refer to .Sx COMMAND LINE PROMPT for more information. Index: othersrc/usr.bin/tnftp/src/progressbar.c diff -u othersrc/usr.bin/tnftp/src/progressbar.c:1.15 othersrc/usr.bin/tnftp/src/progressbar.c:1.16 --- othersrc/usr.bin/tnftp/src/progressbar.c:1.15 Sun May 5 11:17:31 2013 +++ othersrc/usr.bin/tnftp/src/progressbar.c Sat Jul 4 09:59:07 2020 @@ -1,5 +1,5 @@ -/* $NetBSD: progressbar.c,v 1.15 2013/05/05 11:17:31 lukem Exp $ */ -/* from NetBSD: progressbar.c,v 1.22 2012/06/27 22:07:36 riastradh Exp */ +/* $NetBSD: progressbar.c,v 1.16 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: progressbar.c,v 1.23 2019/06/22 23:40:33 christos Exp */ /*- * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. @@ -36,7 +36,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID(" NetBSD: progressbar.c,v 1.22 2012/06/27 22:07:36 riastradh Exp "); +__RCSID(" NetBSD: progressbar.c,v 1.23 2019/06/22 23:40:33 christos Exp "); #endif /* not lint */ /* @@ -98,6 +98,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 */ @@ -113,6 +114,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. @@ -150,8 +152,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 */ /* Index: othersrc/usr.bin/tnftp/src/ftp.c diff -u othersrc/usr.bin/tnftp/src/ftp.c:1.21 othersrc/usr.bin/tnftp/src/ftp.c:1.22 --- othersrc/usr.bin/tnftp/src/ftp.c:1.21 Sun Dec 13 14:08:09 2015 +++ othersrc/usr.bin/tnftp/src/ftp.c Sat Jul 4 09:59:07 2020 @@ -1,8 +1,8 @@ -/* $NetBSD: ftp.c,v 1.21 2015/12/13 14:08:09 tron Exp $ */ -/* from NetBSD: ftp.c,v 1.164 2012/07/04 06:09:37 is Exp */ +/* $NetBSD: ftp.c,v 1.22 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: ftp.c,v 1.169 2020/06/08 01:33:27 lukem Exp */ /*- - * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. + * Copyright (c) 1996-2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -98,7 +98,7 @@ #if 0 static char sccsid[] = "@(#)ftp.c 8.6 (Berkeley) 10/27/94"; #else -__RCSID(" NetBSD: ftp.c,v 1.164 2012/07/04 06:09:37 is Exp "); +__RCSID(" NetBSD: ftp.c,v 1.169 2020/06/08 01:33:27 lukem Exp "); #endif #endif /* not lint */ @@ -1561,8 +1561,8 @@ initconn(void) result = COMPLETE + 1; break; } - /* FALLTHROUGH */ #ifdef INET6 + /* FALLTHROUGH */ case AF_INET6: if (!epsv6 || epsv6bad) { result = COMPLETE + 1; @@ -2055,7 +2055,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; @@ -2065,14 +2065,14 @@ 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); } void abort_remote(FILE *din) { - char buf[BUFSIZ]; + unsigned char buf[BUFSIZ]; int nfnd; if (cout == NULL) { Index: othersrc/usr.bin/tnftp/src/ftp_var.h diff -u othersrc/usr.bin/tnftp/src/ftp_var.h:1.12 othersrc/usr.bin/tnftp/src/ftp_var.h:1.13 --- othersrc/usr.bin/tnftp/src/ftp_var.h:1.12 Sun Oct 4 04:53:26 2015 +++ othersrc/usr.bin/tnftp/src/ftp_var.h Sat Jul 4 09:59:07 2020 @@ -1,5 +1,5 @@ -/* $NetBSD: ftp_var.h,v 1.12 2015/10/04 04:53:26 lukem Exp $ */ -/* from NetBSD: ftp_var.h,v 1.83 2015/01/12 14:17:08 christos Exp */ +/* $NetBSD: ftp_var.h,v 1.13 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: ftp_var.h,v 1.85 2017/11/20 21:11:36 kre Exp */ /*- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. @@ -274,6 +274,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 */ @@ -342,11 +343,12 @@ extern struct option optiontab[]; #endif #ifdef NO_DEBUG -#define DPRINTF(...) -#define DWARN(...) +#define DPRINTF(...) (void)0 +#define DWARN(...) (void)0 #else -#define DPRINTF(...) if (ftp_debug) (void)fprintf(ttyout, __VA_ARGS__) -#define DWARN(...) if (ftp_debug) warn(__VA_ARGS__) +#define DWFTP(a) do a; while (/*CONSTCOND*/0) +#define DPRINTF(...) DWFTP(if (ftp_debug) (void)fprintf(ttyout, __VA_ARGS__)) +#define DWARN(...) DWFTP(if (ftp_debug) warn(__VA_ARGS__)) #endif #define STRorNULL(s) ((s) ? (s) : "<null>") Index: othersrc/usr.bin/tnftp/src/main.c diff -u othersrc/usr.bin/tnftp/src/main.c:1.20 othersrc/usr.bin/tnftp/src/main.c:1.21 --- othersrc/usr.bin/tnftp/src/main.c:1.20 Sun Oct 4 04:53:26 2015 +++ othersrc/usr.bin/tnftp/src/main.c Sat Jul 4 09:59:07 2020 @@ -1,5 +1,5 @@ -/* $NetBSD: main.c,v 1.20 2015/10/04 04:53:26 lukem Exp $ */ -/* from NetBSD: main.c,v 1.123 2015/04/23 23:31:23 lukem Exp */ +/* $NetBSD: main.c,v 1.21 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: main.c,v 1.126 2019/02/04 04:09:13 mrg Exp */ /*- * Copyright (c) 1996-2015 The NetBSD Foundation, Inc. @@ -103,7 +103,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 2015/04/23 23:31:23 lukem Exp "); +__RCSID(" NetBSD: main.c,v 1.126 2019/02/04 04:09:13 mrg Exp "); #endif #endif /* not lint */ @@ -333,7 +333,7 @@ main(int volatile argc, char **volatile break; case 'o': - outfile = optarg; + outfile = ftp_strdup(optarg); if (strcmp(outfile, "-") == 0) ttyout = stderr; break; @@ -473,7 +473,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) || @@ -670,7 +669,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", @@ -692,7 +691,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", Index: othersrc/usr.bin/tnftp/src/ssl.c diff -u othersrc/usr.bin/tnftp/src/ssl.c:1.3 othersrc/usr.bin/tnftp/src/ssl.c:1.4 --- othersrc/usr.bin/tnftp/src/ssl.c:1.3 Sun Oct 4 04:53:26 2015 +++ othersrc/usr.bin/tnftp/src/ssl.c Sat Jul 4 09:59:07 2020 @@ -1,5 +1,5 @@ -/* $NetBSD: ssl.c,v 1.3 2015/10/04 04:53:26 lukem Exp $ */ -/* from NetBSD: ssl.c,v 1.5 2015/09/16 15:32:53 joerg Exp */ +/* $NetBSD: ssl.c,v 1.4 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: ssl.c,v 1.8 2019/04/07 00:44:54 christos Exp */ /*- * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav @@ -39,11 +39,12 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID(" NetBSD: ssl.c,v 1.5 2015/09/16 15:32:53 joerg Exp "); +__RCSID(" NetBSD: ssl.c,v 1.8 2019/04/07 00:44:54 christos Exp "); #endif #include <time.h> #include <unistd.h> +#include <string.h> #include <fcntl.h> #include <sys/param.h> @@ -94,6 +95,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) { @@ -104,8 +106,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; @@ -118,7 +120,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; @@ -129,7 +131,7 @@ fetch_writev(struct fetch_connect *conn, if (conn->ssl != NULL) len = SSL_write(conn->ssl, iov->iov_base, iov->iov_len); else - len = writev(conn->sd, iov, iovcnt); + len = writev(fd, iov, iovcnt); if (len == 0) { /* we consider a short write a failure */ /* XXX perhaps we shouldn't in the SSL case */ @@ -137,7 +139,7 @@ fetch_writev(struct fetch_connect *conn, return -1; } if (len < 0) { - if (errno == EINTR) + if (errno == EINTR || errno == EAGAIN) continue; return -1; } @@ -155,11 +157,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]; @@ -188,7 +187,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; } @@ -217,15 +216,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; } @@ -278,23 +278,19 @@ 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); + SSL_free(conn->ssl); + 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 @@ -302,19 +298,19 @@ 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; } static ssize_t @@ -323,7 +319,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; @@ -354,14 +350,45 @@ 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, >)) { + 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); @@ -413,36 +440,25 @@ fetch_read(void *ptr, size_t size, size_ rlen = fetch_ssl_read(conn->ssl, buf, len); else 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; @@ -457,7 +473,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) { @@ -480,13 +496,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) { @@ -538,8 +553,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) Index: othersrc/usr.bin/tnftp/src/ssl.h diff -u othersrc/usr.bin/tnftp/src/ssl.h:1.3 othersrc/usr.bin/tnftp/src/ssl.h:1.4 --- othersrc/usr.bin/tnftp/src/ssl.h:1.3 Sun Oct 4 04:53:26 2015 +++ othersrc/usr.bin/tnftp/src/ssl.h Sat Jul 4 09:59:07 2020 @@ -1,5 +1,5 @@ -/* $NetBSD: ssl.h,v 1.3 2015/10/04 04:53:26 lukem Exp $ */ -/* from NetBSD: ssl.h,v 1.3 2015/09/12 19:38:42 wiz Exp */ +/* $NetBSD: ssl.h,v 1.4 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: ssl.h,v 1.4 2019/04/04 00:36:09 christos Exp */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -39,7 +39,7 @@ 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 *); Index: othersrc/usr.bin/tnftp/src/version.h diff -u othersrc/usr.bin/tnftp/src/version.h:1.7 othersrc/usr.bin/tnftp/src/version.h:1.8 --- othersrc/usr.bin/tnftp/src/version.h:1.7 Sun Oct 4 04:53:26 2015 +++ othersrc/usr.bin/tnftp/src/version.h Sat Jul 4 09:59:07 2020 @@ -1,8 +1,8 @@ -/* $NetBSD: version.h,v 1.7 2015/10/04 04:53:26 lukem Exp $ */ -/* from NetBSD: version.h,v 1.87 2015/09/12 20:18:52 wiz Exp */ +/* $NetBSD: version.h,v 1.8 2020/07/04 09:59:07 lukem Exp $ */ +/* from NetBSD: version.h,v 1.89 2020/06/08 01:33:27 lukem Exp */ /*- - * Copyright (c) 1999-2015 The NetBSD Foundation, Inc. + * Copyright (c) 1999-2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -35,5 +35,5 @@ #endif #ifndef FTP_VERSION -#define FTP_VERSION "20150912" +#define FTP_VERSION "20200608" #endif