http: make adding a blank header thread-safe

Previously the function would edit the provided header in-place when a
semicolon is used to signify an empty header. This made it impossible to
use the same set of custom headers in multiple threads simultaneously.

This approach now makes a local copy when it needs to edit the string.

Reported-by: d912e3 on github
Fixes #3578
Closes #3579
This commit is contained in:
Daniel Stenberg 2019-02-18 08:14:52 +01:00
parent 5908e900be
commit 942eb09e8a
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
1 changed files with 21 additions and 13 deletions

View File

@ -1790,9 +1790,16 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
}
else {
if(*(--ptr) == ';') {
/* send no-value custom header if terminated by semicolon */
*ptr = ':';
semicolonp = ptr;
/* copy the source */
semicolonp = strdup(headers->data);
if(!semicolonp) {
Curl_add_buffer_free(&req_buffer);
return CURLE_OUT_OF_MEMORY;
}
/* put a colon where the semicolon is */
semicolonp[ptr - headers->data] = ':';
/* point at the colon */
optr = &semicolonp [ptr - headers->data];
}
}
ptr = optr;
@ -1808,36 +1815,37 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
if(*ptr || semicolonp) {
/* only send this if the contents was non-blank or done special */
CURLcode result = CURLE_OK;
char *compare = semicolonp ? semicolonp : headers->data;
if(conn->allocptr.host &&
/* a Host: header was sent already, don't pass on any custom Host:
header as that will produce *two* in the same request! */
checkprefix("Host:", headers->data))
checkprefix("Host:", compare))
;
else if(data->set.httpreq == HTTPREQ_POST_FORM &&
/* this header (extended by formdata.c) is sent later */
checkprefix("Content-Type:", headers->data))
checkprefix("Content-Type:", compare))
;
else if(data->set.httpreq == HTTPREQ_POST_MIME &&
/* this header is sent later */
checkprefix("Content-Type:", headers->data))
checkprefix("Content-Type:", compare))
;
else if(conn->bits.authneg &&
/* while doing auth neg, don't allow the custom length since
we will force length zero then */
checkprefix("Content-Length:", headers->data))
checkprefix("Content-Length:", compare))
;
else if(conn->allocptr.te &&
/* when asking for Transfer-Encoding, don't pass on a custom
Connection: */
checkprefix("Connection:", headers->data))
checkprefix("Connection:", compare))
;
else if((conn->httpversion == 20) &&
checkprefix("Transfer-Encoding:", headers->data))
checkprefix("Transfer-Encoding:", compare))
/* HTTP/2 doesn't support chunked requests */
;
else if((checkprefix("Authorization:", headers->data) ||
checkprefix("Cookie:", headers->data)) &&
else if((checkprefix("Authorization:", compare) ||
checkprefix("Cookie:", compare)) &&
/* be careful of sending this potentially sensitive header to
other hosts */
(data->state.this_is_a_follow &&
@ -1846,10 +1854,10 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
!strcasecompare(data->state.first_host, conn->host.name)))
;
else {
result = Curl_add_bufferf(&req_buffer, "%s\r\n", headers->data);
result = Curl_add_bufferf(&req_buffer, "%s\r\n", compare);
}
if(semicolonp)
*semicolonp = ';'; /* put back the semicolon */
free(semicolonp);
if(result)
return result;
}