urlapi: normalize legacy numeric IPv4 hosts set via CURLUPART_HOST
No binary (build failed)
—
(log yok)
| @@ -131,6 +131,10 @@ you also set the CURLU_URLENCODE flag. | |
| Unless *CURLU_NO_AUTHORITY* is set, a blank hostname is not allowed to set. | |
| + | A numeric IPv4 address (in dotted decimal or a legacy form such as a single |
| + | decimal, octal or hexadecimal number) is normalized to dotted decimal, the |
| + | same way it is when libcurl parses a full URL. |
| + | |
| ## CURLUPART_ZONEID | |
| If the hostname is a numeric IPv6 address, this field can also be set. | |
| @@ -2056,10 +2056,24 @@ static CURLUcode url_sethost(CURLU *u, struct dynbuf *encp, | |
| CURLcode result = Curl_urldecode(newp, n, &decoded, &dlen, REJECT_CTRL); | |
| if(result || hostname_check6(u, decoded, dlen)) | |
| bad = TRUE; | |
| + | else { |
| + | struct dynbuf dbuf; |
| + | curlx_dyn_init(&dbuf, CURL_MAX_INPUT_LENGTH); |
| + | if(curlx_dyn_addn(&dbuf, decoded, dlen)) |
| + | bad = TRUE; |
| + | else if(ipv4_normalize(&dbuf) == HOST_IPV4) { |
| + | curlx_dyn_reset(encp); |
| + | if(curlx_dyn_addn(encp, curlx_dyn_ptr(&dbuf), curlx_dyn_len(&dbuf))) |
| + | bad = TRUE; |
| + | } |
| + | curlx_dyn_free(&dbuf); |
| + | } |
| curlx_free(decoded); | |
| } | |
| else if(hostname_check6(u, newp, n)) | |
| bad = TRUE; | |
| + | else if(ipv4_normalize(encp) == HOST_ERROR) |
| + | bad = TRUE; |
| if(bad) { | |
| curlx_dyn_free(encp); | |
| return CURLUE_BAD_HOSTNAME; | |
| @@ -2143,7 +2157,9 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, | |
| const char *newp = NULL; | |
| struct dynbuf enc; | |
| CURLUcode status; | |
| - | curlx_dyn_init(&enc, (nalloc * 3) + 1 + leadingslash); |
| + | curlx_dyn_init(&enc, (what == CURLUPART_HOST) ? |
| + | CURLMAX((nalloc * 3) + 1 + leadingslash, 16) : |
| + | (nalloc * 3) + 1 + leadingslash); |
| if(leadingslash && (part[0] != '/')) { | |
| CURLcode result = curlx_dyn_addn(&enc, "/", 1); | |
| @@ -1297,7 +1297,7 @@ static const struct setcase set_parts_list[] = { | |
| {"https://example.com/", | |
| "host=0xff,", /* '++' there is no automatic URL decode when setting this | |
| part */ | |
| - | "https://0xff/", |
| + | "https://0.0.0.255/", |
| 0, /* get */ | |
| 0, /* set */ | |
| CURLUE_OK, CURLUE_OK}, | |
| @@ -438,5 +438,73 @@ loop_end: | |
| abort_if(fails, "parse_hostname_login tests failed"); | |
| } | |
| + | /* Test that CURLUPART_HOST normalizes legacy numeric IPv4 forms the |
| + | same way the full URL parser does */ |
| + | { |
| + | int fails = 0; |
| + | unsigned int i; |
| + | struct host_test { |
| + | const char *host; |
| + | const char *out; |
| + | }; |
| + | static const struct host_test tests[] = { |
| + | { "2130706433", "127.0.0.1" }, |
| + | { "0177.1", "127.0.0.1" }, |
| + | { "0x7f.1", "127.0.0.1" }, |
| + | { "0xc0a80001", "192.168.0.1" }, |
| + | { "127.0.0.1", "127.0.0.1" }, |
| + | { "example.com", "example.com" }, |
| + | { "0", "0.0.0.0" }, |
| + | { "1", "0.0.0.1" }, |
| + | { "7", "0.0.0.7" }, |
| + | { "9", "0.0.0.9" }, |
| + | { "00", "0.0.0.0" }, |
| + | }; |
| + | |
| + | for(i = 0; i < CURL_ARRAYSIZE(tests); i++) { |
| + | CURLU *viaurl = curl_url(); |
| + | CURLU *viahost = curl_url(); |
| + | CURLUcode uc1, uc2; |
| + | char *urlbuf = curl_maprintf("http://%s/", tests[i].host); |
| + | char *gothost1 = NULL; |
| + | char *gothost2 = NULL; |
| + | |
| + | if(!viaurl || !viahost || !urlbuf) { |
| + | curl_mfprintf(stderr, "%u: failed to allocate memory\n", i); |
| + | fails++; |
| + | goto host_loop_end; |
| + | } |
| + | |
| + | uc1 = curl_url_set(viaurl, CURLUPART_URL, urlbuf, 0); |
| + | if(!uc1) |
| + | uc1 = curl_url_get(viaurl, CURLUPART_HOST, &gothost1, 0); |
| + | |
| + | curl_url_set(viahost, CURLUPART_SCHEME, "http", 0); |
| + | uc2 = curl_url_set(viahost, CURLUPART_HOST, tests[i].host, 0); |
| + | if(!uc2) |
| + | uc2 = curl_url_get(viahost, CURLUPART_HOST, &gothost2, 0); |
| + | |
| + | if(uc1 || uc2 || |
| + | strcmp(gothost1, tests[i].out) || |
| + | strcmp(gothost2, tests[i].out)) { |
| + | curl_mfprintf(stderr, "host '%s' mismatch: full-URL -> '%s' (%d), " |
| + | "CURLUPART_HOST -> '%s' (%d), expected '%s'\n", |
| + | tests[i].host, |
| + | uc1 ? "error" : gothost1, (int)uc1, |
| + | uc2 ? "error" : gothost2, (int)uc2, |
| + | tests[i].out); |
| + | fails++; |
| + | } |
| + | |
| + | host_loop_end: |
| + | curl_free(gothost1); |
| + | curl_free(gothost2); |
| + | curl_free(urlbuf); |
| + | curl_url_cleanup(viaurl); |
| + | curl_url_cleanup(viahost); |
| + | } |
| + | abort_if(fails, "CURLUPART_HOST ipv4 normalization parity tests failed"); |
| + | } |
| + | |
| UNITTEST_END_SIMPLE | |
| } | |