From b5c0fe20e370ea2e5791fce4cedb34a71bc784e5 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 11 Aug 2022 11:32:22 +0200 Subject: [PATCH] hostip: resolve *.localhost to 127.0.0.1/::1 Following the footsteps of other clients like Firefox/Chrome. RFC 6761 says clients SHOULD do this. Add test 389 to verify. Reported-by: TheKnarf on github Fixes #9192 Closes #9296 --- lib/hostip.c | 36 +++++++++++++++++--------- tests/data/Makefile.inc | 3 +-- tests/data/test389 | 57 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 tests/data/test389 diff --git a/lib/hostip.c b/lib/hostip.c index 1ced9d2e9c..99779044c5 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -463,12 +463,12 @@ Curl_cache_addr(struct Curl_easy *data, } #ifdef ENABLE_IPV6 -/* return a static IPv6 resolve for 'localhost' */ -static struct Curl_addrinfo *get_localhost6(int port) +/* return a static IPv6 ::1 for the name */ +static struct Curl_addrinfo *get_localhost6(int port, const char *name) { struct Curl_addrinfo *ca; const size_t ss_size = sizeof(struct sockaddr_in6); - const size_t hostlen = strlen("localhost"); + const size_t hostlen = strlen(name); struct sockaddr_in6 sa6; unsigned char ipv6[16]; unsigned short port16 = (unsigned short)(port & 0xffff); @@ -493,19 +493,19 @@ static struct Curl_addrinfo *get_localhost6(int port) ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); memcpy(ca->ai_addr, &sa6, ss_size); ca->ai_canonname = (char *)ca->ai_addr + ss_size; - strcpy(ca->ai_canonname, "localhost"); + strcpy(ca->ai_canonname, name); return ca; } #else -#define get_localhost6(x) NULL +#define get_localhost6(x,y) NULL #endif -/* return a static IPv4 resolve for 'localhost' */ -static struct Curl_addrinfo *get_localhost(int port) +/* return a static IPv4 127.0.0.1 for the given name */ +static struct Curl_addrinfo *get_localhost(int port, const char *name) { struct Curl_addrinfo *ca; const size_t ss_size = sizeof(struct sockaddr_in); - const size_t hostlen = strlen("localhost"); + const size_t hostlen = strlen(name); struct sockaddr_in sa; unsigned int ipv4; unsigned short port16 = (unsigned short)(port & 0xffff); @@ -529,8 +529,8 @@ static struct Curl_addrinfo *get_localhost(int port) ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); memcpy(ca->ai_addr, &sa, ss_size); ca->ai_canonname = (char *)ca->ai_addr + ss_size; - strcpy(ca->ai_canonname, "localhost"); - ca->ai_next = get_localhost6(port); + strcpy(ca->ai_canonname, name); + ca->ai_next = get_localhost6(port, name); return ca; } @@ -583,6 +583,17 @@ bool Curl_host_is_ipnum(const char *hostname) return FALSE; } + +/* return TRUE if 'part' is a case insentive tail of 'full' */ +static bool tailmatch(const char *full, const char *part) +{ + size_t plen = strlen(part); + size_t flen = strlen(full); + if(plen > flen) + return FALSE; + return strncasecompare(part, &full[flen - plen], plen); +} + /* * Curl_resolv() is the main name resolve function within libcurl. It resolves * a name and returns a pointer to the entry in the 'entry' argument (if one @@ -718,8 +729,9 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data)) return CURLRESOLV_ERROR; - if(strcasecompare(hostname, "localhost")) - addr = get_localhost(port); + if(strcasecompare(hostname, "localhost") || + tailmatch(hostname, ".localhost")) + addr = get_localhost(port, hostname); #ifndef CURL_DISABLE_DOH else if(allowDOH && data->set.doh && !ipnum) addr = Curl_doh(data, hostname, port, &respwait); diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index b7bc2d41df..cfa0ea3841 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -64,8 +64,7 @@ test343 test344 test345 test346 test347 test348 test349 test350 test351 \ test352 test353 test354 test355 test356 test357 test358 test359 test360 \ test361 test362 test363 test364 test365 test366 test367 test368 test369 \ test370 test371 test372 test373 test374 test375 test376 test378 test379 \ -test380 test381 test383 test384 test385 test386 test387 test388 \ -\ +test380 test381 test383 test384 test385 test386 test387 test388 test389 \ test390 test391 test392 test393 test394 test395 test396 test397 test398 \ \ test400 test401 test402 test403 test404 test405 test406 test407 test408 \ diff --git a/tests/data/test389 b/tests/data/test389 new file mode 100644 index 0000000000..f805bc2770 --- /dev/null +++ b/tests/data/test389 @@ -0,0 +1,57 @@ + + + +HTTP +.localhost + + + +# +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + +# +# Client-side + + +http + + +*.localhost is a local host + + +http://curlmachine.localhost:%HTTPPORT/%TESTNUMBER + +# Ensure that we're running on localhost + +perl -e "print 'Test requires default test server host' if ( '%HOSTIP' ne '127.0.0.1' );" + + + +# +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: curlmachine.localhost:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +