← Commit list

9135294115

Daniel Stenberg
Daniel Stenberg
2026-05-15
SUCCESS Duration: 240s

Subject

urlapi: deny hostnames with more than one trailing dot

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

lib/urlapi.c+7 -0
@@ -475,6 +475,13 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
if(hlen != len)
/* hostname with bad content */
return CURLUE_BAD_HOSTNAME;
+ else if((hlen >= 2) &&
+ (hostname[hlen - 1] == '.') && (hostname[hlen - 2] == '.'))
+ /* more than one trailing dot is not allowed */
+ return CURLUE_BAD_HOSTNAME;
+ else if((hlen == 1) && (hostname[0] == '.'))
+ /* just a single dot is not allowed */
+ return CURLUE_BAD_HOSTNAME;
}
return CURLUE_OK;
}
tests/http/test_17_ssl_use.py+3 -5
@@ -127,7 +127,7 @@ class TestSSLUse:
# the SNI the server received is without trailing dot
assert r.json['SSL_TLS_SNI'] == env.domain1, f'{r.json}'
- # use hostname with double trailing dot, verify handshake
+ # use hostname with double trailing dot
@pytest.mark.parametrize("proto", Env.http_protos())
def test_17_04_double_dot(self, env: Env, proto, httpd, nghttpx):
curl = CurlClient(env=env)
@@ -142,10 +142,8 @@ class TestSSLUse:
if proto != 'h3': # we proxy h3
assert r.json['SSL_TLS_SNI'] == env.domain1, f'{r.json}'
assert False, f'should not have succeeded: {r.json}'
- # 7 - Rustls rejects a servername with .. during setup
- # 35 - LibreSSL rejects setting an SNI name with trailing dot
- # 60 - peer name matching failed against certificate
- assert r.exit_code in [7, 35, 60], f'{r}'
+ # 3 - not allowed in the URL
+ assert r.exit_code in [3], f'{r}'
# use ip address for connect
@pytest.mark.parametrize("proto", Env.http_protos())
tests/libtest/lib1560.c+13 -7
@@ -196,20 +196,19 @@ static const struct testcase get_parts_list[] = {
"http://host:00080/",
"http | [11] | [12] | [13] | host | 80 | / | [16] | [17]",
0, 0, CURLUE_OK },
- { /* Single dot host - technically valid in some contexts but often
- rejected */
+ { /* Single dot host - not ok */
"http://./",
- "http | [11] | [12] | [13] | . | [15] | / | [16] | [17]",
- 0, 0, CURLUE_OK },
+ "",
+ 0, 0, CURLUE_BAD_HOSTNAME },
{ /* Host starting with a dash (RFC 1123 technically allows it, but many
parsers don't) */
"http://-atest/",
"http | [11] | [12] | [13] | -atest | [15] | / | [16] | [17]",
0, 0, CURLUE_OK },
- { /* Multiple trailing dots, not okay in DNS but works in /etc/hosts */
+ { /* Multiple trailing dots is not okey */
"http://example.com../",
- "http | [11] | [12] | [13] | example.com.. | [15] | / | [16] | [17]",
- 0, 0, CURLUE_OK },
+ "",
+ 0, 0, CURLUE_BAD_HOSTNAME },
{ /* Empty IPv6 Zone ID */
"http://[fe80::1%]/",
"", 0, 0, CURLUE_BAD_IPV6 },
@@ -626,6 +625,13 @@ static const struct testcase get_parts_list[] = {
};
static const struct urltestcase get_url_list[] = {
+ {"http://hej./", "http://hej./", 0, 0, CURLUE_OK},
+ {"http://hej../", "", 0, 0, CURLUE_BAD_HOSTNAME},
+ {"http://hej.../", "", 0, 0, CURLUE_BAD_HOSTNAME},
+ {"http://hej..../index.html", "", 0, 0, CURLUE_BAD_HOSTNAME},
+ {"http://.", "", 0, 0, CURLUE_BAD_HOSTNAME},
+ {"http://..", "", 0, 0, CURLUE_BAD_HOSTNAME},
+ {"http://...", "", 0, 0, CURLUE_BAD_HOSTNAME},
{"018.0.0.0", "http://018.0.0.0/", CURLU_GUESS_SCHEME, 0, CURLUE_OK},
{"08", "http://08/", CURLU_GUESS_SCHEME, 0, CURLUE_OK},
{"0", "http://0.0.0.0/", CURLU_GUESS_SCHEME, 0, CURLUE_OK},