This commit is contained in:
Daniel Stenberg 2026-07-30 03:00:10 +01:00 committed by GitHub
commit 4d25e56d20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 219 additions and 27 deletions

View file

@ -1334,11 +1334,33 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
return CURLE_UNKNOWN_OPTION;
}
static bool str_has_crlf(const char *p)
{
while(p && *p) {
if(ISNEWLINE(*p))
return TRUE;
p++;
}
return FALSE;
}
static bool list_has_crlf(const struct curl_slist *slist)
{
while(slist) {
if(str_has_crlf(slist->data))
return TRUE;
slist = slist->next;
}
return FALSE;
}
static CURLcode setopt_slist(struct Curl_easy *data, CURLoption option,
struct curl_slist *slist)
{
CURLcode result = CURLE_OK;
struct UserDefined *s = &data->set;
struct curl_slist **slist_set = NULL;
struct curl_slist **slist_set2 = NULL;
switch(option) {
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXYHEADER:
@ -1352,7 +1374,7 @@ static CURLcode setopt_slist(struct Curl_easy *data, CURLoption option,
*
* Set this option to NULL to restore the previous behavior.
*/
s->proxyheaders = slist;
slist_set = &s->proxyheaders;
break;
#endif
#ifndef CURL_DISABLE_HTTP
@ -1360,7 +1382,7 @@ static CURLcode setopt_slist(struct Curl_easy *data, CURLoption option,
/*
* Set a list of aliases for HTTP 200 in response header
*/
s->http200aliases = slist;
slist_set = &s->http200aliases;
break;
#endif
#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
@ -1368,19 +1390,19 @@ static CURLcode setopt_slist(struct Curl_easy *data, CURLoption option,
/*
* List of RAW FTP commands to use after a transfer
*/
s->postquote = slist;
slist_set = &s->postquote;
break;
case CURLOPT_PREQUOTE:
/*
* List of RAW FTP commands to use prior to RETR (Wesley Laxton)
*/
s->prequote = slist;
slist_set = &s->prequote;
break;
case CURLOPT_QUOTE:
/*
* List of RAW FTP commands to use before a transfer
*/
s->quote = slist;
slist_set = &s->quote;
break;
#endif
case CURLOPT_RESOLVE:
@ -1397,15 +1419,15 @@ static CURLcode setopt_slist(struct Curl_easy *data, CURLoption option,
* This API can remove any entry from the DNS cache, but only entries
* that are not actually in use right now will be pruned immediately.
*/
s->resolve = slist;
data->state.resolve = s->resolve;
slist_set = &s->resolve;
slist_set2 = &data->state.resolve;
break;
#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MIME)
case CURLOPT_HTTPHEADER:
/*
* Set a list with HTTP headers to use (or replace internals with)
*/
s->headers = slist;
slist_set = &s->headers;
break;
#endif
#ifndef CURL_DISABLE_TELNET
@ -1413,21 +1435,27 @@ static CURLcode setopt_slist(struct Curl_easy *data, CURLoption option,
/*
* Set a linked list of telnet options
*/
s->telnet_options = slist;
slist_set = &s->telnet_options;
break;
#endif
#ifndef CURL_DISABLE_SMTP
case CURLOPT_MAIL_RCPT:
/* Set the list of mail recipients */
s->mail_rcpt = slist;
slist_set = &s->mail_rcpt;
break;
#endif
case CURLOPT_CONNECT_TO:
s->connect_to = slist;
slist_set = &s->connect_to;
break;
default:
return CURLE_UNKNOWN_OPTION;
}
if(list_has_crlf(slist))
return CURLE_BAD_FUNCTION_ARGUMENT;
/* assign once the list has been confirmed to be decent */
*slist_set = slist;
if(slist_set2)
*slist_set2 = slist;
return result;
}

View file

@ -948,7 +948,14 @@ static CURLcode credentials_and_headers_setopts(struct OperationConfig *config,
if(config->authtype)
my_setopt_bitmask(curl, CURLOPT_HTTPAUTH, config->authtype);
my_setopt_slist(curl, CURLOPT_HTTPHEADER, config->headers);
result = my_setopt_slist(curl, CURLOPT_HTTPHEADER, config->headers);
if(result == CURLE_BAD_FUNCTION_ARGUMENT) {
errorf("Illegal CR/LF in --header data");
config->synthetic_error = TRUE;
return result;
}
if(setopt_bad(result))
return result;
if(proto_http || proto_rtsp) {
MY_SETOPT_STR(curl, CURLOPT_REFERER, config->referer);

View file

@ -242,7 +242,7 @@ test1979 test1980 test1981 test1982 test1983 test1984 \
\
test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \
test2008 test2009 test2010 test2011 test2012 test2013 test2014 test2015 \
\
test2016 test2017 \
test2023 \
test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 \
test2032 test2033 test2034 test2035 test2036 test2037 test2038 test2039 \

32
tests/data/test2016 Normal file
View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="US-ASCII"?>
<testcase>
<info>
<keywords>
HTTP
</keywords>
</info>
# Server-side
<reply>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
Attempt sending a carriage return in a HTTP header
</name>
<command>
http://%HOSTIP:%HTTPPORT/%TESTNUMBER -H 'A: %CR'
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<errorcode>
43
</errorcode>
</verify>
</testcase>

34
tests/data/test2017 Normal file
View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="US-ASCII"?>
<testcase>
<info>
<keywords>
HTTP
curl_easy_setopt
</keywords>
</info>
# Server-side
<reply>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
Pass and reject CR and LF in setopt slist options
</name>
<command>
http://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<tool>
lib%TESTNUMBER
</tool>
</client>
# Verify data after the test has been "shot"
<verify>
</verify>
</testcase>

View file

@ -236,20 +236,6 @@ class TestBasic:
else:
r.check_exit_code(100) # CURLE_TOO_LARGE
# http: invalid request headers, GET, issue #16998
@pytest.mark.parametrize("proto", Env.http_protos())
def test_01_16_inv_req_get(self, env: Env, httpd, nghttpx, proto):
curl = CurlClient(env=env)
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo'
r = curl.http_get(url=url, alpn_proto=proto, extra_args=[
'-H', "a: a\x0ab"
])
# on h1, request is sent, h2/h3 reject
if proto == 'http/1.1':
r.check_exit_code(0)
else:
r.check_exit_code(43)
# http: special handling of TE request header
@pytest.mark.parametrize("te_in, te_out", [
pytest.param('trailers', 'trailers', id='trailers'),

View file

@ -116,6 +116,7 @@ TESTS_C = \
lib1955.c lib1956.c lib1957.c lib1958.c lib1959.c lib1960.c \
lib1964.c lib1965.c lib1967.c lib1970.c \
lib1971.c lib1972.c lib1973.c lib1974.c lib1975.c lib1977.c lib1978.c \
lib2017.c \
lib2023.c lib2032.c lib2082.c \
lib2301.c lib2302.c lib2304.c lib2306.c lib2308.c lib2309.c \
lib2402.c lib2404.c lib2405.c \

104
tests/libtest/lib2017.c Normal file
View file

@ -0,0 +1,104 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "first.h"
#include "testtrace.h"
static CURLcode test_lib2017(const char *URL)
{
CURLcode result;
CURL *curl;
int errors = 0;
struct curl_slist crlist = {
CURL_UNCONST("data and a \x0d in there"), NULL
};
struct curl_slist lflist = {
CURL_UNCONST("data and a \x0a in there"), NULL
};
struct curl_slist bothlist[] = {
{ CURL_UNCONST("nothing harmful"), NULL },
{ CURL_UNCONST("both a \x0a and a \x0d embedded"), NULL}
};
/* all slist options should reject these lists */
CURLoption opts[] = {
CURLOPT_PROXYHEADER,
CURLOPT_HTTP200ALIASES,
CURLOPT_POSTQUOTE,
CURLOPT_PREQUOTE,
CURLOPT_QUOTE,
CURLOPT_RESOLVE,
CURLOPT_HTTPHEADER,
CURLOPT_TELNETOPTIONS,
CURLOPT_MAIL_RCPT,
CURLOPT_CONNECT_TO,
CURLOPT_LASTENTRY /* end of list */
};
int i;
bothlist[0].next = &bothlist[1];
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
curl_mfprintf(stderr, "curl_global_init() failed\n");
return TEST_ERR_MAJOR_BAD;
}
curl = curl_easy_init();
if(!curl) {
curl_mfprintf(stderr, "curl_easy_init() failed\n");
curl_global_cleanup();
return TEST_ERR_MAJOR_BAD;
}
easy_setopt(curl, CURLOPT_URL, URL);
for(i = 0; opts[i] != CURLOPT_LASTENTRY; i++) {
CURLcode result1;
CURLcode result2;
CURLcode result3;
CURLoption o = opts[i];
result1 = curl_easy_setopt(curl, o, &lflist);
if(!result1)
curl_mfprintf(stderr, "Option %d unexpectedly OK for LF", (int)o);
result2 = curl_easy_setopt(curl, opts[i], &crlist);
if(!result2)
curl_mfprintf(stderr, "Option %d unexpectedly OK for CR", (int)o);
result3 = curl_easy_setopt(curl, opts[i], bothlist);
if(!result3)
curl_mfprintf(stderr, "Option %d unexpectedly OK for CR+LF", (int)o);
if(!result1 || !result2 || !result3)
errors++;
}
test_cleanup:
curl_easy_cleanup(curl);
curl_global_cleanup();
return errors ? CURLE_BAD_FUNCTION_ARGUMENT : result;
}