diff --git a/docs/libcurl/curl_url_set.3 b/docs/libcurl/curl_url_set.3 index 43d741220..e2e2c1887 100644 --- a/docs/libcurl/curl_url_set.3 +++ b/docs/libcurl/curl_url_set.3 @@ -111,6 +111,12 @@ instead "guesses" which scheme that was intended based on the host name. If the outermost sub-domain name matches DICT, FTP, IMAP, LDAP, POP3 or SMTP then that scheme will be used, otherwise it picks HTTP. Conflicts with the \fICURLU_DEFAULT_SCHEME\fP option which takes precedence if both are set. +.IP CURLU_NO_AUTHORITY +If set, skips authority checks. The RFC allows individual schemes to omit the +host part (normally the only mandatory part of the authority), but libcurl +cannot know whether this is permitted for custom schemes. Specifying the flag +permits empty authority sections, similar to how file scheme is handled. + .SH RETURN VALUE Returns a CURLUcode error value, which is CURLUE_OK (0) if everything went fine. diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 9daad949f..ff0dfb238 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -779,6 +779,7 @@ CURLU_DISALLOW_USER 7.62.0 CURLU_GUESS_SCHEME 7.62.0 CURLU_NON_SUPPORT_SCHEME 7.62.0 CURLU_NO_DEFAULT_PORT 7.62.0 +CURLU_NO_AUTHORITY 7.67.0 CURLU_PATH_AS_IS 7.62.0 CURLU_URLDECODE 7.62.0 CURLU_URLENCODE 7.62.0 diff --git a/include/curl/urlapi.h b/include/curl/urlapi.h index 0f2f152f1..f2d06770d 100644 --- a/include/curl/urlapi.h +++ b/include/curl/urlapi.h @@ -77,6 +77,8 @@ typedef enum { #define CURLU_URLENCODE (1<<7) /* URL encode on set */ #define CURLU_APPENDQUERY (1<<8) /* append a form style part */ #define CURLU_GUESS_SCHEME (1<<9) /* legacy curl-style guessing */ +#define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the + scheme is unknown. */ typedef struct Curl_URL CURLU; diff --git a/lib/urlapi.c b/lib/urlapi.c index a0ee331da..6e414cf18 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -784,6 +784,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) if(junkscan(schemep)) return CURLUE_MALFORMED_INPUT; + } else { /* no scheme! */ @@ -804,11 +805,14 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) p++; len = p - hostp; - if(!len) - return CURLUE_MALFORMED_INPUT; - - memcpy(hostname, hostp, len); - hostname[len] = 0; + if(len) { + memcpy(hostname, hostp, len); + hostname[len] = 0; + } + else { + if(!(flags & CURLU_NO_AUTHORITY)) + return CURLUE_MALFORMED_INPUT; + } if((flags & CURLU_GUESS_SCHEME) && !schemep) { /* legacy curl-style guess based on host name */ @@ -889,9 +893,14 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) if(result) return result; - result = hostname_check(u, hostname); - if(result) - return result; + if(0 == strlen(hostname) && (flags & CURLU_NO_AUTHORITY)) { + /* Skip hostname check, it's allowed to be empty. */ + } + else { + result = hostname_check(u, hostname); + if(result) + return result; + } u->host = strdup(hostname); if(!u->host) @@ -1432,9 +1441,14 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, } if(what == CURLUPART_HOST) { - if(hostname_check(u, (char *)newp)) { - free((char *)newp); - return CURLUE_MALFORMED_INPUT; + if(0 == strlen(newp) && (flags & CURLU_NO_AUTHORITY)) { + /* Skip hostname check, it's allowed to be empty. */ + } + else { + if(hostname_check(u, (char *)newp)) { + free((char *)newp); + return CURLUE_MALFORMED_INPUT; + } } } diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 85884474e..d161423e3 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -414,6 +414,18 @@ static struct urltestcase get_url_list[] = { {"tp://example.com/path/html", "tp://example.com/path/html", CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK}, + {"custom-scheme://host?expected=test-good", + "custom-scheme://host/?expected=test-good", + CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK}, + {"custom-scheme://?expected=test-bad", + "", + CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_MALFORMED_INPUT}, + {"custom-scheme://?expected=test-new-good", + "custom-scheme:///?expected=test-new-good", + CURLU_NON_SUPPORT_SCHEME | CURLU_NO_AUTHORITY, 0, CURLUE_OK}, + {"custom-scheme://host?expected=test-still-good", + "custom-scheme://host/?expected=test-still-good", + CURLU_NON_SUPPORT_SCHEME | CURLU_NO_AUTHORITY, 0, CURLUE_OK}, {NULL, NULL, 0, 0, 0} }; @@ -551,6 +563,17 @@ static struct setcase set_parts_list[] = { "scheme=ftp,", "ftp://example.com:80/", 0, 0, CURLUE_OK, CURLUE_OK}, + {"custom-scheme://host", + "host=\"\",", + "custom-scheme://host/", + CURLU_NON_SUPPORT_SCHEME, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, + CURLUE_MALFORMED_INPUT}, + {"custom-scheme://host", + "host=\"\",", + "custom-scheme:///", + CURLU_NON_SUPPORT_SCHEME, CURLU_NON_SUPPORT_SCHEME | CURLU_NO_AUTHORITY, + CURLUE_OK, CURLUE_OK}, + {NULL, NULL, NULL, 0, 0, 0, 0} };