]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
urlapi: support UNC paths in file: URLs on Windows
authorSergey Markelov <sergey@solidstatenetworks.com>
Mon, 27 Sep 2021 06:31:52 +0000 (08:31 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 27 Sep 2021 06:32:41 +0000 (08:32 +0200)
- file://host.name/path/file.txt is a valid UNC path
  \\host.name\path\files.txt to a non-local file transformed into URI
  (RFC 8089 Appendix E.3)

- UNC paths on other OSs must be smb: URLs

Closes #7366

lib/urlapi.c
tests/data/test2080
tests/libtest/lib1560.c

index 7f03862cfa219258e0cbc7b47af65bd46c22fdd1..59123ed57ffc2ed3eed68d41efb2e77677e06955 100644 (file)
@@ -760,6 +760,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
 {
   char *path;
   bool path_alloced = FALSE;
+  bool uncpath = FALSE;
   char *hostname;
   char *query = NULL;
   char *fragment = NULL;
@@ -798,7 +799,6 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
     /* path has been allocated large enough to hold this */
     strcpy(path, &url[5]);
 
-    hostname = NULL; /* no host for file: URLs */
     u->scheme = strdup("file");
     if(!u->scheme)
       return CURLUE_OUT_OF_MEMORY;
@@ -820,10 +820,13 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
        *
        *  o the hostname matches "localhost" (case-insensitively), or
        *
-       *  o the hostname is a FQDN that resolves to this machine.
+       *  o the hostname is a FQDN that resolves to this machine, or
+       *
+       *  o it is an UNC String transformed to an URI (Windows only, RFC 8089
+       *    Appendix E.3).
        *
        * For brevity, we only consider URLs with empty, "localhost", or
-       * "127.0.0.1" hostnames as local.
+       * "127.0.0.1" hostnames as local, otherwise as an UNC String.
        *
        * Additionally, there is an exception for URLs with a Windows drive
        * letter in the authority (which was accidentally omitted from RFC 8089
@@ -832,18 +835,43 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
       if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) {
         /* the URL includes a host name, it must match "localhost" or
            "127.0.0.1" to be valid */
-        if(!checkprefix("localhost/", ptr) &&
-           !checkprefix("127.0.0.1/", ptr)) {
+        if(checkprefix("localhost/", ptr) ||
+           checkprefix("127.0.0.1/", ptr)) {
+          ptr += 9; /* now points to the slash after the host */
+        }
+        else {
+#if defined(WIN32)
+          size_t len;
+
+          /* the host name, NetBIOS computer name, can not contain disallowed
+             chars, and the delimiting slash character must be appended to the
+             host name */
+          path = strpbrk(ptr, "/\\:*?\"<>|");
+          if(!path || *path != '/')
+            return CURLUE_MALFORMED_INPUT;
+
+          len = path - ptr;
+          if(len) {
+            memcpy(hostname, ptr, len);
+            hostname[len] = 0;
+            uncpath = TRUE;
+          }
+
+          ptr -= 2; /* now points to the // before the host in UNC */
+#else
           /* Invalid file://hostname/, expected localhost or 127.0.0.1 or
              none */
           return CURLUE_MALFORMED_INPUT;
+#endif
         }
-        ptr += 9; /* now points to the slash after the host */
       }
 
       path = ptr;
     }
 
+    if(!uncpath)
+        hostname = NULL; /* no host for file: URLs by default */
+
 #if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__)
     /* Don't allow Windows drive letters when not in Windows.
      * This catches both "file:/c:" and "file:c:" */
index 9a337031d694d2d224afc63ceaedaf800ed811e0..9c8d538fc896b486e19301f84ffabbeb539bc9f3 100644 (file)
Binary files a/tests/data/test2080 and b/tests/data/test2080 differ
index 3d341ddb44f10354ce1fdc48deb6fd175a71736f..f7529592ccccc1886622f818c435c5fd0f7f6a8e 100644 (file)
@@ -187,6 +187,10 @@ static const struct testcase get_parts_list[] ={
   {"file:///C:\\programs\\foo",
    "file | [11] | [12] | [13] | [14] | [15] | C:\\programs\\foo | [16] | [17]",
    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
+  {"file://host.example.com/Share/path/to/file.txt",
+   "file | [11] | [12] | [13] | host.example.com | [15] | "
+   "//host.example.com/Share/path/to/file.txt | [16] | [17]",
+   CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
 #endif
   {"https://example.com/color/#green?no-red",
    "https | [11] | [12] | [13] | example.com | [15] | /color/ | [16] | "