From 6acae15f2fc824a32f9a376d2a80356577ad4060 Mon Sep 17 00:00:00 2001
From: Kostiantyn Syrykh <cs.this@gmail.com>
Date: Thu, 9 Jan 2025 20:12:22 +0200
Subject: [PATCH] url: parse link-local IPv6 zone identifier

Parse URLs like "http://[fe80::1%25eth0]" (ref. RFC 6874).
---
 src/test/url.c | 3 +++
 src/text/url.c | 7 ++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/test/url.c b/src/test/url.c
index 4c12809afa09..9351ce9ccc3b 100644
--- a/src/test/url.c
+++ b/src/test/url.c
@@ -277,6 +277,8 @@ int main (void)
                    "/", NULL);
     test_url_parse("http://[2001:db8::1]", "http", NULL, NULL, "2001:db8::1",
                    0, NULL, NULL);
+    test_url_parse("http://[fe80::1%25eth0]", "http", NULL, NULL, "fe80::1%eth0",
+                   0, NULL, NULL);
     test_url_parse("http://example.com:", "http", NULL, NULL, "example.com", 0,
                     NULL, NULL);
     test_url_parse("protocol://john:doe@1.2.3.4:567", "protocol", "john", "doe", "1.2.3.4", 567, NULL, NULL);
@@ -321,6 +323,7 @@ int main (void)
     test_url_parse("http://example.com:-18446744073709551615", NULL, NULL, NULL, NULL, 0, NULL, NULL );
     test_url_parse("http://user%/Oath", "http", NULL, NULL, NULL, 0, "/Oath",
                    NULL);
+    test_url_parse("http://[2001::1%25eth0]", NULL, NULL, NULL, NULL, 0, NULL, NULL);
 
     /* URIs to fixup */
     test_url_parse("smb://SERVER:445/SHARE/My file.mp3", "smb", NULL, NULL, "SERVER", 445, NULL, NULL);
diff --git a/src/text/url.c b/src/text/url.c
index 30c61b49552c..250e5be2538b 100644
--- a/src/text/url.c
+++ b/src/text/url.c
@@ -422,6 +422,11 @@ static bool vlc_uri_component_validate(const char *str, const char *extras)
 
 static bool vlc_uri_host_validate(const char *str)
 {
+    // Only link-Local IPv6 addresses can have a zone identifier
+    if (!strncasecmp(str, "fe80:", 5)) {
+        return vlc_uri_component_validate(str, ":%");
+    }
+
     return vlc_uri_component_validate(str, ":");
 }
 
@@ -524,7 +529,7 @@ static int vlc_UrlParseInner(vlc_url_t *restrict url, const char *str)
         if (*cur == '[' && (next = strrchr(cur, ']')) != NULL)
         {   /* Try IPv6 numeral within brackets */
             *(next++) = '\0';
-            url->psz_host = strdup(cur + 1);
+            url->psz_host = vlc_uri_decode_duplicate(cur + 1);
 
             if (*next == ':')
                 next++;
-- 
GitLab