mirror of
https://github.com/curl/curl.git
synced 2025-09-11 06:32:41 +03:00
urlapi: fix redirect to a new fragment or query (only)
The redirect logic was broken when the redirect-to URL was a relative URL only as a fragment or query (starting with '#' or '?'). Extended test 1560 to reproduce, then verify. Reported-by: Jeroen Ooms Fixes #15836 Closes #15848
This commit is contained in:
parent
687a62f100
commit
66e5351e0a
|
@ -41,6 +41,9 @@ order to try out server implementations.
|
||||||
|
|
||||||
By default libcurl normalizes such sequences before using the path.
|
By default libcurl normalizes such sequences before using the path.
|
||||||
|
|
||||||
|
This is a request for the *first* request libcurl issues. When following
|
||||||
|
redirects, it may no longer apply.
|
||||||
|
|
||||||
The corresponding flag for the curl_url_set(3) function is called
|
The corresponding flag for the curl_url_set(3) function is called
|
||||||
**CURLU_PATH_AS_IS**.
|
**CURLU_PATH_AS_IS**.
|
||||||
|
|
||||||
|
|
115
lib/urlapi.c
115
lib/urlapi.c
|
@ -258,78 +258,41 @@ static CURLcode concat_url(char *base, const char *relurl, char **newurl)
|
||||||
problems in the future...
|
problems in the future...
|
||||||
*/
|
*/
|
||||||
struct dynbuf newest;
|
struct dynbuf newest;
|
||||||
char *protsep;
|
|
||||||
char *pathsep;
|
|
||||||
bool host_changed = FALSE;
|
bool host_changed = FALSE;
|
||||||
const char *useurl = relurl;
|
const char *useurl = relurl;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
CURLUcode uc;
|
CURLUcode uc;
|
||||||
bool skip_slash = FALSE;
|
|
||||||
*newurl = NULL;
|
|
||||||
|
|
||||||
/* protsep points to the start of the hostname */
|
/* protsep points to the start of the hostname */
|
||||||
protsep = strstr(base, "//");
|
char *protsep = strstr(base, "//");
|
||||||
|
DEBUGASSERT(protsep);
|
||||||
if(!protsep)
|
if(!protsep)
|
||||||
protsep = base;
|
protsep = base;
|
||||||
else
|
else
|
||||||
protsep += 2; /* pass the slashes */
|
protsep += 2; /* pass the slashes */
|
||||||
|
|
||||||
if('/' != relurl[0]) {
|
*newurl = NULL;
|
||||||
int level = 0;
|
if(('/' != relurl[0]) && ('#' != relurl[0])) {
|
||||||
|
/* First we need to find out if there is a ?-letter in the original URL,
|
||||||
/* First we need to find out if there is a ?-letter in the URL,
|
|
||||||
and cut it and the right-side of that off */
|
and cut it and the right-side of that off */
|
||||||
pathsep = strchr(protsep, '?');
|
char *pathsep = strchr(protsep, '?');
|
||||||
if(pathsep)
|
if(pathsep)
|
||||||
*pathsep = 0;
|
*pathsep = 0;
|
||||||
|
else {
|
||||||
/* we have a relative path to append to the last slash if there is one
|
/* if not, cut off the potential fragment */
|
||||||
available, or the new URL is just a query string (starts with a '?') or
|
pathsep = strchr(protsep, '#');
|
||||||
a fragment (starts with '#') we append the new one at the end of the
|
|
||||||
current URL */
|
|
||||||
if((useurl[0] != '?') && (useurl[0] != '#')) {
|
|
||||||
pathsep = strrchr(protsep, '/');
|
|
||||||
if(pathsep)
|
if(pathsep)
|
||||||
*pathsep = 0;
|
*pathsep = 0;
|
||||||
|
|
||||||
/* Check if there is any slash after the hostname, and if so, remember
|
|
||||||
that position instead */
|
|
||||||
pathsep = strchr(protsep, '/');
|
|
||||||
if(pathsep)
|
|
||||||
protsep = pathsep + 1;
|
|
||||||
else
|
|
||||||
protsep = NULL;
|
|
||||||
|
|
||||||
/* now deal with one "./" or any amount of "../" in the newurl
|
|
||||||
and act accordingly */
|
|
||||||
|
|
||||||
if((useurl[0] == '.') && (useurl[1] == '/'))
|
|
||||||
useurl += 2; /* just skip the "./" */
|
|
||||||
|
|
||||||
while((useurl[0] == '.') &&
|
|
||||||
(useurl[1] == '.') &&
|
|
||||||
(useurl[2] == '/')) {
|
|
||||||
level++;
|
|
||||||
useurl += 3; /* pass the "../" */
|
|
||||||
}
|
|
||||||
|
|
||||||
if(protsep) {
|
|
||||||
while(level--) {
|
|
||||||
/* cut off one more level from the right of the original URL */
|
|
||||||
pathsep = strrchr(protsep, '/');
|
|
||||||
if(pathsep)
|
|
||||||
*pathsep = 0;
|
|
||||||
else {
|
|
||||||
*protsep = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
skip_slash = TRUE;
|
/* if the redirect-to piece is not just a query, cut the path after the
|
||||||
|
last slash */
|
||||||
|
if(useurl[0] != '?') {
|
||||||
|
pathsep = strrchr(protsep, '/');
|
||||||
|
if(pathsep)
|
||||||
|
pathsep[1] = 0; /* leave the slash */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if('/' == relurl[0]) {
|
||||||
/* We got a new absolute path for this server */
|
/* We got a new absolute path for this server */
|
||||||
|
|
||||||
if(relurl[1] == '/') {
|
if(relurl[1] == '/') {
|
||||||
|
@ -341,29 +304,20 @@ static CURLcode concat_url(char *base, const char *relurl, char **newurl)
|
||||||
host_changed = TRUE;
|
host_changed = TRUE;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* cut off the original URL from the first slash, or deal with URLs
|
/* cut the original URL at first slash */
|
||||||
without slash */
|
char *pathsep = strchr(protsep, '/');
|
||||||
pathsep = strchr(protsep, '/');
|
if(pathsep)
|
||||||
if(pathsep) {
|
|
||||||
/* When people use badly formatted URLs, such as
|
|
||||||
"http://www.example.com?dir=/home/daniel" we must not use the first
|
|
||||||
slash, if there is a ?-letter before it! */
|
|
||||||
char *sep = strchr(protsep, '?');
|
|
||||||
if(sep && (sep < pathsep))
|
|
||||||
pathsep = sep;
|
|
||||||
*pathsep = 0;
|
*pathsep = 0;
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* There was no slash. Now, since we might be operating on a badly
|
|
||||||
formatted URL, such as "http://www.example.com?id=2380" which does
|
|
||||||
not use a slash separator as it is supposed to, we need to check
|
|
||||||
for a ?-letter as well! */
|
|
||||||
pathsep = strchr(protsep, '?');
|
|
||||||
if(pathsep)
|
|
||||||
*pathsep = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
/* the relative piece starts with '#' */
|
||||||
|
|
||||||
|
/* If there is a fragment in the original URL, cut it off */
|
||||||
|
char *pathsep = strchr(protsep, '#');
|
||||||
|
if(pathsep)
|
||||||
|
*pathsep = 0;
|
||||||
|
}
|
||||||
|
|
||||||
Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH);
|
Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH);
|
||||||
|
|
||||||
|
@ -372,15 +326,6 @@ static CURLcode concat_url(char *base, const char *relurl, char **newurl)
|
||||||
if(result)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
/* check if we need to append a slash */
|
|
||||||
if(('/' == useurl[0]) || (protsep && !*protsep) || skip_slash)
|
|
||||||
;
|
|
||||||
else {
|
|
||||||
result = Curl_dyn_addn(&newest, "/", 1);
|
|
||||||
if(result)
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* then append the new piece on the right side */
|
/* then append the new piece on the right side */
|
||||||
uc = urlencode_str(&newest, useurl, strlen(useurl), !host_changed,
|
uc = urlencode_str(&newest, useurl, strlen(useurl), !host_changed,
|
||||||
FALSE);
|
FALSE);
|
||||||
|
@ -1882,7 +1827,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
|
||||||
if(result)
|
if(result)
|
||||||
return cc2cu(result);
|
return cc2cu(result);
|
||||||
|
|
||||||
uc = parseurl_and_replace(redired_url, u, flags);
|
uc = parseurl_and_replace(redired_url, u, flags&~CURLU_PATH_AS_IS);
|
||||||
free(redired_url);
|
free(redired_url);
|
||||||
return uc;
|
return uc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ Host: %HOSTIP:%HTTPPORT
|
||||||
User-Agent: curl/%VERSION
|
User-Agent: curl/%VERSION
|
||||||
Accept: */*
|
Accept: */*
|
||||||
|
|
||||||
GET /../%TESTNUMBER0002 HTTP/1.1
|
GET /%TESTNUMBER0002 HTTP/1.1
|
||||||
Host: %HOSTIP:%HTTPPORT
|
Host: %HOSTIP:%HTTPPORT
|
||||||
User-Agent: curl/%VERSION
|
User-Agent: curl/%VERSION
|
||||||
Accept: */*
|
Accept: */*
|
||||||
|
|
|
@ -1143,6 +1143,38 @@ static CURLUcode updateurl(CURLU *u, const char *cmd, unsigned int setflags)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct redircase set_url_list[] = {
|
static const struct redircase set_url_list[] = {
|
||||||
|
{"http://example.org#withs/ash", "/moo#frag",
|
||||||
|
"http://example.org/moo#frag",
|
||||||
|
0, 0, CURLUE_OK},
|
||||||
|
{"http://example.org/", "../path/././../././../moo",
|
||||||
|
"http://example.org/moo",
|
||||||
|
0, 0, CURLUE_OK},
|
||||||
|
|
||||||
|
{"http://example.org?bar/moo", "?weird",
|
||||||
|
"http://example.org/?weird", 0, 0, CURLUE_OK},
|
||||||
|
{"http://example.org/foo?bar", "?weird",
|
||||||
|
"http://example.org/foo?weird", 0, 0, CURLUE_OK},
|
||||||
|
{"http://example.org/foo", "?weird",
|
||||||
|
"http://example.org/foo?weird", 0, 0, CURLUE_OK},
|
||||||
|
{"http://example.org", "?weird",
|
||||||
|
"http://example.org/?weird", 0, 0, CURLUE_OK},
|
||||||
|
{"http://example.org/#original", "?weird#moo",
|
||||||
|
"http://example.org/?weird#moo", 0, 0, CURLUE_OK},
|
||||||
|
|
||||||
|
{"http://example.org?bar/moo#yes/path", "#new/slash",
|
||||||
|
"http://example.org/?bar/moo#new/slash", 0, 0, CURLUE_OK},
|
||||||
|
{"http://example.org/foo?bar", "#weird",
|
||||||
|
"http://example.org/foo?bar#weird", 0, 0, CURLUE_OK},
|
||||||
|
{"http://example.org/foo?bar#original", "#weird",
|
||||||
|
"http://example.org/foo?bar#weird", 0, 0, CURLUE_OK},
|
||||||
|
{"http://example.org/foo#original", "#weird",
|
||||||
|
"http://example.org/foo#weird", 0, 0, CURLUE_OK},
|
||||||
|
{"http://example.org/#original", "#weird",
|
||||||
|
"http://example.org/#weird", 0, 0, CURLUE_OK},
|
||||||
|
{"http://example.org#original", "#weird",
|
||||||
|
"http://example.org/#weird", 0, 0, CURLUE_OK},
|
||||||
|
{"http://example.org/foo?bar", "moo?hey#weird",
|
||||||
|
"http://example.org/moo?hey#weird", 0, 0, CURLUE_OK},
|
||||||
{"http://example.org/",
|
{"http://example.org/",
|
||||||
"../path/././../../moo",
|
"../path/././../../moo",
|
||||||
"http://example.org/moo",
|
"http://example.org/moo",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user