From 573a6ec16b5822ee209d46c490f7e4200036c7dd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 27 Jul 2026 16:05:38 +0200 Subject: [PATCH] urlapi: improved return codes - add CURLUE_BACKSLASH that can be returned when a backslash was used where a forward one probably was intended. - make CURLUE_NO_HOST higher priority than port number errors for URLs without hostname. Like in "http://::1" - shortened some URL parser error strings Extend test 1560 to verify. Reported-by: kit-ty-kate on github Fixes #22337 Closes #22408 --- docs/libcurl/libcurl-errors.md | 5 +++++ docs/libcurl/symbols-in-versions | 1 + include/curl/urlapi.h | 1 + lib/strerror.c | 23 +++++++++++++---------- lib/urlapi.c | 16 +++++++++++----- projects/OS400/curl.inc.in | 2 ++ tests/data/test1538 | 23 ++++++++++++----------- tests/libtest/lib1560.c | 10 +++++++++- 8 files changed, 54 insertions(+), 27 deletions(-) diff --git a/docs/libcurl/libcurl-errors.md b/docs/libcurl/libcurl-errors.md index c658361a79..6f348eb581 100644 --- a/docs/libcurl/libcurl-errors.md +++ b/docs/libcurl/libcurl-errors.md @@ -724,6 +724,11 @@ libcurl lacks IDN support. A value or data field is larger than allowed. +## CURLUE_BACKSLASH (32) + +Found a backslash character where a forward slash was expected. URL separators +are forward slashes (`/`), not backslashes (`\`). + # CURLHcode The header interface returns a *CURLHcode* to indicate when an error has diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index a3d92a2637..65279d6426 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -1113,6 +1113,7 @@ CURLU_PUNY2IDN 8.3.0 CURLU_PUNYCODE 7.88.0 CURLU_URLDECODE 7.62.0 CURLU_URLENCODE 7.62.0 +CURLUE_BACKSLASH 8.22.0 CURLUE_BAD_FILE_URL 7.81.0 CURLUE_BAD_FRAGMENT 7.81.0 CURLUE_BAD_HANDLE 7.62.0 diff --git a/include/curl/urlapi.h b/include/curl/urlapi.h index b1f3a2316b..02553dfebf 100644 --- a/include/curl/urlapi.h +++ b/include/curl/urlapi.h @@ -64,6 +64,7 @@ typedef enum { CURLUE_BAD_USER, /* 29 */ CURLUE_LACKS_IDN, /* 30 */ CURLUE_TOO_LARGE, /* 31 */ + CURLUE_BACKSLASH, /* 32 */ CURLUE_LAST } CURLUcode; diff --git a/lib/strerror.c b/lib/strerror.c index 7b6dc02127..6fc801dc61 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -452,34 +452,34 @@ const char *curl_url_strerror(CURLUcode error) return "An unknown part ID was passed to a URL API function"; case CURLUE_NO_SCHEME: - return "No scheme part in the URL"; + return "No scheme present"; case CURLUE_NO_USER: - return "No user part in the URL"; + return "No user present"; case CURLUE_NO_PASSWORD: - return "No password part in the URL"; + return "No password present"; case CURLUE_NO_OPTIONS: - return "No options part in the URL"; + return "No options present"; case CURLUE_NO_HOST: - return "No host part in the URL"; + return "No host present"; case CURLUE_NO_PORT: - return "No port part in the URL"; + return "No port number present"; case CURLUE_NO_QUERY: - return "No query part in the URL"; + return "No query present"; case CURLUE_NO_FRAGMENT: - return "No fragment part in the URL"; + return "No fragment present"; case CURLUE_NO_ZONEID: - return "No zoneid part in the URL"; + return "No zoneid present"; case CURLUE_BAD_LOGIN: - return "Bad login part"; + return "Bad login"; case CURLUE_BAD_IPV6: return "Bad IPv6 address"; @@ -517,6 +517,9 @@ const char *curl_url_strerror(CURLUcode error) case CURLUE_TOO_LARGE: return "A value or data field is larger than allowed"; + case CURLUE_BACKSLASH: + return "Found a backslash where a forward slash was expected"; + case CURLUE_LAST: break; } diff --git a/lib/urlapi.c b/lib/urlapi.c index cbb16a43c4..35251053ad 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -382,6 +382,7 @@ UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, if(portptr) { curl_off_t port; size_t keep = portptr - hostname; + int rc; /* Browser behavior adaptation. If there is a colon with no digits after, cut off the name there which makes us ignore the colon and use the @@ -393,8 +394,14 @@ UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, portptr++; if(!*portptr) return has_scheme ? CURLUE_OK : CURLUE_BAD_PORT_NUMBER; - - if(curlx_str_number(&portptr, &port, 0xffff) || *portptr) + if(*portptr == '\\') + return CURLUE_BACKSLASH; + rc = curlx_str_number(&portptr, &port, 0xffff); + if(rc) + return CURLUE_BAD_PORT_NUMBER; + else if(*portptr == '\\') + return CURLUE_BACKSLASH; + else if(*portptr) return CURLUE_BAD_PORT_NUMBER; u->portnum = (uint16_t)port; @@ -666,12 +673,11 @@ static CURLUcode parse_authority(struct Curl_URL *u, } uc = parse_port(u, host, has_scheme); - if(uc) - return uc; if(!curlx_dyn_len(host)) + /* this makes no-host errors override port number problems */ uc = CURLUE_NO_HOST; - else + if(!uc) uc = urldecode_host(host); if(uc) return uc; diff --git a/projects/OS400/curl.inc.in b/projects/OS400/curl.inc.in index f97f26e012..d048663f72 100644 --- a/projects/OS400/curl.inc.in +++ b/projects/OS400/curl.inc.in @@ -2340,6 +2340,8 @@ d c 30 d CURLUE_TOO_LARGE... d c 31 + d CURLUE_BACKSLASH... + d c 32 * d CURLUPart s 10i 0 based(######ptr######) Enum d CURLUPART_URL c 0 diff --git a/tests/data/test1538 b/tests/data/test1538 index 51580576ee..bdfe9478ed 100644 --- a/tests/data/test1538 +++ b/tests/data/test1538 @@ -163,20 +163,20 @@ u6: URL decode error, most likely because of rubbish in the input u7: A memory function failed u8: Credentials was passed in the URL when prohibited u9: An unknown part ID was passed to a URL API function -u10: No scheme part in the URL -u11: No user part in the URL -u12: No password part in the URL -u13: No options part in the URL -u14: No host part in the URL -u15: No port part in the URL -u16: No query part in the URL -u17: No fragment part in the URL -u18: No zoneid part in the URL +u10: No scheme present +u11: No user present +u12: No password present +u13: No options present +u14: No host present +u15: No port number present +u16: No query present +u17: No fragment present +u18: No zoneid present u19: Bad file:// URL u20: Bad fragment u21: Bad hostname u22: Bad IPv6 address -u23: Bad login part +u23: Bad login u24: Bad password u25: Bad path u26: Bad query @@ -185,7 +185,8 @@ u28: Unsupported number of slashes following scheme u29: Bad user u30: libcurl lacks IDN support u31: A value or data field is larger than allowed -u32: CURLUcode unknown +u32: Found a backslash where a forward slash was expected +u33: CURLUcode unknown diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 0b20780b7e..1c8aabfd5b 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -153,6 +153,14 @@ struct clearurlcase { }; static const struct testcase get_parts_list[] = { + /* backslash mistakes */ + {"http:\\\\hostname", "", + CURLU_GUESS_SCHEME, 0, CURLUE_BACKSLASH }, + {"http:\\\\hostname:1234", "", + CURLU_GUESS_SCHEME, 0, CURLUE_BACKSLASH }, + {"http://hostname:1111\\path", "", + 0, 0, CURLUE_BACKSLASH }, + /* non-supported URL without hostname */ {"weird:///path", "weird | [11] | [12] | [13] | | [15] | /path | [16] | [17]", @@ -753,7 +761,7 @@ static const struct urltestcase get_url_list[] = { /* malformed unbracketed IPv6 */ {"https://fe80:8080::1/", "", 0, 0, CURLUE_BAD_PORT_NUMBER}, - {"https://::1/", "", 0, 0, CURLUE_BAD_PORT_NUMBER}, + {"https://::1/", "", 0, 0, CURLUE_NO_HOST}, /* Empty host with standard schemes */ {"http:///", "", 0, 0, CURLUE_NO_HOST},