diff --git a/docs/DEPRECATE.md b/docs/DEPRECATE.md index 7e140cd5d6..ac7c4d340f 100644 --- a/docs/DEPRECATE.md +++ b/docs/DEPRECATE.md @@ -52,6 +52,25 @@ We remove support for building curl with the gskit TLS library in August 2023. - build breakages in this code take weeks or more to get detected - fixing gskit code is mostly done "flying blind" +## space-separated `NOPROXY` patterns + +When specifying patterns/domain names for curl that should *not* go through a +proxy, the curl tool features the `--noproxy` command line option and the +library supports the `NO_PROXY` environment variable and the `CURLOPT_NOPROXY` +libcurl option. + +They all set the same list of patterns. This list is documented to be a set of +**comma-separated** names, but can also be provided separated with just +space. The ability to just use spaces for this has never been documented but +some users may still have come to rely on this. + +Several other tools and utilities also parse the `NO_PROXY` environment +variable but do not consider a space to be a valid separator. Using spaces for +separator is probably less portable and might cause more friction than commas +do. Users should use commas for this for greater portability. + +curl will remove the support for space-separated names in July 2024. + ## past removals - Pipelining diff --git a/lib/noproxy.c b/lib/noproxy.c index 6c0c486f81..f1c1ed2c63 100644 --- a/lib/noproxy.c +++ b/lib/noproxy.c @@ -119,8 +119,10 @@ enum nametype { * Checks if the host is in the noproxy list. returns TRUE if it matches and * therefore the proxy should NOT be used. ****************************************************************/ -bool Curl_check_noproxy(const char *name, const char *no_proxy) +bool Curl_check_noproxy(const char *name, const char *no_proxy, + bool *spacesep) { + *spacesep = FALSE; /* * If we don't have a hostname at all, like for example with a FILE * transfer, we have nothing to interrogate the noproxy list with. @@ -244,6 +246,15 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy) if(match) return TRUE; } /* if(tokenlen) */ + /* pass blanks after pattern */ + while(ISBLANK(*p)) + p++; + /* if not a comma! */ + if(*p && (*p != ',')) { + *spacesep = TRUE; + continue; + } + /* pass any number of commas */ while(*p == ',') p++; } /* while(*p) */ diff --git a/lib/noproxy.h b/lib/noproxy.h index b6a1a556ae..a3a6807722 100644 --- a/lib/noproxy.h +++ b/lib/noproxy.h @@ -37,7 +37,8 @@ UNITTEST bool Curl_cidr6_match(const char *ipv6, unsigned int bits); #endif -bool Curl_check_noproxy(const char *name, const char *no_proxy); +bool Curl_check_noproxy(const char *name, const char *no_proxy, + bool *spacesep); #endif diff --git a/lib/url.c b/lib/url.c index e531a821ce..f90427f9b7 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2401,6 +2401,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, char *socksproxy = NULL; char *no_proxy = NULL; CURLcode result = CURLE_OK; + bool spacesep = FALSE; /************************************************************* * Extract the user and password from the authentication string @@ -2447,7 +2448,8 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, } if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? - data->set.str[STRING_NOPROXY] : no_proxy)) { + data->set.str[STRING_NOPROXY] : no_proxy, + &spacesep)) { Curl_safefree(proxy); Curl_safefree(socksproxy); } @@ -2456,6 +2458,8 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, /* if the host is not in the noproxy list, detect proxy. */ proxy = detect_proxy(data, conn); #endif /* CURL_DISABLE_HTTP */ + if(spacesep) + infof(data, "space-separated NOPROXY patterns are deprecated"); Curl_safefree(no_proxy); diff --git a/tests/unit/unit1614.c b/tests/unit/unit1614.c index cc685b9ce8..0818ea7ed5 100644 --- a/tests/unit/unit1614.c +++ b/tests/unit/unit1614.c @@ -46,6 +46,7 @@ struct noproxy { const char *a; const char *n; bool match; + bool space; /* space separated */ }; UNITTEST_START @@ -77,50 +78,52 @@ UNITTEST_START { NULL, NULL, 0, FALSE} /* end marker */ }; struct noproxy list[]= { - { "www.example.com", "localhost,.example.com,.example.de", TRUE}, - { "www.example.com.", "localhost,.example.com,.example.de", TRUE}, - { "example.com", "localhost,.example.com,.example.de", TRUE}, - { "example.com.", "localhost,.example.com,.example.de", TRUE}, - { "www.example.com", "localhost,.example.com.,.example.de", TRUE}, - { "www.example.com", "localhost,www.example.com.,.example.de", TRUE}, - { "example.com", "localhost,example.com,.example.de", TRUE}, - { "example.com.", "localhost,example.com,.example.de", TRUE}, - { "nexample.com", "localhost,example.com,.example.de", FALSE}, - { "www.example.com", "localhost,example.com,.example.de", TRUE}, - { "127.0.0.1", "127.0.0.1,localhost", TRUE}, - { "127.0.0.1", "127.0.0.1,localhost,", TRUE}, - { "127.0.0.1", "127.0.0.1/8,localhost,", TRUE}, - { "127.0.0.1", "127.0.0.1/28,localhost,", TRUE}, - { "127.0.0.1", "127.0.0.1/31,localhost,", TRUE}, - { "127.0.0.1", "localhost,127.0.0.1", TRUE}, + { "www.example.com", "localhost .example.com .example.de", TRUE, TRUE}, + { "www.example.com", "localhost,.example.com,.example.de", TRUE, FALSE}, + { "www.example.com.", "localhost,.example.com,.example.de", TRUE, FALSE}, + { "example.com", "localhost,.example.com,.example.de", TRUE, FALSE}, + { "example.com.", "localhost,.example.com,.example.de", TRUE, FALSE}, + { "www.example.com", "localhost,.example.com.,.example.de", TRUE, FALSE}, + { "www.example.com", "localhost,www.example.com.,.example.de", + TRUE, FALSE}, + { "example.com", "localhost,example.com,.example.de", TRUE, FALSE}, + { "example.com.", "localhost,example.com,.example.de", TRUE, FALSE}, + { "nexample.com", "localhost,example.com,.example.de", FALSE, FALSE}, + { "www.example.com", "localhost,example.com,.example.de", TRUE, FALSE}, + { "127.0.0.1", "127.0.0.1,localhost", TRUE, FALSE}, + { "127.0.0.1", "127.0.0.1,localhost,", TRUE, FALSE}, + { "127.0.0.1", "127.0.0.1/8,localhost,", TRUE, FALSE}, + { "127.0.0.1", "127.0.0.1/28,localhost,", TRUE, FALSE}, + { "127.0.0.1", "127.0.0.1/31,localhost,", TRUE, FALSE}, + { "127.0.0.1", "localhost,127.0.0.1", TRUE, FALSE}, { "127.0.0.1", "localhost,127.0.0.1.127.0.0.1.127.0.0.1.127.0.0.1." "127.0.0.1.127.0.0.1.127.0.0.1.127.0.0.1.127.0.0.1.127.0.0.1.127." - "0.0.1.127.0.0.1.127.0.0." /* 128 bytes "address" */, FALSE}, + "0.0.1.127.0.0.1.127.0.0." /* 128 bytes "address" */, FALSE, FALSE}, { "127.0.0.1", "localhost,127.0.0.1.127.0.0.1.127.0.0.1.127.0.0.1." "127.0.0.1.127.0.0.1.127.0.0.1.127.0.0.1.127.0.0.1.127.0.0.1.127." - "0.0.1.127.0.0.1.127.0.0" /* 127 bytes "address" */, FALSE}, - { "localhost", "localhost,127.0.0.1", TRUE}, - { "localhost", "127.0.0.1,localhost", TRUE}, - { "foobar", "barfoo", FALSE}, - { "foobar", "foobar", TRUE}, - { "192.168.0.1", "foobar", FALSE}, - { "192.168.0.1", "192.168.0.0/16", TRUE}, - { "192.168.0.1", "192.168.0.0/24", TRUE}, - { "192.168.0.1", "192.168.0.0/32", FALSE}, - { "192.168.0.1", "192.168.0.0", FALSE}, - { "192.168.1.1", "192.168.0.0/24", FALSE}, - { "192.168.1.1", "foo, bar, 192.168.0.0/24", FALSE}, - { "192.168.1.1", "foo, bar, 192.168.0.0/16", TRUE}, - { "[::1]", "foo, bar, 192.168.0.0/16", FALSE}, - { "[::1]", "foo, bar, ::1/64", TRUE}, - { "bar", "foo, bar, ::1/64", TRUE}, - { "BAr", "foo, bar, ::1/64", TRUE}, - { "BAr", "foo,,,,, bar, ::1/64", TRUE}, - { "www.example.com", "foo, .example.com", TRUE}, - { "www.example.com", "www2.example.com, .example.net", FALSE}, - { "example.com", ".example.com, .example.net", TRUE}, - { "nonexample.com", ".example.com, .example.net", FALSE}, - { NULL, NULL, FALSE} + "0.0.1.127.0.0.1.127.0.0" /* 127 bytes "address" */, FALSE, FALSE}, + { "localhost", "localhost,127.0.0.1", TRUE, FALSE}, + { "localhost", "127.0.0.1,localhost", TRUE, FALSE}, + { "foobar", "barfoo", FALSE, FALSE}, + { "foobar", "foobar", TRUE, FALSE}, + { "192.168.0.1", "foobar", FALSE, FALSE}, + { "192.168.0.1", "192.168.0.0/16", TRUE, FALSE}, + { "192.168.0.1", "192.168.0.0/24", TRUE, FALSE}, + { "192.168.0.1", "192.168.0.0/32", FALSE, FALSE}, + { "192.168.0.1", "192.168.0.0", FALSE, FALSE}, + { "192.168.1.1", "192.168.0.0/24", FALSE, FALSE}, + { "192.168.1.1", "foo, bar, 192.168.0.0/24", FALSE, FALSE}, + { "192.168.1.1", "foo, bar, 192.168.0.0/16", TRUE, FALSE}, + { "[::1]", "foo, bar, 192.168.0.0/16", FALSE, FALSE}, + { "[::1]", "foo, bar, ::1/64", TRUE, FALSE}, + { "bar", "foo, bar, ::1/64", TRUE, FALSE}, + { "BAr", "foo, bar, ::1/64", TRUE, FALSE}, + { "BAr", "foo,,,,, bar, ::1/64", TRUE, FALSE}, + { "www.example.com", "foo, .example.com", TRUE, FALSE}, + { "www.example.com", "www2.example.com, .example.net", FALSE, FALSE}, + { "example.com", ".example.com, .example.net", TRUE, FALSE}, + { "nonexample.com", ".example.com, .example.net", FALSE, FALSE}, + { NULL, NULL, FALSE, FALSE} }; for(i = 0; list4[i].a; i++) { bool match = Curl_cidr4_match(list4[i].a, list4[i].n, list4[i].bits); @@ -141,13 +144,19 @@ UNITTEST_START } } for(i = 0; list[i].a; i++) { - bool match = Curl_check_noproxy(list[i].a, list[i].n); + bool spacesep = FALSE; + bool match = Curl_check_noproxy(list[i].a, list[i].n, &spacesep); if(match != list[i].match) { fprintf(stderr, "%s in %s should %smatch\n", list[i].a, list[i].n, list[i].match ? "": "not "); err++; } + if(spacesep != list[i].space) { + fprintf(stderr, "%s is claimed to be %sspace separated\n", + list[i].n, list[i].space?"":"NOT "); + err++; + } } return err; }