- Bug report #1025986. When following a Location: with a custom Host: header

replacement, curl only replaced the Host: header on the initial request
  and didn't replace it on the following ones. This resulted in requests with
  two Host: headers.

  Now, curl checks if the location is on the same host as the initial request
  and then continues to replace the Host: header. And when it moves to another
  host, it doesn't replace the Host: header but it also doesn't make the
  second Host: header get used in the request.

  This change is verified by the two new test cases 184 and 185.
This commit is contained in:
Daniel Stenberg 2004-09-10 20:58:51 +00:00
parent 33929117f9
commit b8b56248bd
7 changed files with 177 additions and 19 deletions

13
CHANGES
View File

@ -6,6 +6,19 @@
Changelog Changelog
Daniel (10 September 2004)
- Bug report #1025986. When following a Location: with a custom Host: header
replacement, curl only replaced the Host: header on the initial request
and didn't replace it on the following ones. This resulted in requests with
two Host: headers.
Now, curl checks if the location is on the same host as the initial request
and then continues to replace the Host: header. And when it moves to another
host, it doesn't replace the Host: header but it also doesn't make the
second Host: header get used in the request.
This change is verified by the two new test cases 184 and 185.
Daniel (31 August 2004) Daniel (31 August 2004)
- David Tarendash fount out that curl_multi_add_handle() returned - David Tarendash fount out that curl_multi_add_handle() returned
CURLM_CALL_MULTI_PERFORM instead of CURLM_OK. CURLM_CALL_MULTI_PERFORM instead of CURLM_OK.

View File

