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;
+}

Reply via email to