Sfoglia il codice sorgente

Merge pull request #5181 from BaronGreenback/Fix_IPHostIP6Parsing

Claus Vium 4 anni fa
parent
commit
f8c9c37c29

+ 51 - 46
MediaBrowser.Common/Net/IPHost.cs

@@ -128,62 +128,63 @@ namespace MediaBrowser.Common.Net
         /// <returns><c>true</c> if the parsing is successful, <c>false</c> if not.</returns>
         public static bool TryParse(string host, out IPHost hostObj)
         {
-            if (!string.IsNullOrEmpty(host))
+            if (string.IsNullOrWhiteSpace(host))
             {
-                // See if it's an IPv6 with port address e.g. [::1]:120.
-                int i = host.IndexOf("]:", StringComparison.OrdinalIgnoreCase);
-                if (i != -1)
-                {
-                    return TryParse(host.Remove(i - 1).TrimStart(' ', '['), out hostObj);
-                }
-                else
-                {
-                    // See if it's an IPv6 in [] with no port.
-                    i = host.IndexOf(']', StringComparison.OrdinalIgnoreCase);
-                    if (i != -1)
-                    {
-                        return TryParse(host.Remove(i - 1).TrimStart(' ', '['), out hostObj);
-                    }
+                hostObj = IPHost.None;
+                return false;
+            }
 
-                    // Is it a host or IPv4 with port?
-                    string[] hosts = host.Split(':');
+            // See if it's an IPv6 with port address e.g. [::1] or [::1]:120.
+            int i = host.IndexOf("]", StringComparison.OrdinalIgnoreCase);
+            if (i != -1)
+            {
+                return TryParse(host.Remove(i - 1).TrimStart(' ', '['), out hostObj);
+            }
 
-                    if (hosts.Length > 2)
-                    {
-                        hostObj = new IPHost(string.Empty, IPAddress.None);
-                        return false;
-                    }
+            if (IPNetAddress.TryParse(host, out var netAddress))
+            {
+                // Host name is an ip address, so fake resolve.
+                hostObj = new IPHost(host, netAddress.Address);
+                return true;
+            }
 
-                    // Remove port from IPv4 if it exists.
-                    host = hosts[0];
+            // Is it a host, IPv4/6 with/out port?
+            string[] hosts = host.Split(':');
 
-                    if (string.Equals("localhost", host, StringComparison.OrdinalIgnoreCase))
-                    {
-                        hostObj = new IPHost(host, new IPAddress(Ipv4Loopback));
-                        return true;
-                    }
+            if (hosts.Length <= 2)
+            {
+                // This is either a hostname: port, or an IP4:port.
+                host = hosts[0];
 
-                    if (IPNetAddress.TryParse(host, out IPNetAddress netIP))
-                    {
-                        // Host name is an ip address, so fake resolve.
-                        hostObj = new IPHost(host, netIP.Address);
-                        return true;
-                    }
+                if (string.Equals("localhost", host, StringComparison.OrdinalIgnoreCase))
+                {
+                    hostObj = new IPHost(host);
+                    return true;
                 }
 
-                // Only thing left is to see if it's a host string.
-                if (!string.IsNullOrEmpty(host))
+                if (IPAddress.TryParse(host, out var netIP))
                 {
-                    // Use regular expression as CheckHostName isn't RFC5892 compliant.
-                    // Modified from gSkinner's expression at https://stackoverflow.com/questions/11809631/fully-qualified-domain-name-validation
-                    Regex re = new Regex(@"^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){0,127}(?![0-9]*$)[a-z0-9-]+\.?)$", RegexOptions.IgnoreCase | RegexOptions.Multiline);
-                    if (re.Match(host).Success)
-                    {
-                        hostObj = new IPHost(host);
-                        return true;
-                    }
+                    // Host name is an ip address, so fake resolve.
+                    hostObj = new IPHost(host, netIP);
+                    return true;
                 }
             }
+            else
+            {
+                // Invalid host name, as it cannot contain :
+                hostObj = new IPHost(string.Empty, IPAddress.None);
+                return false;
+            }
+
+            // Use regular expression as CheckHostName isn't RFC5892 compliant.
+            // Modified from gSkinner's expression at https://stackoverflow.com/questions/11809631/fully-qualified-domain-name-validation
+            string pattern = @"(?im)^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){0,127}(?![0-9]*$)[a-z0-9-]+\.?)$";
+
+            if (Regex.IsMatch(host, pattern))
+            {
+                hostObj = new IPHost(host);
+                return true;
+            }
 
             hostObj = IPHost.None;
             return false;
@@ -344,10 +345,14 @@ namespace MediaBrowser.Common.Net
                     {
                         output += "Any Address,";
                     }
-                    else
+                    else if (i.AddressFamily == AddressFamily.InterNetwork)
                     {
                         output += $"{i}/32,";
                     }
+                    else
+                    {
+                        output += $"{i}/128,";
+                    }
                 }
 
                 output = output[0..^1];

+ 30 - 35
tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs

@@ -13,34 +13,6 @@ namespace Jellyfin.Networking.Tests
 {
     public class NetworkParseTests
     {
-        /// <summary>
-        /// Tries to identify the string and return an object of that class.
-        /// </summary>
-        /// <param name="addr">String to parse.</param>
-        /// <param name="result">IPObject to return.</param>
-        /// <returns>True if the value parsed successfully.</returns>
-        private static bool TryParse(string addr, out IPObject result)
-        {
-            if (!string.IsNullOrEmpty(addr))
-            {
-                // Is it an IP address
-                if (IPNetAddress.TryParse(addr, out IPNetAddress nw))
-                {
-                    result = nw;
-                    return true;
-                }
-
-                if (IPHost.TryParse(addr, out IPHost h))
-                {
-                    result = h;
-                    return true;
-                }
-            }
-
-            result = IPNetAddress.None;
-            return false;
-        }
-
         private static IConfigurationManager GetMockConfig(NetworkConfiguration conf)
         {
             var configManager = new Mock<IConfigurationManager>
@@ -123,11 +95,33 @@ namespace Jellyfin.Networking.Tests
         [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]:124")]
         [InlineData("fe80::7add:12ff:febb:c67b%16")]
         [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")]
+        [InlineData("fe80::7add:12ff:febb:c67b%16:123")]
+        [InlineData("[fe80::7add:12ff:febb:c67b%16]")]
+        [InlineData("192.168.1.2/255.255.255.0")]
+        [InlineData("192.168.1.2/24")]
+        public void ValidHostStrings(string address)
+        {
+            Assert.True(IPHost.TryParse(address, out _));
+        }
+
+        /// <summary>
+        /// Checks IP address formats.
+        /// </summary>
+        /// <param name="address"></param>
+        [Theory]
+        [InlineData("127.0.0.1")]
+        [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")]
+        [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")]
+        [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]")]
+        [InlineData("fe80::7add:12ff:febb:c67b%16")]
+        [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")]
+        [InlineData("fe80::7add:12ff:febb:c67b%16:123")]
+        [InlineData("[fe80::7add:12ff:febb:c67b%16]")]
         [InlineData("192.168.1.2/255.255.255.0")]
         [InlineData("192.168.1.2/24")]
         public void ValidIPStrings(string address)
         {
-            Assert.True(TryParse(address, out _));
+            Assert.True(IPNetAddress.TryParse(address, out _));
         }
 
 
@@ -143,7 +137,8 @@ namespace Jellyfin.Networking.Tests
         [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")]
         public void InvalidAddressString(string address)
         {
-            Assert.False(TryParse(address, out _));
+            Assert.False(IPNetAddress.TryParse(address, out _));
+            Assert.False(IPHost.TryParse(address, out _));
         }
 
 
@@ -177,11 +172,11 @@ namespace Jellyfin.Networking.Tests
             "[]")]
         [InlineData(
             "192.158.1.2/16, localhost, fd23:184f:2029:0:3139:7386:67d7:d517,    !10.10.10.10",
-            "[192.158.1.2/16,127.0.0.1/32,fd23:184f:2029:0:3139:7386:67d7:d517/128]",
+            "[192.158.1.2/16,[127.0.0.1/32,::1/128],fd23:184f:2029:0:3139:7386:67d7:d517/128]",
             "[192.158.1.2/16,127.0.0.1/32]",
             "[10.10.10.10/32]",
             "[10.10.10.10/32]",
-            "[192.158.0.0/16,127.0.0.1/32,fd23:184f:2029:0:3139:7386:67d7:d517/128]")]
+            "[192.158.0.0/16,127.0.0.1/32,::1/128,fd23:184f:2029:0:3139:7386:67d7:d517/128]")]
         [InlineData("192.158.1.2/255.255.0.0,192.169.1.2/8",
             "[192.158.1.2/16,192.169.1.2/8]",
             "[192.158.1.2/16,192.169.1.2/8]",
@@ -338,8 +333,8 @@ namespace Jellyfin.Networking.Tests
 
         public void TestSubnetContains(string network, string ip)
         {
-            Assert.True(TryParse(network, out IPObject? networkObj));
-            Assert.True(TryParse(ip, out IPObject? ipObj));
+            Assert.True(IPNetAddress.TryParse(network, out var networkObj));
+            Assert.True(IPNetAddress.TryParse(ip, out var ipObj));
             Assert.True(networkObj.Contains(ipObj));
         }
 
@@ -473,7 +468,7 @@ namespace Jellyfin.Networking.Tests
         // User on internal network, no binding specified - so result is the 1st internal.
         [InlineData("192.168.1.1", "192.168.1.0/24", "", false, "0.0.0.0=http://helloworld.com", "eth16")]
 
-        // User on external network, internal binding only - so asumption is a proxy forward, return external override.
+        // User on external network, internal binding only - so assumption is a proxy forward, return external override.
         [InlineData("jellyfin.org", "192.168.1.0/24", "eth16", false, "0.0.0.0=http://helloworld.com", "http://helloworld.com")]
 
         // User on external network, no binding - so result is the 1st external which is overriden.