fabrics: Unescape URI elements
authorTomas Bzatek <tbzatek@redhat.com>
Mon, 13 May 2024 15:38:25 +0000 (17:38 +0200)
committerDaniel Wagner <wagi@monom.org>
Wed, 15 May 2024 17:55:21 +0000 (19:55 +0200)
This adds support for unescaping percent-encoded URI parts.

Signed-off-by: Tomas Bzatek <tbzatek@redhat.com>
src/nvme/fabrics.c
test/uriparser.c

index 324a7321d803c8c1cf17c7bd26c763fa2611c658..e5921f8b48a3b14e600882610e5e9a54265e2c0e 100644 (file)
@@ -1704,12 +1704,41 @@ int nvmf_register_ctrl(nvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result)
        return nvmf_dim(c, tas, NVMF_TRTYPE_TCP, nvme_get_adrfam(c), "", NULL, result);
 }
 
+#define IS_XDIGIT(c) ((c >= '0' && c <= '9') || \
+                     (c >= 'A' && c <= 'F') || \
+                     (c >= 'a' && c <= 'f'))
+#define XDIGIT_VAL(c) ((c >= '0' && c <= '9') ? c - '0' : ( \
+                      (c >= 'A' && c <= 'F') ? c - 'A' + 10 : c - 'a' + 10))
+
+/* returns newly allocated string */
+static char *unescape_uri(const char *str, int len)
+{
+       char *dst;
+       int l;
+       int i, j;
+
+       l = len > 0 ? len : strlen(str);
+       dst = malloc(l + 1);
+       for (i = 0, j = 0; i < l; i++, j++) {
+               if (str[i] == '%' && i + 2 < l &&
+                   IS_XDIGIT(str[i + 1]) && IS_XDIGIT(str[i + 2])) {
+                       dst[j] = (XDIGIT_VAL(str[i + 1]) << 4) +
+                                 XDIGIT_VAL(str[i + 2]);
+                       i += 2;
+               } else
+                       dst[j] = str[i];
+       }
+       dst[j] = '\0';
+       return dst;
+}
+
 struct nvme_fabrics_uri *nvme_parse_uri(const char *str)
 {
        struct nvme_fabrics_uri *uri;
        _cleanup_free_ char *scheme = NULL;
        _cleanup_free_ char *authority = NULL;
        _cleanup_free_ char *path = NULL;
+       _cleanup_free_ char *h = NULL;
        const char *host;
        int i;
 
@@ -1726,8 +1755,6 @@ struct nvme_fabrics_uri *nvme_parse_uri(const char *str)
         *  -CONTROLLER PORT]/NQN.2014-08.ORG.NVMEXPRESS.DISCOVERY/<NID>
         */
 
-       /* TODO: unescape? */
-
        uri = calloc(1, sizeof(struct nvme_fabrics_uri));
        if (!uri)
                return NULL;
@@ -1750,20 +1777,22 @@ struct nvme_fabrics_uri *nvme_parse_uri(const char *str)
        host = strrchr(authority, '@');
        if (host) {
                host++;
-               uri->userinfo = strndup(authority, host - authority);
+               uri->userinfo = unescape_uri(authority, host - authority);
        } else
                host = authority;
 
        /* try matching IPv6 address first */
        if (sscanf(host, "[%m[^]]]:%d",
-                  &uri->host, &uri->port) < 1)
+                  &uri->host, &uri->port) < 1) {
                /* treat it as IPv4/hostname */
                if (sscanf(host, "%m[^:]:%d",
-                          &uri->host, &uri->port) < 1) {
+                          &h, &uri->port) < 1) {
                        nvme_free_uri(uri);
                        errno = EINVAL;
                        return NULL;
                }
+               uri->host = unescape_uri(h, 0);
+       }
 
        /* split path into elements */
        if (path) {
@@ -1772,13 +1801,13 @@ struct nvme_fabrics_uri *nvme_parse_uri(const char *str)
                /* separate the fragment */
                e = strrchr(path, '#');
                if (e) {
-                       uri->fragment = strdup(e + 1);
+                       uri->fragment = unescape_uri(e + 1, 0);
                        *e = '\0';
                }
                /* separate the query string */
                e = strrchr(path, '?');
                if (e) {
-                       uri->query = strdup(e + 1);
+                       uri->query = unescape_uri(e + 1, 0);
                        *e = '\0';
                }
 
@@ -1791,11 +1820,11 @@ struct nvme_fabrics_uri *nvme_parse_uri(const char *str)
                i = 0;
                elem = strtok_r(path, "/", &e);
                if (elem)
-                       uri->path_segments[i++] = strdup(elem);
+                       uri->path_segments[i++] = unescape_uri(elem, 0);
                while (elem && strlen(elem)) {
                        elem = strtok_r(NULL, "/", &e);
                        if (elem)
-                               uri->path_segments[i++] = strdup(elem);
+                               uri->path_segments[i++] = unescape_uri(elem, 0);
                }
        }
 
index cf26bfd28b065f507b2bff066d78978ed3c037eb..09b2a732b9d6ffa6f5754c61ac661c63240018d3 100644 (file)
@@ -119,6 +119,19 @@ static struct test_data test_data[] = {
          "nvme", "aa:bb::cc", .proto = "rdma", .port = 12345,
          .user = "u[aa:bb::cc]", .path = { "p1", "x" },
          .query = "q=val", .frag = "fr" },
+       { "nvme://ex%5Cmp%3Ae",  "nvme", "ex\\mp:e" },
+       { "nvme://ex%5Cmp%3Ae.com/", "nvme", "ex\\mp:e.com" },
+       { "nvme://u%24er@ex%5Cmp%3Ae.com/", "nvme", "ex\\mp:e.com",
+         .user = "u$er" },
+       { "nvme+tcp://ex%5Cmp%3Ae.com:1234",
+         "nvme", "ex\\mp:e.com", .proto = "tcp", .port = 1234 },
+       { "nvme+tcp://ex%5Cmp%3Ae.com:1234/p1/ex%3Camp%3Ele/p3",
+         "nvme", "ex\\mp:e.com", .proto = "tcp", .port = 1234,
+         .path = { "p1", "ex<amp>le", "p3", NULL } },
+       { "nvme+tcp://ex%5Cmp%3Ae.com:1234/p1/%3C%3E/p3?q%5E%24ry#fr%26gm%23nt",
+         "nvme", "ex\\mp:e.com", .proto = "tcp", .port = 1234,
+         .path = { "p1", "<>", "p3", NULL }, .query = "q^$ry",
+         .frag = "fr&gm#nt" },
 };
 
 const char *test_data_bad[] = {