@ -299,8 +299,8 @@ Curl_http_output_auth(struct connectdata *conn,
/* To prevent the user+password to get sent to other than the original /* To prevent the user+password to get sent to other than the original
host due to a location-follow, we do some weirdo checks here */ host due to a location-follow, we do some weirdo checks here */
if(!data->state.this_is_a_follow || if(!data->state.this_is_a_follow ||
!data->state.auth_host || !data->state.first_host ||
curl_strequal(data->state.auth_host, conn->host.name) || curl_strequal(data->state.first_host, conn->host.name) ||
data->set.http_disable_hostname_check_before_authentication) { data->set.http_disable_hostname_check_before_authentication) {
/* Send proxy authentication header if needed */ /* Send proxy authentication header if needed */
@ -1156,14 +1156,13 @@ CURLcode Curl_http_connect(struct connectdata *conn)
return result; return result;
} }
if(conn->bits.user_passwd && !data->state.this_is_a_follow) { if(!data->state.this_is_a_follow) {
/* Authorization: is requested, this is not a followed location, get the /* this is not a followed location, get the original host name */
original host name */ if (data->state.first_host)
if (data->state.auth_host)
/* Free to avoid leaking memory on multiple requests*/ /* Free to avoid leaking memory on multiple requests*/
free(data->state.auth_host); free(data->state.first_host);
data->state.auth_host = strdup(conn->host.name); data->state.first_host = strdup(conn->host.name);
} }
return CURLE_OK; return CURLE_OK;
@ -1363,11 +1362,13 @@ CURLcode Curl_http(struct connectdata *conn)
Curl_safefree(conn->allocptr.host); Curl_safefree(conn->allocptr.host);
ptr = checkheaders(data, "Host:"); ptr = checkheaders(data, "Host:");
if(ptr && !data->state.this_is_a_follow) { if(ptr && (!data->state.this_is_a_follow ||
curl_strequal(data->state.first_host, conn->host.name))) {
/* If we have a given custom Host: header, we extract the host name in /* If we have a given custom Host: header, we extract the host name in
order to possibly use it for cookie reasons later on. We only allow the order to possibly use it for cookie reasons later on. We only allow the
custom Host: header if this is NOT a redirect, as setting Host: in the custom Host: header if this is NOT a redirect, as setting Host: in the
redirected request is being out on thin ice. */ redirected request is being out on thin ice. Except if the host name
is the same as the first one! */
char *start = ptr+strlen("Host:"); char *start = ptr+strlen("Host:");
while(*start && isspace((int)*start )) while(*start && isspace((int)*start ))
start++; start++;
@ -1379,6 +1380,7 @@ CURLcode Curl_http(struct connectdata *conn)
if(ptr != start) { if(ptr != start) {
size_t len=ptr-start; size_t len=ptr-start;
Curl_safefree(conn->allocptr.cookiehost);
conn->allocptr.cookiehost = malloc(len+1); conn->allocptr.cookiehost = malloc(len+1);
if(!conn->allocptr.cookiehost) if(!conn->allocptr.cookiehost)
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
@ -1727,9 +1729,17 @@ CURLcode Curl_http(struct connectdata *conn)
if(*ptr) { if(*ptr) {
/* only send this if the contents was non-blank */ /* only send this if the contents was non-blank */
result = add_bufferf(req_buffer, "%s\r\n", headers->data); if(conn->allocptr.host &&
if(result) /* a Host: header was sent already, don't pass on any custom Host:
return result; header as that will produce *two* in the same request! */
curl_strnequal("Host:", headers->data, 5))
;
else {
result = add_bufferf(req_buffer, "%s\r\n", headers->data);
if(result)
return result;
}
} }
} }
headers = headers->next; headers = headers->next;

View File

@ -211,7 +211,7 @@ CURLcode Curl_close(struct SessionHandle *data)
if(data->change.cookielist) /* clean up list if any */ if(data->change.cookielist) /* clean up list if any */
curl_slist_free_all(data->change.cookielist); curl_slist_free_all(data->change.cookielist);
Curl_safefree(data->state.auth_host); Curl_safefree(data->state.first_host);
Curl_safefree(data->state.scratch); Curl_safefree(data->state.scratch);
if(data->change.proxy_alloc) if(data->change.proxy_alloc)

View File

@ -720,10 +720,10 @@ struct UrlState {
bytes / second */ bytes / second */
bool this_is_a_follow; /* this is a followed Location: request */ bool this_is_a_follow; /* this is a followed Location: request */
char *auth_host; /* if set, this should be the host name that we will char *first_host; /* if set, this should be the host name that we will
sent authorization to, no else. Used to make Location: sent authorization to, no else. Used to make Location:
following not keep sending user+password... This is following not keep sending user+password... This is
strdup() data. strdup() data.
*/ */
struct curl_ssl_session *session; /* array of 'numsessions' size */ struct curl_ssl_session *session; /* array of 'numsessions' size */

View File

@ -25,7 +25,8 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \
test158 test159 test511 test160 test161 test162 test163 test164 \ test158 test159 test511 test160 test161 test162 test163 test164 \
test512 test165 test166 test167 test168 test169 test170 test171 \ test512 test165 test166 test167 test168 test169 test170 test171 \
test172 test204 test205 test173 test174 test175 test176 test177 \ test172 test204 test205 test173 test174 test175 test176 test177 \
test513 test514 test178 test179 test180 test181 test182 test183 test513 test514 test178 test179 test180 test181 test182 test183 \
test184 test185
# The following tests have been removed from the dist since they no longer # The following tests have been removed from the dist since they no longer
# work. We need to fix the test suite's FTPS server first, then bring them # work. We need to fix the test suite's FTPS server first, then bring them

67
tests/data/test184 Normal file
View File

@ -0,0 +1,67 @@
# Server-side
<reply>
<data>
HTTP/1.1 301 OK swsbounce
Date: Thu, 09 Nov 2010 14:49:00 GMT
Content-Length: 4
Location: http://yet.another.host/184
moo
</data>
<data1>
HTTP/1.1 200 OK
Date: Thu, 09 Nov 2010 14:49:00 GMT
Content-Length: 4
moo
</data1>
<datacheck>
HTTP/1.1 301 OK swsbounce
Date: Thu, 09 Nov 2010 14:49:00 GMT
Content-Length: 4
Location: http://yet.another.host/184
HTTP/1.1 200 OK
Date: Thu, 09 Nov 2010 14:49:00 GMT
Content-Length: 4
moo
</datacheck>
</reply>
# Client-side
<client>
<server>
http
</server>
<features>
SSL
</features>
<name>
HTTP replace Host: when following Location: to new host
</name>
<command>
http://deathstar.another.galaxy/184 -L -H "Host: another.visitor.stay.a.while.stay.foreeeeeever" --proxy http://%HOSTIP:%HTTPPORT
</command>
</test>
# Verify data after the test has been "shot"
<verify>
<strip>
^User-Agent: curl/.*
</strip>
<protocol>
GET http://deathstar.another.galaxy/184 HTTP/1.1
User-Agent: curl/7.12.2-CVS (i686-pc-linux-gnu) libcurl/7.12.2-CVS OpenSSL/0.9.6b zlib/1.1.4 libidn/0.4.6
Pragma: no-cache
Accept: */*
Host: another.visitor.stay.a.while.stay.foreeeeeever
GET http://yet.another.host/184 HTTP/1.1
Host: yet.another.host
Pragma: no-cache
Accept: */*
</protocol>
</verify>

67
tests/data/test185 Normal file
View File

@ -0,0 +1,67 @@
# Server-side
<reply>
<data>
HTTP/1.1 301 OK swsbounce
Date: Thu, 09 Nov 2010 14:49:00 GMT
Content-Length: 4
Location: go/west/185
moo
</data>
<data1>
HTTP/1.1 200 OK
Date: Thu, 09 Nov 2010 14:49:00 GMT
Content-Length: 4
moo
</data1>
<datacheck>
HTTP/1.1 301 OK swsbounce
Date: Thu, 09 Nov 2010 14:49:00 GMT
Content-Length: 4
Location: go/west/185
HTTP/1.1 200 OK
Date: Thu, 09 Nov 2010 14:49:00 GMT
Content-Length: 4
moo
</datacheck>
</reply>
# Client-side
<client>
<server>
http
</server>
<features>
SSL
</features>
<name>
HTTP replace Host: when following Location: on the same host
</name>
<command>
http://deathstar.another.galaxy/185 -L -H "Host: another.visitor.stay.a.while.stay.foreeeeeever" --proxy http://%HOSTIP:%HTTPPORT
</command>
</test>
# Verify data after the test has been "shot"
<verify>
<strip>
^User-Agent: curl/.*
</strip>
<protocol>
GET http://deathstar.another.galaxy/185 HTTP/1.1
User-Agent: curl/7.12.2-CVS (i686-pc-linux-gnu) libcurl/7.12.2-CVS OpenSSL/0.9.6b zlib/1.1.4 libidn/0.4.6
Pragma: no-cache
Accept: */*
Host: another.visitor.stay.a.while.stay.foreeeeeever
GET http://deathstar.another.galaxy/go/west/185 HTTP/1.1
Pragma: no-cache
Accept: */*
Host: another.visitor.stay.a.while.stay.foreeeeeever
</protocol>
</verify>