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;
* -CONTROLLER PORT]/NQN.2014-08.ORG.NVMEXPRESS.DISCOVERY/<NID>
*/
- /* TODO: unescape? */
-
uri = calloc(1, sizeof(struct nvme_fabrics_uri));
if (!uri)
return NULL;
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) {
/* 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';
}
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);
}
}
"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[] = {