Browse Source

urlapi: CURLU_NO_AUTHORITY allows empty authority/host part

CURLU_NO_AUTHORITY is intended for use with unknown schemes (i.e. not
"file:///") to override cURL's default demand that an authority exists.

Closes #4349
Jens Finkhaeuser 4 years ago
parent
commit
0a4ecbdf1c
5 changed files with 57 additions and 11 deletions
  1. 6 0
      docs/libcurl/curl_url_set.3
  2. 1 0
      docs/libcurl/symbols-in-versions
  3. 2 0
      include/curl/urlapi.h
  4. 25 11
      lib/urlapi.c
  5. 23 0
      tests/libtest/lib1560.c

+ 6 - 0
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
 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
 that scheme will be used, otherwise it picks HTTP. Conflicts with the
 \fICURLU_DEFAULT_SCHEME\fP option which takes precedence if both are set.
 \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
 .SH RETURN VALUE
 Returns a CURLUcode error value, which is CURLUE_OK (0) if everything went
 Returns a CURLUcode error value, which is CURLUE_OK (0) if everything went
 fine.
 fine.

+ 1 - 0
docs/libcurl/symbols-in-versions

@@ -779,6 +779,7 @@ CURLU_DISALLOW_USER             7.62.0
 CURLU_GUESS_SCHEME              7.62.0
 CURLU_GUESS_SCHEME              7.62.0
 CURLU_NON_SUPPORT_SCHEME        7.62.0
 CURLU_NON_SUPPORT_SCHEME        7.62.0
 CURLU_NO_DEFAULT_PORT           7.62.0
 CURLU_NO_DEFAULT_PORT           7.62.0
+CURLU_NO_AUTHORITY              7.67.0
 CURLU_PATH_AS_IS                7.62.0
 CURLU_PATH_AS_IS                7.62.0
 CURLU_URLDECODE                 7.62.0
 CURLU_URLDECODE                 7.62.0
 CURLU_URLENCODE                 7.62.0
 CURLU_URLENCODE                 7.62.0

+ 2 - 0
include/curl/urlapi.h

@@ -77,6 +77,8 @@ typedef enum {
 #define CURLU_URLENCODE (1<<7)          /* URL encode on set */
 #define CURLU_URLENCODE (1<<7)          /* URL encode on set */
 #define CURLU_APPENDQUERY (1<<8)        /* append a form style part */
 #define CURLU_APPENDQUERY (1<<8)        /* append a form style part */
 #define CURLU_GUESS_SCHEME (1<<9)       /* legacy curl-style guessing */
 #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;
 typedef struct Curl_URL CURLU;
 
 

+ 25 - 11
lib/urlapi.c

@@ -784,6 +784,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
 
 
       if(junkscan(schemep))
       if(junkscan(schemep))
         return CURLUE_MALFORMED_INPUT;
         return CURLUE_MALFORMED_INPUT;
+
     }
     }
     else {
     else {
       /* no scheme! */
       /* no scheme! */
@@ -804,11 +805,14 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
       p++;
       p++;
 
 
     len = p - hostp;
     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) {
     if((flags & CURLU_GUESS_SCHEME) && !schemep) {
       /* legacy curl-style guess based on host name */
       /* 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)
     if(result)
       return 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);
     u->host = strdup(hostname);
     if(!u->host)
     if(!u->host)
@@ -1432,9 +1441,14 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
     }
     }
 
 
     if(what == CURLUPART_HOST) {
     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;
+        }
       }
       }
     }
     }
 
 

+ 23 - 0
tests/libtest/lib1560.c

@@ -414,6 +414,18 @@ static struct urltestcase get_url_list[] = {
   {"tp://example.com/path/html",
   {"tp://example.com/path/html",
    "tp://example.com/path/html",
    "tp://example.com/path/html",
    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK},
    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}
   {NULL, NULL, 0, 0, 0}
 };
 };
 
 
@@ -551,6 +563,17 @@ static struct setcase set_parts_list[] = {
    "scheme=ftp,",
    "scheme=ftp,",
    "ftp://example.com:80/",
    "ftp://example.com:80/",
    0, 0, CURLUE_OK, CURLUE_OK},
    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}
   {NULL, NULL, NULL, 0, 0, 0, 0}
 };
 };