parent
edd573d980
commit
04289c62de
245
lib/http.c
245
lib/http.c
@ -273,46 +273,28 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Strip off leading and trailing whitespace from the value in the
|
* Strip off leading and trailing whitespace from the value in the given HTTP
|
||||||
* given HTTP header line and return a strdupped copy. Returns NULL in
|
* header line and return a strdup()ed copy. Returns NULL in case of
|
||||||
* case of allocation failure. Returns an empty string if the header value
|
* allocation failure or bad input. Returns an empty string if the header
|
||||||
* consists entirely of whitespace.
|
* value consists entirely of whitespace.
|
||||||
|
*
|
||||||
|
* If the header is provided as "name;", ending with a semicolon, it must
|
||||||
|
* return a blank string.
|
||||||
*/
|
*/
|
||||||
char *Curl_copy_header_value(const char *header)
|
char *Curl_copy_header_value(const char *header)
|
||||||
{
|
{
|
||||||
const char *start;
|
struct Curl_str out;
|
||||||
const char *end;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
/* Find the end of the header name */
|
/* find the end of the header name */
|
||||||
while(*header && (*header != ':'))
|
if(!Curl_str_cspn(&header, &out, ";:") &&
|
||||||
++header;
|
(!Curl_str_single(&header, ':') || !Curl_str_single(&header, ';'))) {
|
||||||
|
Curl_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE);
|
||||||
|
Curl_str_trimblanks(&out);
|
||||||
|
|
||||||
if(*header)
|
return Curl_memdup0(Curl_str(&out), Curl_strlen(&out));
|
||||||
/* Skip over colon */
|
}
|
||||||
++header;
|
/* bad input */
|
||||||
|
return NULL;
|
||||||
/* Find the first non-space letter */
|
|
||||||
start = header;
|
|
||||||
while(ISSPACE(*start))
|
|
||||||
start++;
|
|
||||||
|
|
||||||
end = strchr(start, '\r');
|
|
||||||
if(!end)
|
|
||||||
end = strchr(start, '\n');
|
|
||||||
if(!end)
|
|
||||||
end = strchr(start, '\0');
|
|
||||||
if(!end)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* skip all trailing space letters */
|
|
||||||
while((end > start) && ISSPACE(*end))
|
|
||||||
end--;
|
|
||||||
|
|
||||||
/* get length of the type */
|
|
||||||
len = end - start + 1;
|
|
||||||
|
|
||||||
return Curl_memdup0(start, len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_HTTP_AUTH
|
#ifndef CURL_DISABLE_HTTP_AUTH
|
||||||
@ -1468,9 +1450,8 @@ Curl_compareheader(const char *headerline, /* line to check */
|
|||||||
* The field value MAY be preceded by any amount of LWS, though a single SP
|
* The field value MAY be preceded by any amount of LWS, though a single SP
|
||||||
* is preferred." */
|
* is preferred." */
|
||||||
|
|
||||||
size_t len;
|
const char *p;
|
||||||
const char *start;
|
struct Curl_str val;
|
||||||
const char *end;
|
|
||||||
DEBUGASSERT(hlen);
|
DEBUGASSERT(hlen);
|
||||||
DEBUGASSERT(clen);
|
DEBUGASSERT(clen);
|
||||||
DEBUGASSERT(header);
|
DEBUGASSERT(header);
|
||||||
@ -1480,31 +1461,21 @@ Curl_compareheader(const char *headerline, /* line to check */
|
|||||||
return FALSE; /* does not start with header */
|
return FALSE; /* does not start with header */
|
||||||
|
|
||||||
/* pass the header */
|
/* pass the header */
|
||||||
start = &headerline[hlen];
|
p = &headerline[hlen];
|
||||||
|
|
||||||
/* pass all whitespace */
|
if(Curl_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE))
|
||||||
while(ISSPACE(*start))
|
return FALSE;
|
||||||
start++;
|
Curl_str_trimblanks(&val);
|
||||||
|
|
||||||
/* find the end of the header line */
|
|
||||||
end = strchr(start, '\r'); /* lines end with CRLF */
|
|
||||||
if(!end) {
|
|
||||||
/* in case there is a non-standard compliant line here */
|
|
||||||
end = strchr(start, '\n');
|
|
||||||
|
|
||||||
if(!end)
|
|
||||||
/* hm, there is no line ending here, use the zero byte! */
|
|
||||||
end = strchr(start, '\0');
|
|
||||||
}
|
|
||||||
|
|
||||||
len = end-start; /* length of the content part of the input line */
|
|
||||||
|
|
||||||
/* find the content string in the rest of the line */
|
/* find the content string in the rest of the line */
|
||||||
for(; len >= clen; len--, start++) {
|
if(Curl_strlen(&val) >= clen) {
|
||||||
if(strncasecompare(start, content, clen))
|
size_t len;
|
||||||
return TRUE; /* match! */
|
p = Curl_str(&val);
|
||||||
|
for(len = Curl_strlen(&val); len >= Curl_strlen(&val); len--, p++) {
|
||||||
|
if(strncasecompare(p, content, clen))
|
||||||
|
return TRUE; /* match! */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE; /* no match */
|
return FALSE; /* no match */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1623,7 +1594,6 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
|
|||||||
bool is_connect, int httpversion,
|
bool is_connect, int httpversion,
|
||||||
struct dynbuf *req)
|
struct dynbuf *req)
|
||||||
{
|
{
|
||||||
char *ptr;
|
|
||||||
struct curl_slist *h[2];
|
struct curl_slist *h[2];
|
||||||
struct curl_slist *headers;
|
struct curl_slist *headers;
|
||||||
int numlists = 1; /* by default */
|
int numlists = 1; /* by default */
|
||||||
@ -1663,98 +1633,81 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
|
|||||||
|
|
||||||
/* loop through one or two lists */
|
/* loop through one or two lists */
|
||||||
for(i = 0; i < numlists; i++) {
|
for(i = 0; i < numlists; i++) {
|
||||||
headers = h[i];
|
for(headers = h[i]; headers; headers = headers->next) {
|
||||||
|
CURLcode result = CURLE_OK;
|
||||||
|
bool blankheader = FALSE;
|
||||||
|
struct Curl_str name;
|
||||||
|
const char *p = headers->data;
|
||||||
|
const char *origp = p;
|
||||||
|
|
||||||
while(headers) {
|
/* explicitly asked to send header without content is done by a header
|
||||||
char *semicolonp = NULL;
|
that ends with a semicolon, but there must be no colon present in the
|
||||||
ptr = strchr(headers->data, ':');
|
name */
|
||||||
if(!ptr) {
|
if(!Curl_str_until(&p, &name, MAX_HTTP_RESP_HEADER_SIZE, ';') &&
|
||||||
char *optr;
|
!Curl_str_single(&p, ';') &&
|
||||||
/* no colon, semicolon? */
|
!Curl_str_single(&p, '\0') &&
|
||||||
ptr = strchr(headers->data, ';');
|
!memchr(Curl_str(&name), ':', Curl_strlen(&name)))
|
||||||
if(ptr) {
|
blankheader = TRUE;
|
||||||
optr = ptr;
|
else {
|
||||||
ptr++; /* pass the semicolon */
|
p = origp;
|
||||||
while(ISSPACE(*ptr))
|
if(!Curl_str_until(&p, &name, MAX_HTTP_RESP_HEADER_SIZE, ':') &&
|
||||||
ptr++;
|
!Curl_str_single(&p, ':')) {
|
||||||
|
struct Curl_str val;
|
||||||
if(*ptr) {
|
Curl_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE);
|
||||||
/* this may be used for something else in the future */
|
Curl_str_trimblanks(&val);
|
||||||
optr = NULL;
|
if(!Curl_strlen(&val))
|
||||||
}
|
/* no content, don't send this */
|
||||||
else {
|
continue;
|
||||||
if(*(--ptr) == ';') {
|
|
||||||
/* copy the source */
|
|
||||||
semicolonp = strdup(headers->data);
|
|
||||||
if(!semicolonp) {
|
|
||||||
Curl_dyn_free(req);
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
/* no colon */
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if(ptr && (ptr != headers->data)) {
|
|
||||||
/* we require a colon for this to be a true header */
|
|
||||||
|
|
||||||
ptr++; /* pass the colon */
|
/* only send this if the contents was non-blank or done special */
|
||||||
while(ISSPACE(*ptr))
|
|
||||||
ptr++;
|
|
||||||
|
|
||||||
if(*ptr || semicolonp) {
|
if(data->state.aptr.host &&
|
||||||
/* only send this if the contents was non-blank or done special */
|
/* a Host: header was sent already, do not pass on any custom
|
||||||
CURLcode result = CURLE_OK;
|
Host: header as that will produce *two* in the same
|
||||||
char *compare = semicolonp ? semicolonp : headers->data;
|
request! */
|
||||||
|
Curl_str_casecompare(&name, "Host"))
|
||||||
|
;
|
||||||
|
else if(data->state.httpreq == HTTPREQ_POST_FORM &&
|
||||||
|
/* this header (extended by formdata.c) is sent later */
|
||||||
|
Curl_str_casecompare(&name, "Content-Type"))
|
||||||
|
;
|
||||||
|
else if(data->state.httpreq == HTTPREQ_POST_MIME &&
|
||||||
|
/* this header is sent later */
|
||||||
|
Curl_str_casecompare(&name, "Content-Type"))
|
||||||
|
;
|
||||||
|
else if(data->req.authneg &&
|
||||||
|
/* while doing auth neg, do not allow the custom length since
|
||||||
|
we will force length zero then */
|
||||||
|
Curl_str_casecompare(&name, "Content-Length"))
|
||||||
|
;
|
||||||
|
else if(data->state.aptr.te &&
|
||||||
|
/* when asking for Transfer-Encoding, do not pass on a custom
|
||||||
|
Connection: */
|
||||||
|
Curl_str_casecompare(&name, "Connection"))
|
||||||
|
;
|
||||||
|
else if((httpversion >= 20) &&
|
||||||
|
Curl_str_casecompare(&name, "Transfer-Encoding"))
|
||||||
|
/* HTTP/2 does not support chunked requests */
|
||||||
|
;
|
||||||
|
else if((Curl_str_casecompare(&name, "Authorization") ||
|
||||||
|
Curl_str_casecompare(&name, "Cookie")) &&
|
||||||
|
/* be careful of sending this potentially sensitive header to
|
||||||
|
other hosts */
|
||||||
|
!Curl_auth_allowed_to_host(data))
|
||||||
|
;
|
||||||
|
else if(blankheader)
|
||||||
|
result = Curl_dyn_addf(req, "%.*s:\r\n", (int)Curl_strlen(&name),
|
||||||
|
Curl_str(&name));
|
||||||
|
else
|
||||||
|
result = Curl_dyn_addf(req, "%s\r\n", origp);
|
||||||
|
|
||||||
if(data->state.aptr.host &&
|
if(result)
|
||||||
/* a Host: header was sent already, do not pass on any custom
|
return result;
|
||||||
Host: header as that will produce *two* in the same
|
|
||||||
request! */
|
|
||||||
checkprefix("Host:", compare))
|
|
||||||
;
|
|
||||||
else if(data->state.httpreq == HTTPREQ_POST_FORM &&
|
|
||||||
/* this header (extended by formdata.c) is sent later */
|
|
||||||
checkprefix("Content-Type:", compare))
|
|
||||||
;
|
|
||||||
else if(data->state.httpreq == HTTPREQ_POST_MIME &&
|
|
||||||
/* this header is sent later */
|
|
||||||
checkprefix("Content-Type:", compare))
|
|
||||||
;
|
|
||||||
else if(data->req.authneg &&
|
|
||||||
/* while doing auth neg, do not allow the custom length since
|
|
||||||
we will force length zero then */
|
|
||||||
checkprefix("Content-Length:", compare))
|
|
||||||
;
|
|
||||||
else if(data->state.aptr.te &&
|
|
||||||
/* when asking for Transfer-Encoding, do not pass on a custom
|
|
||||||
Connection: */
|
|
||||||
checkprefix("Connection:", compare))
|
|
||||||
;
|
|
||||||
else if((httpversion >= 20) &&
|
|
||||||
checkprefix("Transfer-Encoding:", compare))
|
|
||||||
/* HTTP/2 does not support chunked requests */
|
|
||||||
;
|
|
||||||
else if((checkprefix("Authorization:", compare) ||
|
|
||||||
checkprefix("Cookie:", compare)) &&
|
|
||||||
/* be careful of sending this potentially sensitive header to
|
|
||||||
other hosts */
|
|
||||||
!Curl_auth_allowed_to_host(data))
|
|
||||||
;
|
|
||||||
else {
|
|
||||||
result = Curl_dyn_addf(req, "%s\r\n", compare);
|
|
||||||
}
|
|
||||||
if(semicolonp)
|
|
||||||
free(semicolonp);
|
|
||||||
if(result)
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
headers = headers->next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -114,7 +114,10 @@ void *Curl_memdup0(const char *src, size_t length)
|
|||||||
char *buf = malloc(length + 1);
|
char *buf = malloc(length + 1);
|
||||||
if(!buf)
|
if(!buf)
|
||||||
return NULL;
|
return NULL;
|
||||||
memcpy(buf, src, length);
|
if(length) {
|
||||||
|
DEBUGASSERT(src); /* must never be NULL */
|
||||||
|
memcpy(buf, src, length);
|
||||||
|
}
|
||||||
buf[length] = 0;
|
buf[length] = 0;
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,6 +63,29 @@ int Curl_str_word(const char **linep, struct Curl_str *out,
|
|||||||
return Curl_str_until(linep, out, max, ' ');
|
return Curl_str_until(linep, out, max, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get a word until a newline byte or end of string. At least one byte long.
|
||||||
|
return non-zero on error */
|
||||||
|
int Curl_str_untilnl(const char **linep, struct Curl_str *out,
|
||||||
|
const size_t max)
|
||||||
|
{
|
||||||
|
const char *s = *linep;
|
||||||
|
size_t len = 0;
|
||||||
|
DEBUGASSERT(linep && *linep && out && max);
|
||||||
|
|
||||||
|
Curl_str_init(out);
|
||||||
|
while(*s && !ISNEWLINE(*s)) {
|
||||||
|
s++;
|
||||||
|
if(++len > max)
|
||||||
|
return STRE_BIG;
|
||||||
|
}
|
||||||
|
if(!len)
|
||||||
|
return STRE_SHORT;
|
||||||
|
out->str = *linep;
|
||||||
|
out->len = len;
|
||||||
|
*linep = s; /* point to the first byte after the word */
|
||||||
|
return STRE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Get a "quoted" word. No escaping possible.
|
/* Get a "quoted" word. No escaping possible.
|
||||||
return non-zero on error */
|
return non-zero on error */
|
||||||
|
|||||||
@ -56,6 +56,11 @@ int Curl_str_word(const char **linep, struct Curl_str *out, const size_t max);
|
|||||||
int Curl_str_until(const char **linep, struct Curl_str *out, const size_t max,
|
int Curl_str_until(const char **linep, struct Curl_str *out, const size_t max,
|
||||||
char delim);
|
char delim);
|
||||||
|
|
||||||
|
/* Get a word until a newline byte or end of string. At least one byte long.
|
||||||
|
return non-zero on error */
|
||||||
|
int Curl_str_untilnl(const char **linep, struct Curl_str *out,
|
||||||
|
const size_t max);
|
||||||
|
|
||||||
/* Get a "quoted" word. No escaping possible.
|
/* Get a "quoted" word. No escaping possible.
|
||||||
return non-zero on error */
|
return non-zero on error */
|
||||||
int Curl_str_quotedword(const char **linep, struct Curl_str *out,
|
int Curl_str_quotedword(const char **linep, struct Curl_str *out,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user