Module Name: src
Committed By: christos
Date: Sun Sep 11 20:49:27 UTC 2022
Modified Files:
src/usr.bin/ftp: fetch.c
Log Message:
PR/57003: Handle relative URLs (patch by kim@)
To generate a diff of this commit:
cvs rdiff -u -r1.234 -r1.235 src/usr.bin/ftp/fetch.c
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/fetch.c
diff -u src/usr.bin/ftp/fetch.c:1.234 src/usr.bin/ftp/fetch.c:1.235
--- src/usr.bin/ftp/fetch.c:1.234 Sun Aug 1 11:29:30 2021
+++ src/usr.bin/ftp/fetch.c Sun Sep 11 16:49:27 2022
@@ -1,4 +1,4 @@
-/* $NetBSD: fetch.c,v 1.234 2021/08/01 15:29:30 andvar Exp $ */
+/* $NetBSD: fetch.c,v 1.235 2022/09/11 20:49:27 christos 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.234 2021/08/01 15:29:30 andvar Exp $");
+__RCSID("$NetBSD: fetch.c,v 1.235 2022/09/11 20:49:27 christos 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 *);
@@ -274,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'",
@@ -336,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);
@@ -367,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)
@@ -400,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):
@@ -435,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;
@@ -446,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:
@@ -477,6 +511,7 @@ parse_url(const char *url, const char *d
return (-1);
}
+
if (*url == '\0')
return (0);
@@ -541,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) {
@@ -556,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);
@@ -567,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));
@@ -581,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;
@@ -686,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;
@@ -718,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) ||
@@ -889,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)
@@ -990,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;
@@ -1025,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);
}
@@ -1036,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;
@@ -1156,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:
@@ -1232,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)
@@ -1261,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:
@@ -1299,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;
@@ -1308,7 +1350,7 @@ 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;
@@ -1319,7 +1361,7 @@ fetch_url(const char *url, const char *p
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 *);
@@ -1352,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);
@@ -1368,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;
}
@@ -1423,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;
@@ -1535,7 +1578,7 @@ fetch_url(const char *url, const char *p
switch (negotiate_connection(fin, url, penv, &pi,
&mtime, &wauth, &pauth, &rval, &ischunked,
- __UNVOLATILE(&auth))) {
+ __UNVOLATILE(&auth), &ui)) {
case C_OK:
break;
case C_CLEANUP:
@@ -1642,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.
*/
@@ -1850,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;
@@ -1862,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);
@@ -1874,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)
@@ -1916,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.
@@ -1952,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));
@@ -2002,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);
}
/*
@@ -2024,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.
@@ -2067,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"
@@ -2084,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));
@@ -2179,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;
@@ -2228,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://
@@ -2243,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()
* 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));
}
@@ -2292,7 +2343,7 @@ 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] != '|') {
FREEPTR(outfile);
@@ -2331,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);
@@ -2340,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);
@@ -2364,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 */