tech-userlevel archive

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

[patch] ftp(1) does not understand "Location"-headers with a relative reference



The "Location"-Header in a HTTP Redirect used to require a full URL,
but as of RFC 7231, relative references are also allowed.

ftp(1) does not understand this; the following patch addresses that issue.

Comments?


diff --git a/usr.bin/ftp/fetch.c b/usr.bin/ftp/fetch.c
index d5b13b6..32f0368 100644
--- a/usr.bin/ftp/fetch.c
+++ b/usr.bin/ftp/fetch.c
@@ -115,6 +115,7 @@ static int	parse_url(const char *, const char *, struct urlinfo *,
 static void	url_decode(char *);
 static void	freeauthinfo(struct authinfo *);
 static void	freeurlinfo(struct urlinfo *);
+static int	isfullurl(const char *);
 
 static int	redirect_loop;
 
@@ -1010,9 +1011,29 @@ negotiate_connection(FETCH *fin, const char *url, const char *penv,
 			getmtime(cp, mtime);
 
 		} else if (match_token(&cp, "Location:")) {
-			location = ftp_strdup(cp);
+			if (!isfullurl(cp)) {
+				/* Redirect using "relative reference",
+				 * RFC 7231 7.1.2.  The destination is just a
+				 * path, which may be absolute or relative */
+
+				/* This is going to be the base URL */
+				char *bu = ftp_strdup(url);
+
+				/* Cut off URL at the first slash if we're
+				 * being redirected using an absolute path, or
+				 * at the last slash if relative. */
+				if (cp[0] == '/')
+					*strchr(strstr(bu, "//")+2, '/') = '\0';
+				else
+					*strrchr(bu, '/') = '\0';
+
+				location = malloc(strlen(bu) + strlen(cp) + 1);
+				sprintf(location, "%s%s", bu, cp);
+				free(bu);
+			} else
+				location = ftp_strdup(cp);
 			DPRINTF("%s: parsed location as `%s'\n",
-			    __func__, cp);
+			    __func__, location);
 
 		} else if (match_token(&cp, "Transfer-Encoding:")) {
 			if (match_token(&cp, "binary")) {
@@ -2334,3 +2355,14 @@ auto_put(int argc, char **argv, const char *uploadserver)
 	FREEPTR(uargv[2]);
 	return (rval);
 }
+
+/* Determine whether the given string is an URL (starting with <scheme>:)
+ * or a lone path (be it absolute or relative) */
+static int
+isfullurl(const char *url)
+{
+	size_t slash = strcspn(url, "/");
+	size_t colon = strcspn(url, ":");
+
+	return slash > colon;
+}


Home | Main Index | Thread Index | Old Index