From 913529411514d864ee2f377379d872dcab2c8317 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 15 May 2026 10:14:36 +0200 Subject: [PATCH] urlapi: deny hostnames with more than one trailing dot Or consisting of just a single dot. Such names cannot be resolved with DNS. While they *can* still be resolved with /etc/hosts or --resolve tricks, they easily cause internal problems because their trailing dots. Let's not allow them anymore. Closes #21622 --- lib/urlapi.c | 7 +++++++ tests/http/test_17_ssl_use.py | 8 +++----- tests/libtest/lib1560.c | 20 +++++++++++++------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index 24b8bc244a..a3111f9db6 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -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; } diff --git a/tests/http/test_17_ssl_use.py b/tests/http/test_17_ssl_use.py index b2339dab51..4a4dd0bf7e 100644 --- a/tests/http/test_17_ssl_use.py +++ b/tests/http/test_17_ssl_use.py @@ -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()) diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 6c3fff9bc9..e833c304e3 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -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},