diff options
author | Daniel Stenberg <daniel@haxx.se> | 2023-05-18 00:31:17 +0200 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2023-05-18 00:31:17 +0200 |
commit | a2815af63a3ab1bfcdff4acc4269c0e7d05e96e8 (patch) | |
tree | 9416c77de3424c5bdfa9351b93c3392a6003dffa | |
parent | a9f8fe28481fef7c28d85b4a12a3a35521408eaf (diff) | |
download | curl-a2815af63a3ab1bfcdff4acc4269c0e7d05e96e8.tar.gz |
urlapi: allow numerical parts in the host namebagder/url-digits-hostname
It can only be an IPv4 address if the last part is all digits, otherwise
it is a host name.
Extended test 1560 accordingly.
Reported-by: Pavel Kalyugin
Fixes #11129
-rw-r--r-- | lib/curl_ctype.h | 1 | ||||
-rw-r--r-- | lib/urlapi.c | 57 | ||||
-rw-r--r-- | tests/libtest/lib1560.c | 15 |
3 files changed, 69 insertions, 4 deletions
diff --git a/lib/curl_ctype.h b/lib/curl_ctype.h index 1d1d60c28..f4603ae5f 100644 --- a/lib/curl_ctype.h +++ b/lib/curl_ctype.h @@ -41,6 +41,7 @@ #define ISUPPER(x) (((x) >= 'A') && ((x) <= 'Z')) #define ISLOWER(x) (((x) >= 'a') && ((x) <= 'z')) #define ISDIGIT(x) (((x) >= '0') && ((x) <= '9')) +#define ISOCTAL(x) (((x) >= '0') && ((x) <= '7')) #define ISBLANK(x) (((x) == ' ') || ((x) == '\t')) #define ISSPACE(x) (ISBLANK(x) || (((x) >= 0xa) && ((x) <= 0x0d))) diff --git a/lib/urlapi.c b/lib/urlapi.c index 83e4ceda3..4c2d01b1b 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -34,6 +34,7 @@ #include "inet_ntop.h" #include "strdup.h" #include "idn.h" +#include "curl_memrchr.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -656,6 +657,54 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, #define HOST_IPV4 2 #define HOST_IPV6 3 +static bool finetld(const char *c, size_t len) +{ + const char *ldot; + const char *end = &c[len]; + int i; + + for(i = 0; i < 2; i++) { + ldot = memrchr(c, '.', len); + if(!ldot) + ldot = c; + else { + ldot++; + if(!*ldot) { + /* this is a trailing dot, we rather want the one before */ + len--; + end = &c[len]; + /* now loop */ + continue; + } + } + break; + } + + /* this is the last part of the host name, if not only digits it is a + host name, otherwise a bad IPv4 address */ + if(*ldot == '0') { + /* hex or octal */ + + ldot++; + if(Curl_raw_tolower(*ldot) == 'x') + /* hex */ + ldot++; + else { + /* octal */ + while(*ldot && ISOCTAL(*ldot)) + ldot++; + } + while(*ldot && ISXDIGIT(*ldot)) + ldot++; + } + else { + /* decimal */ + while(*ldot && ISDIGIT(*ldot)) + ldot++; + } + return (ldot != end) ? TRUE : FALSE; +} + static int ipv4_normalize(struct dynbuf *host) { bool done = FALSE; @@ -667,12 +716,15 @@ static int ipv4_normalize(struct dynbuf *host) if(*c == '[') return HOST_IPV6; + if(finetld(c, Curl_dyn_len(host))) + return HOST_NAME; + while(!done) { char *endp; unsigned long l; if(!ISDIGIT(*c)) /* most importantly this doesn't allow a leading plus or minus */ - return n ? HOST_BAD : HOST_NAME; + return HOST_BAD; l = strtoul(c, &endp, 0); parts[n] = l; @@ -793,6 +845,9 @@ static CURLUcode parse_authority(struct Curl_URL *u, if(result) goto out; + if(!Curl_dyn_len(host)) + return CURLUE_NO_HOST; + switch(ipv4_normalize(host)) { case HOST_IPV4: break; diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 8d7b4e966..3120a63ab 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -474,6 +474,12 @@ static const struct testcase get_parts_list[] ={ }; static const struct urltestcase get_url_list[] = { + {"https://0x7f.1", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://1.2.3.256.com", "https://1.2.3.256.com/", 0, 0, CURLUE_OK}, + {"https://10.com", "https://10.com/", 0, 0, CURLUE_OK}, + {"https://1.2.com", "https://1.2.com/", 0, 0, CURLUE_OK}, + {"https://1.2.3.com", "https://1.2.3.com/", 0, 0, CURLUE_OK}, + {"https://1.2.com.99", "https://1.2.com.99/", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://[fe80::0000:20c:29ff:fe9c:409b]:80/moo", "https://[fe80::20c:29ff:fe9c:409b]:80/moo", 0, 0, CURLUE_OK}, @@ -522,19 +528,22 @@ static const struct urltestcase get_url_list[] = { /* IPv4 trickeries */ {"https://16843009", "https://1.1.1.1/", 0, 0, CURLUE_OK}, - {"https://0x7f.1", "https://127.0.0.1/", 0, 0, CURLUE_OK}, {"https://0177.1", "https://127.0.0.1/", 0, 0, CURLUE_OK}, {"https://0111.02.0x3", "https://73.2.0.3/", 0, 0, CURLUE_OK}, + {"https://0111.02.0x3.", "https://73.2.0.3./", 0, 0, CURLUE_BAD_HOSTNAME}, + {"https://0111.02.030", "https://73.2.0.24/", 0, 0, CURLUE_OK}, + {"https://0111.02.030.", "https://73.2.0.24./", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://0xff.0xff.0377.255", "https://255.255.255.255/", 0, 0, CURLUE_OK}, {"https://1.0xffffff", "https://1.255.255.255/", 0, 0, CURLUE_OK}, /* IPv4 numerical overflows or syntax errors will not normalize */ - {"https://a127.0.0.1", "https://a127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://a127.0.0.1", "https://a127.0.0.1/", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://\xff.127.0.0.1", "https://%FF.127.0.0.1/", 0, CURLU_URLENCODE, - CURLUE_OK}, + CURLUE_BAD_HOSTNAME}, {"https://127.-0.0.1", "https://127.-0.0.1/", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://127.0. 1", "https://127.0.0.1/", 0, 0, CURLUE_MALFORMED_INPUT}, {"https://1.0x1000000", "https://1.0x1000000/", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://1.2.3.256", "https://1.2.3.256/", 0, 0, CURLUE_BAD_HOSTNAME}, + {"https://1.2.3.256.", "https://1.2.3.256./", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://1.2.3.4.5", "https://1.2.3.4.5/", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://1.2.0x100.3", "https://1.2.0x100.3/", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://4294967296", "https://4294967296/", 0, 0, CURLUE_BAD_HOSTNAME}, |