Selaa lähdekoodia

Migrate NetworkManager and Tests to native .NET IP objects

Shadowghost 2 vuotta sitten
vanhempi
sitoutus
066db8ac7f

+ 11 - 8
Emby.Dlna/Main/DlnaEntryPoint.cs

@@ -3,15 +3,16 @@
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;
+using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.Linq;
 using System.Linq;
+using System.Net;
 using System.Net.Http;
 using System.Net.Http;
 using System.Net.Sockets;
 using System.Net.Sockets;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Emby.Dlna.PlayTo;
 using Emby.Dlna.PlayTo;
 using Emby.Dlna.Ssdp;
 using Emby.Dlna.Ssdp;
 using Jellyfin.Networking.Configuration;
 using Jellyfin.Networking.Configuration;
-using Jellyfin.Networking.Manager;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
@@ -285,9 +286,11 @@ namespace Emby.Dlna.Main
             var udn = CreateUuid(_appHost.SystemId);
             var udn = CreateUuid(_appHost.SystemId);
             var descriptorUri = "/dlna/" + udn + "/description.xml";
             var descriptorUri = "/dlna/" + udn + "/description.xml";
 
 
-            var bindAddresses = NetworkManager.CreateCollection(
-                _networkManager.GetInternalBindAddresses()
-                .Where(i => i.AddressFamily == AddressFamily.InterNetwork || (i.AddressFamily == AddressFamily.InterNetworkV6 && i.Address.ScopeId != 0)));
+            var bindAddresses = _networkManager
+                .GetInternalBindAddresses()
+                .Where(i => i.Address.AddressFamily == AddressFamily.InterNetwork
+                    || (i.AddressFamily == AddressFamily.InterNetworkV6 && i.Address.ScopeId != 0))
+                .ToList();
 
 
             if (bindAddresses.Count == 0)
             if (bindAddresses.Count == 0)
             {
             {
@@ -295,7 +298,7 @@ namespace Emby.Dlna.Main
                 bindAddresses = _networkManager.GetLoopbacks();
                 bindAddresses = _networkManager.GetLoopbacks();
             }
             }
 
 
-            foreach (IPNetAddress address in bindAddresses)
+            foreach (var address in bindAddresses)
             {
             {
                 if (address.AddressFamily == AddressFamily.InterNetworkV6)
                 if (address.AddressFamily == AddressFamily.InterNetworkV6)
                 {
                 {
@@ -304,7 +307,7 @@ namespace Emby.Dlna.Main
                 }
                 }
 
 
                 // Limit to LAN addresses only
                 // Limit to LAN addresses only
-                if (!_networkManager.IsInLocalNetwork(address))
+                if (!_networkManager.IsInLocalNetwork(address.Address))
                 {
                 {
                     continue;
                     continue;
                 }
                 }
@@ -313,14 +316,14 @@ namespace Emby.Dlna.Main
 
 
                 _logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, address);
                 _logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, address);
 
 
-                var uri = new UriBuilder(_appHost.GetApiUrlForLocalAccess(address, false) + descriptorUri);
+                var uri = new UriBuilder(_appHost.GetApiUrlForLocalAccess(address.Address, false) + descriptorUri);
 
 
                 var device = new SsdpRootDevice
                 var device = new SsdpRootDevice
                 {
                 {
                     CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info.
                     CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info.
                     Location = uri.Uri, // Must point to the URL that serves your devices UPnP description document.
                     Location = uri.Uri, // Must point to the URL that serves your devices UPnP description document.
                     Address = address.Address,
                     Address = address.Address,
-                    PrefixLength = address.PrefixLength,
+                    PrefixLength = NetworkExtensions.MaskToCidr(address.Subnet.Prefix),
                     FriendlyName = "Jellyfin",
                     FriendlyName = "Jellyfin",
                     Manufacturer = "Jellyfin",
                     Manufacturer = "Jellyfin",
                     ModelName = "Jellyfin Server",
                     ModelName = "Jellyfin Server",

+ 2 - 2
Emby.Server.Implementations/ApplicationHost.cs

@@ -1114,10 +1114,10 @@ namespace Emby.Server.Implementations
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
-        public string GetApiUrlForLocalAccess(IPObject hostname = null, bool allowHttps = true)
+        public string GetApiUrlForLocalAccess(IPAddress ipAddress = null, bool allowHttps = true)
         {
         {
             // With an empty source, the port will be null
             // With an empty source, the port will be null
-            var smart = NetManager.GetBindInterface(hostname ?? IPHost.None, out _);
+            var smart = NetManager.GetBindInterface(ipAddress, out _);
             var scheme = !allowHttps ? Uri.UriSchemeHttp : null;
             var scheme = !allowHttps ? Uri.UriSchemeHttp : null;
             int? port = !allowHttps ? HttpPort : null;
             int? port = !allowHttps ? HttpPort : null;
             return GetLocalApiUrl(smart, scheme, port);
             return GetLocalApiUrl(smart, scheme, port);

+ 2 - 8
Jellyfin.Networking/Configuration/NetworkConfiguration.cs

@@ -113,12 +113,12 @@ namespace Jellyfin.Networking.Configuration
         public string UDPPortRange { get; set; } = string.Empty;
         public string UDPPortRange { get; set; } = string.Empty;
 
 
         /// <summary>
         /// <summary>
-        /// Gets or sets a value indicating whether gets or sets IPV6 capability.
+        /// Gets or sets a value indicating whether IPv6 is enabled or not.
         /// </summary>
         /// </summary>
         public bool EnableIPV6 { get; set; }
         public bool EnableIPV6 { get; set; }
 
 
         /// <summary>
         /// <summary>
-        /// Gets or sets a value indicating whether gets or sets IPV4 capability.
+        /// Gets or sets a value indicating whether IPv6 is enabled or not.
         /// </summary>
         /// </summary>
         public bool EnableIPV4 { get; set; } = true;
         public bool EnableIPV4 { get; set; } = true;
 
 
@@ -165,12 +165,6 @@ namespace Jellyfin.Networking.Configuration
         /// </summary>
         /// </summary>
         public bool EnableMultiSocketBinding { get; } = true;
         public bool EnableMultiSocketBinding { get; } = true;
 
 
-        /// <summary>
-        /// Gets or sets a value indicating whether all IPv6 interfaces should be treated as on the internal network.
-        /// Depending on the address range implemented ULA ranges might not be used.
-        /// </summary>
-        public bool TrustAllIP6Interfaces { get; set; }
-
         /// <summary>
         /// <summary>
         /// Gets or sets the ports that HDHomerun uses.
         /// Gets or sets the ports that HDHomerun uses.
         /// </summary>
         /// </summary>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 630 - 798
Jellyfin.Networking/Manager/NetworkManager.cs


+ 4 - 4
Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs

@@ -344,13 +344,13 @@ namespace Jellyfin.Server.Extensions
         {
         {
             for (var i = 0; i < allowedProxies.Length; i++)
             for (var i = 0; i < allowedProxies.Length; i++)
             {
             {
-                if (IPNetAddress.TryParse(allowedProxies[i], out var addr))
+                if (IPAddress.TryParse(allowedProxies[i], out var addr))
                 {
                 {
-                    AddIpAddress(config, options, addr.Address, addr.PrefixLength);
+                    AddIpAddress(config, options, addr, addr.AddressFamily == AddressFamily.InterNetwork ? 32 : 128);
                 }
                 }
-                else if (IPHost.TryParse(allowedProxies[i], out var host))
+                else if (NetworkExtensions.TryParseHost(allowedProxies[i], out var host))
                 {
                 {
-                    foreach (var address in host.GetAddresses())
+                    foreach (var address in host)
                     {
                     {
                         AddIpAddress(config, options, address, address.AddressFamily == AddressFamily.InterNetwork ? 32 : 128);
                         AddIpAddress(config, options, address, address.AddressFamily == AddressFamily.InterNetwork ? 32 : 128);
                     }
                     }

+ 1 - 3
Jellyfin.Server/Migrations/PreStartupRoutines/CreateNetworkConfiguration.cs

@@ -114,9 +114,7 @@ public class CreateNetworkConfiguration : IMigrationRoutine
 
 
         public bool IgnoreVirtualInterfaces { get; set; } = true;
         public bool IgnoreVirtualInterfaces { get; set; } = true;
 
 
-        public string VirtualInterfaceNames { get; set; } = "vEthernet*";
-
-        public bool TrustAllIP6Interfaces { get; set; }
+        public string VirtualInterfaceNames { get; set; } = "veth*";
 
 
         public string[] PublishedServerUriBySubnet { get; set; } = Array.Empty<string>();
         public string[] PublishedServerUriBySubnet { get; set; } = Array.Empty<string>();
 
 

+ 3 - 3
Jellyfin.Server/Program.cs

@@ -300,7 +300,7 @@ namespace Jellyfin.Server
                     var addresses = appHost.NetManager.GetAllBindInterfaces();
                     var addresses = appHost.NetManager.GetAllBindInterfaces();
 
 
                     bool flagged = false;
                     bool flagged = false;
-                    foreach (IPObject netAdd in addresses)
+                    foreach (IPData netAdd in addresses)
                     {
                     {
                         _logger.LogInformation("Kestrel listening on {Address}", netAdd.Address == IPAddress.IPv6Any ? "All Addresses" : netAdd);
                         _logger.LogInformation("Kestrel listening on {Address}", netAdd.Address == IPAddress.IPv6Any ? "All Addresses" : netAdd);
                         options.Listen(netAdd.Address, appHost.HttpPort);
                         options.Listen(netAdd.Address, appHost.HttpPort);
@@ -689,10 +689,10 @@ namespace Jellyfin.Server
 
 
             if (!string.IsNullOrEmpty(socketPerms))
             if (!string.IsNullOrEmpty(socketPerms))
             {
             {
-                #pragma warning disable SA1300 // Entrypoint is case sensitive.
+#pragma warning disable SA1300 // Entrypoint is case sensitive.
                 [DllImport("libc")]
                 [DllImport("libc")]
                 static extern int chmod(string pathname, int mode);
                 static extern int chmod(string pathname, int mode);
-                #pragma warning restore SA1300
+#pragma warning restore SA1300
 
 
                 var exitCode = chmod(socketPath, Convert.ToInt32(socketPerms, 8));
                 var exitCode = chmod(socketPath, Convert.ToInt32(socketPerms, 8));
 
 

+ 15 - 113
MediaBrowser.Common/Net/INetworkManager.cs

@@ -18,44 +18,29 @@ namespace MediaBrowser.Common.Net
         event EventHandler NetworkChanged;
         event EventHandler NetworkChanged;
 
 
         /// <summary>
         /// <summary>
-        /// Gets the published server urls list.
+        /// Gets a value indicating whether iP6 is enabled.
         /// </summary>
         /// </summary>
-        Dictionary<IPNetAddress, string> PublishedServerUrls { get; }
+        bool IsIpv6Enabled { get; }
 
 
         /// <summary>
         /// <summary>
-        /// Gets a value indicating whether is all IPv6 interfaces are trusted as internal.
+        /// Gets a value indicating whether iP4 is enabled.
         /// </summary>
         /// </summary>
-        bool TrustAllIP6Interfaces { get; }
-
-        /// <summary>
-        /// Gets the remote address filter.
-        /// </summary>
-        Collection<IPObject> RemoteAddressFilter { get; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether iP6 is enabled.
-        /// </summary>
-        bool IsIP6Enabled { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether iP4 is enabled.
-        /// </summary>
-        bool IsIP4Enabled { get; set; }
+        bool IsIpv4Enabled { get; }
 
 
         /// <summary>
         /// <summary>
         /// Calculates the list of interfaces to use for Kestrel.
         /// Calculates the list of interfaces to use for Kestrel.
         /// </summary>
         /// </summary>
-        /// <returns>A Collection{IPObject} object containing all the interfaces to bind.
+        /// <returns>A List{IPData} object containing all the interfaces to bind.
         /// If all the interfaces are specified, and none are excluded, it returns zero items
         /// If all the interfaces are specified, and none are excluded, it returns zero items
         /// to represent any address.</returns>
         /// to represent any address.</returns>
         /// <param name="individualInterfaces">When false, return <see cref="IPAddress.Any"/> or <see cref="IPAddress.IPv6Any"/> for all interfaces.</param>
         /// <param name="individualInterfaces">When false, return <see cref="IPAddress.Any"/> or <see cref="IPAddress.IPv6Any"/> for all interfaces.</param>
-        Collection<IPObject> GetAllBindInterfaces(bool individualInterfaces = false);
+        List<IPData> GetAllBindInterfaces(bool individualInterfaces = false);
 
 
         /// <summary>
         /// <summary>
         /// Returns a collection containing the loopback interfaces.
         /// Returns a collection containing the loopback interfaces.
         /// </summary>
         /// </summary>
-        /// <returns>Collection{IPObject}.</returns>
-        Collection<IPObject> GetLoopbacks();
+        /// <returns>List{IPData}.</returns>
+        List<IPData> GetLoopbacks();
 
 
         /// <summary>
         /// <summary>
         /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo)
         /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo)
@@ -86,22 +71,12 @@ namespace MediaBrowser.Common.Net
         /// <param name="source">Source of the request.</param>
         /// <param name="source">Source of the request.</param>
         /// <param name="port">Optional port returned, if it's part of an override.</param>
         /// <param name="port">Optional port returned, if it's part of an override.</param>
         /// <returns>IP Address to use, or loopback address if all else fails.</returns>
         /// <returns>IP Address to use, or loopback address if all else fails.</returns>
-        string GetBindInterface(IPObject source, out int? port);
-
-        /// <summary>
-        /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo)
-        /// If no bind addresses are specified, an internal interface address is selected.
-        /// (See <see cref="GetBindInterface(IPObject, out int?)"/>.
-        /// </summary>
-        /// <param name="source">Source of the request.</param>
-        /// <param name="port">Optional port returned, if it's part of an override.</param>
-        /// <returns>IP Address to use, or loopback address if all else fails.</returns>
         string GetBindInterface(HttpRequest source, out int? port);
         string GetBindInterface(HttpRequest source, out int? port);
 
 
         /// <summary>
         /// <summary>
         /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo)
         /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo)
         /// If no bind addresses are specified, an internal interface address is selected.
         /// If no bind addresses are specified, an internal interface address is selected.
-        /// (See <see cref="GetBindInterface(IPObject, out int?)"/>.
+        /// (See <see cref="GetBindInterface(IPAddress, out int?)"/>.
         /// </summary>
         /// </summary>
         /// <param name="source">IP address of the request.</param>
         /// <param name="source">IP address of the request.</param>
         /// <param name="port">Optional port returned, if it's part of an override.</param>
         /// <param name="port">Optional port returned, if it's part of an override.</param>
@@ -111,48 +86,19 @@ namespace MediaBrowser.Common.Net
         /// <summary>
         /// <summary>
         /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo)
         /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo)
         /// If no bind addresses are specified, an internal interface address is selected.
         /// If no bind addresses are specified, an internal interface address is selected.
-        /// (See <see cref="GetBindInterface(IPObject, out int?)"/>.
+        /// (See <see cref="GetBindInterface(IPAddress, out int?)"/>.
         /// </summary>
         /// </summary>
         /// <param name="source">Source of the request.</param>
         /// <param name="source">Source of the request.</param>
         /// <param name="port">Optional port returned, if it's part of an override.</param>
         /// <param name="port">Optional port returned, if it's part of an override.</param>
         /// <returns>IP Address to use, or loopback address if all else fails.</returns>
         /// <returns>IP Address to use, or loopback address if all else fails.</returns>
         string GetBindInterface(string source, out int? port);
         string GetBindInterface(string source, out int? port);
 
 
-        /// <summary>
-        /// Checks to see if the ip address is specifically excluded in LocalNetworkAddresses.
-        /// </summary>
-        /// <param name="address">IP address to check.</param>
-        /// <returns>True if it is.</returns>
-        bool IsExcludedInterface(IPAddress address);
-
         /// <summary>
         /// <summary>
         /// Get a list of all the MAC addresses associated with active interfaces.
         /// Get a list of all the MAC addresses associated with active interfaces.
         /// </summary>
         /// </summary>
         /// <returns>List of MAC addresses.</returns>
         /// <returns>List of MAC addresses.</returns>
         IReadOnlyCollection<PhysicalAddress> GetMacAddresses();
         IReadOnlyCollection<PhysicalAddress> GetMacAddresses();
 
 
-        /// <summary>
-        /// Checks to see if the IP Address provided matches an interface that has a gateway.
-        /// </summary>
-        /// <param name="addressObj">IP to check. Can be an IPAddress or an IPObject.</param>
-        /// <returns>Result of the check.</returns>
-        bool IsGatewayInterface(IPObject? addressObj);
-
-        /// <summary>
-        /// Checks to see if the IP Address provided matches an interface that has a gateway.
-        /// </summary>
-        /// <param name="addressObj">IP to check. Can be an IPAddress or an IPObject.</param>
-        /// <returns>Result of the check.</returns>
-        bool IsGatewayInterface(IPAddress? addressObj);
-
-        /// <summary>
-        /// Returns true if the address is a private address.
-        /// The configuration option TrustIP6Interfaces overrides this functions behaviour.
-        /// </summary>
-        /// <param name="address">Address to check.</param>
-        /// <returns>True or False.</returns>
-        bool IsPrivateAddressRange(IPObject address);
-
         /// <summary>
         /// <summary>
         /// Returns true if the address is part of the user defined LAN.
         /// Returns true if the address is part of the user defined LAN.
         /// The configuration option TrustIP6Interfaces overrides this functions behaviour.
         /// The configuration option TrustIP6Interfaces overrides this functions behaviour.
@@ -161,14 +107,6 @@ namespace MediaBrowser.Common.Net
         /// <returns>True if endpoint is within the LAN range.</returns>
         /// <returns>True if endpoint is within the LAN range.</returns>
         bool IsInLocalNetwork(string address);
         bool IsInLocalNetwork(string address);
 
 
-        /// <summary>
-        /// Returns true if the address is part of the user defined LAN.
-        /// The configuration option TrustIP6Interfaces overrides this functions behaviour.
-        /// </summary>
-        /// <param name="address">IP to check.</param>
-        /// <returns>True if endpoint is within the LAN range.</returns>
-        bool IsInLocalNetwork(IPObject address);
-
         /// <summary>
         /// <summary>
         /// Returns true if the address is part of the user defined LAN.
         /// Returns true if the address is part of the user defined LAN.
         /// The configuration option TrustIP6Interfaces overrides this functions behaviour.
         /// The configuration option TrustIP6Interfaces overrides this functions behaviour.
@@ -178,55 +116,19 @@ namespace MediaBrowser.Common.Net
         bool IsInLocalNetwork(IPAddress address);
         bool IsInLocalNetwork(IPAddress address);
 
 
         /// <summary>
         /// <summary>
-        /// Attempts to convert the token to an IP address, permitting for interface descriptions and indexes.
-        /// eg. "eth1", or "TP-LINK Wireless USB Adapter".
+        /// Attempts to convert the interface name to an IP address.
+        /// eg. "eth1", or "enp3s5".
         /// </summary>
         /// </summary>
-        /// <param name="token">Token to parse.</param>
+        /// <param name="intf">Interface name.</param>
         /// <param name="result">Resultant object's ip addresses, if successful.</param>
         /// <param name="result">Resultant object's ip addresses, if successful.</param>
         /// <returns>Success of the operation.</returns>
         /// <returns>Success of the operation.</returns>
-        bool TryParseInterface(string token, out Collection<IPObject>? result);
-
-        /// <summary>
-        /// Parses an array of strings into a Collection{IPObject}.
-        /// </summary>
-        /// <param name="values">Values to parse.</param>
-        /// <param name="negated">When true, only include values beginning with !. When false, ignore ! values.</param>
-        /// <returns>IPCollection object containing the value strings.</returns>
-        Collection<IPObject> CreateIPCollection(string[] values, bool negated = false);
+        bool TryParseInterface(string intf, out Collection<IPData>? result);
 
 
         /// <summary>
         /// <summary>
         /// Returns all the internal Bind interface addresses.
         /// Returns all the internal Bind interface addresses.
         /// </summary>
         /// </summary>
         /// <returns>An internal list of interfaces addresses.</returns>
         /// <returns>An internal list of interfaces addresses.</returns>
-        Collection<IPObject> GetInternalBindAddresses();
-
-        /// <summary>
-        /// Checks to see if an IP address is still a valid interface address.
-        /// </summary>
-        /// <param name="address">IP address to check.</param>
-        /// <returns>True if it is.</returns>
-        bool IsValidInterfaceAddress(IPAddress address);
-
-        /// <summary>
-        /// Returns true if the IP address is in the excluded list.
-        /// </summary>
-        /// <param name="ip">IP to check.</param>
-        /// <returns>True if excluded.</returns>
-        bool IsExcluded(IPAddress ip);
-
-        /// <summary>
-        /// Returns true if the IP address is in the excluded list.
-        /// </summary>
-        /// <param name="ip">IP to check.</param>
-        /// <returns>True if excluded.</returns>
-        bool IsExcluded(EndPoint ip);
-
-        /// <summary>
-        /// Gets the filtered LAN ip addresses.
-        /// </summary>
-        /// <param name="filter">Optional filter for the list.</param>
-        /// <returns>Returns a filtered list of LAN addresses.</returns>
-        Collection<IPObject> GetFilteredLANSubnets(Collection<IPObject>? filter = null);
+        List<IPData> GetInternalBindAddresses();
 
 
         /// <summary>
         /// <summary>
         /// Checks to see if <paramref name="remoteIp"/> has access.
         /// Checks to see if <paramref name="remoteIp"/> has access.

+ 76 - 0
MediaBrowser.Common/Net/IPData.cs

@@ -0,0 +1,76 @@
+using System.Net;
+using System.Net.Sockets;
+using Microsoft.AspNetCore.HttpOverrides;
+
+namespace MediaBrowser.Common.Net
+{
+    /// <summary>
+    /// Base network object class.
+    /// </summary>
+    public class IPData
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="IPData"/> class.
+        /// </summary>
+        /// <param name="address">An <see cref="IPAddress"/>.</param>
+        /// <param name="subnet">The <see cref="IPNetwork"/>.</param>
+        public IPData(
+            IPAddress address,
+            IPNetwork? subnet)
+        {
+            Address = address;
+            Subnet = subnet ?? (address.AddressFamily == AddressFamily.InterNetwork ? new IPNetwork(address, 32) : new IPNetwork(address, 128));
+            Name = string.Empty;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="IPData"/> class.
+        /// </summary>
+        /// <param name="address">An <see cref="IPAddress"/>.</param>
+        /// <param name="subnet">The <see cref="IPNetwork"/>.</param>
+        /// <param name="name">The object's name.</param>
+        public IPData(
+            IPAddress address,
+            IPNetwork? subnet,
+            string name)
+        {
+            Address = address;
+            Subnet = subnet ?? (address.AddressFamily == AddressFamily.InterNetwork ? new IPNetwork(address, 32) : new IPNetwork(address, 128));
+            Name = name;
+        }
+
+        /// <summary>
+        /// Gets or sets the object's IP address.
+        /// </summary>
+        public IPAddress Address { get; set; }
+
+        /// <summary>
+        /// Gets or sets the object's IP address.
+        /// </summary>
+        public IPNetwork Subnet { get; set; }
+
+        /// <summary>
+        /// Gets or sets the interface index.
+        /// </summary>
+        public int Index { get; set; }
+
+        /// <summary>
+        /// Gets or sets the interface name.
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Gets the AddressFamily of this object.
+        /// </summary>
+        public AddressFamily AddressFamily
+        {
+            get
+            {
+                return Address.Equals(IPAddress.None)
+                    ? (Subnet.Prefix.AddressFamily.Equals(IPAddress.None)
+                        ? AddressFamily.Unspecified : Subnet.Prefix.AddressFamily)
+                    : Address.AddressFamily;
+            }
+        }
+    }
+}

+ 0 - 441
MediaBrowser.Common/Net/IPHost.cs

@@ -1,441 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Linq;
-using System.Net;
-using System.Net.Sockets;
-using System.Text.RegularExpressions;
-
-namespace MediaBrowser.Common.Net
-{
-    /// <summary>
-    /// Object that holds a host name.
-    /// </summary>
-    public class IPHost : IPObject
-    {
-        /// <summary>
-        /// Gets or sets timeout value before resolve required, in minutes.
-        /// </summary>
-        public const int Timeout = 30;
-
-        /// <summary>
-        /// Represents an IPHost that has no value.
-        /// </summary>
-        public static readonly IPHost None = new IPHost(string.Empty, IPAddress.None);
-
-        /// <summary>
-        /// Time when last resolved in ticks.
-        /// </summary>
-        private DateTime? _lastResolved = null;
-
-        /// <summary>
-        /// Gets the IP Addresses, attempting to resolve the name, if there are none.
-        /// </summary>
-        private IPAddress[] _addresses;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="IPHost"/> class.
-        /// </summary>
-        /// <param name="name">Host name to assign.</param>
-        public IPHost(string name)
-        {
-            HostName = name ?? throw new ArgumentNullException(nameof(name));
-            _addresses = Array.Empty<IPAddress>();
-            Resolved = false;
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="IPHost"/> class.
-        /// </summary>
-        /// <param name="name">Host name to assign.</param>
-        /// <param name="address">Address to assign.</param>
-        private IPHost(string name, IPAddress address)
-        {
-            HostName = name ?? throw new ArgumentNullException(nameof(name));
-            _addresses = new IPAddress[] { address ?? throw new ArgumentNullException(nameof(address)) };
-            Resolved = !address.Equals(IPAddress.None);
-        }
-
-        /// <summary>
-        /// Gets or sets the object's first IP address.
-        /// </summary>
-        public override IPAddress Address
-        {
-            get
-            {
-                return ResolveHost() ? this[0] : IPAddress.None;
-            }
-
-            set
-            {
-                // Not implemented, as a host's address is determined by DNS.
-                throw new NotImplementedException("The address of a host is determined by DNS.");
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets the object's first IP's subnet prefix.
-        /// The setter does nothing, but shouldn't raise an exception.
-        /// </summary>
-        public override byte PrefixLength
-        {
-            get => (byte)(ResolveHost() ? 128 : 32);
-
-            // Not implemented, as a host object can only have a prefix length of 128 (IPv6) or 32 (IPv4) prefix length,
-            // which is automatically determined by it's IP type. Anything else is meaningless.
-            set => throw new NotImplementedException();
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether the address has a value.
-        /// </summary>
-        public bool HasAddress => _addresses.Length != 0;
-
-        /// <summary>
-        /// Gets the host name of this object.
-        /// </summary>
-        public string HostName { get; }
-
-        /// <summary>
-        /// Gets a value indicating whether this host has attempted to be resolved.
-        /// </summary>
-        public bool Resolved { get; private set; }
-
-        /// <summary>
-        /// Gets or sets the IP Addresses associated with this object.
-        /// </summary>
-        /// <param name="index">Index of address.</param>
-        public IPAddress this[int index]
-        {
-            get
-            {
-                ResolveHost();
-                return index >= 0 && index < _addresses.Length ? _addresses[index] : IPAddress.None;
-            }
-        }
-
-        /// <summary>
-        /// Attempts to parse the host string.
-        /// </summary>
-        /// <param name="host">Host name to parse.</param>
-        /// <param name="hostObj">Object representing the string, if it has successfully been parsed.</param>
-        /// <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.IsNullOrWhiteSpace(host))
-            {
-                hostObj = IPHost.None;
-                return false;
-            }
-
-            // See if it's an IPv6 with port address e.g. [::1] or [::1]:120.
-            int i = host.IndexOf(']', StringComparison.Ordinal);
-            if (i != -1)
-            {
-                return TryParse(host.Remove(i - 1).TrimStart(' ', '['), out hostObj);
-            }
-
-            if (IPNetAddress.TryParse(host, out var netAddress))
-            {
-                // Host name is an ip address, so fake resolve.
-                hostObj = new IPHost(host, netAddress.Address);
-                return true;
-            }
-
-            // Is it a host, IPv4/6 with/out port?
-            string[] hosts = host.Split(':');
-
-            if (hosts.Length <= 2)
-            {
-                // This is either a hostname: port, or an IP4:port.
-                host = hosts[0];
-
-                if (string.Equals("localhost", host, StringComparison.OrdinalIgnoreCase))
-                {
-                    hostObj = new IPHost(host);
-                    return true;
-                }
-
-                if (IPAddress.TryParse(host, out var netIP))
-                {
-                    // 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;
-        }
-
-        /// <summary>
-        /// Attempts to parse the host string.
-        /// </summary>
-        /// <param name="host">Host name to parse.</param>
-        /// <returns>Object representing the string, if it has successfully been parsed.</returns>
-        public static IPHost Parse(string host)
-        {
-            if (!string.IsNullOrEmpty(host) && IPHost.TryParse(host, out IPHost res))
-            {
-                return res;
-            }
-
-            throw new InvalidCastException($"Host does not contain a valid value. {host}");
-        }
-
-        /// <summary>
-        /// Attempts to parse the host string, ensuring that it resolves only to a specific IP type.
-        /// </summary>
-        /// <param name="host">Host name to parse.</param>
-        /// <param name="family">Addressfamily filter.</param>
-        /// <returns>Object representing the string, if it has successfully been parsed.</returns>
-        public static IPHost Parse(string host, AddressFamily family)
-        {
-            if (!string.IsNullOrEmpty(host) && IPHost.TryParse(host, out IPHost res))
-            {
-                if (family == AddressFamily.InterNetwork)
-                {
-                    res.Remove(AddressFamily.InterNetworkV6);
-                }
-                else
-                {
-                    res.Remove(AddressFamily.InterNetwork);
-                }
-
-                return res;
-            }
-
-            throw new InvalidCastException($"Host does not contain a valid value. {host}");
-        }
-
-        /// <summary>
-        /// Returns the Addresses that this item resolved to.
-        /// </summary>
-        /// <returns>IPAddress Array.</returns>
-        public IPAddress[] GetAddresses()
-        {
-            ResolveHost();
-            return _addresses;
-        }
-
-        /// <inheritdoc/>
-        public override bool Contains(IPAddress address)
-        {
-            if (address != null && !Address.Equals(IPAddress.None))
-            {
-                if (address.IsIPv4MappedToIPv6)
-                {
-                    address = address.MapToIPv4();
-                }
-
-                foreach (var addr in GetAddresses())
-                {
-                    if (address.Equals(addr))
-                    {
-                        return true;
-                    }
-                }
-            }
-
-            return false;
-        }
-
-        /// <inheritdoc/>
-        public override bool Equals(IPObject? other)
-        {
-            if (other is IPHost otherObj)
-            {
-                // Do we have the name Hostname?
-                if (string.Equals(otherObj.HostName, HostName, StringComparison.OrdinalIgnoreCase))
-                {
-                    return true;
-                }
-
-                if (!ResolveHost() || !otherObj.ResolveHost())
-                {
-                    return false;
-                }
-
-                // Do any of our IP addresses match?
-                foreach (IPAddress addr in _addresses)
-                {
-                    foreach (IPAddress otherAddress in otherObj._addresses)
-                    {
-                        if (addr.Equals(otherAddress))
-                        {
-                            return true;
-                        }
-                    }
-                }
-            }
-
-            return false;
-        }
-
-        /// <inheritdoc/>
-        public override bool IsIP6()
-        {
-            // Returns true if interfaces are only IP6.
-            if (ResolveHost())
-            {
-                foreach (IPAddress i in _addresses)
-                {
-                    if (i.AddressFamily != AddressFamily.InterNetworkV6)
-                    {
-                        return false;
-                    }
-                }
-
-                return true;
-            }
-
-            return false;
-        }
-
-        /// <inheritdoc/>
-        public override string ToString()
-        {
-            // StringBuilder not optimum here.
-            string output = string.Empty;
-            if (_addresses.Length > 0)
-            {
-                bool moreThanOne = _addresses.Length > 1;
-                if (moreThanOne)
-                {
-                    output = "[";
-                }
-
-                foreach (var i in _addresses)
-                {
-                    if (Address.Equals(IPAddress.None) && Address.AddressFamily == AddressFamily.Unspecified)
-                    {
-                        output += HostName + ",";
-                    }
-                    else if (i.Equals(IPAddress.Any))
-                    {
-                        output += "Any IP4 Address,";
-                    }
-                    else if (Address.Equals(IPAddress.IPv6Any))
-                    {
-                        output += "Any IP6 Address,";
-                    }
-                    else if (i.Equals(IPAddress.Broadcast))
-                    {
-                        output += "Any Address,";
-                    }
-                    else if (i.AddressFamily == AddressFamily.InterNetwork)
-                    {
-                        output += $"{i}/32,";
-                    }
-                    else
-                    {
-                        output += $"{i}/128,";
-                    }
-                }
-
-                output = output[..^1];
-
-                if (moreThanOne)
-                {
-                    output += "]";
-                }
-            }
-            else
-            {
-                output = HostName;
-            }
-
-            return output;
-        }
-
-        /// <inheritdoc/>
-        public override void Remove(AddressFamily family)
-        {
-            if (ResolveHost())
-            {
-                _addresses = _addresses.Where(p => p.AddressFamily != family).ToArray();
-            }
-        }
-
-        /// <inheritdoc/>
-        public override bool Contains(IPObject address)
-        {
-            // An IPHost cannot contain another IPObject, it can only be equal.
-            return Equals(address);
-        }
-
-        /// <inheritdoc/>
-        protected override IPObject CalculateNetworkAddress()
-        {
-            var (address, prefixLength) = NetworkAddressOf(this[0], PrefixLength);
-            return new IPNetAddress(address, prefixLength);
-        }
-
-        /// <summary>
-        /// Attempt to resolve the ip address of a host.
-        /// </summary>
-        /// <returns><c>true</c> if any addresses have been resolved, otherwise <c>false</c>.</returns>
-        private bool ResolveHost()
-        {
-            // When was the last time we resolved?
-            _lastResolved ??= DateTime.UtcNow;
-
-            // If we haven't resolved before, or our timer has run out...
-            if ((_addresses.Length == 0 && !Resolved) || (DateTime.UtcNow > _lastResolved.Value.AddMinutes(Timeout)))
-            {
-                _lastResolved = DateTime.UtcNow;
-                ResolveHostInternal();
-                Resolved = true;
-            }
-
-            return _addresses.Length > 0;
-        }
-
-        /// <summary>
-        /// Task that looks up a Host name and returns its IP addresses.
-        /// </summary>
-        private void ResolveHostInternal()
-        {
-            var hostName = HostName;
-            if (string.IsNullOrEmpty(hostName))
-            {
-                return;
-            }
-
-            // Resolves the host name - so save a DNS lookup.
-            if (string.Equals(hostName, "localhost", StringComparison.OrdinalIgnoreCase))
-            {
-                _addresses = new IPAddress[] { IPAddress.Loopback, IPAddress.IPv6Loopback };
-                return;
-            }
-
-            if (Uri.CheckHostName(hostName) == UriHostNameType.Dns)
-            {
-                try
-                {
-                    _addresses = Dns.GetHostEntry(hostName).AddressList;
-                }
-                catch (SocketException ex)
-                {
-                    // Log and then ignore socket errors, as the result value will just be an empty array.
-                    Debug.WriteLine("GetHostAddresses failed with {Message}.", ex.Message);
-                }
-            }
-        }
-    }
-}

+ 0 - 276
MediaBrowser.Common/Net/IPNetAddress.cs

@@ -1,276 +0,0 @@
-using System;
-using System.Net;
-using System.Net.Sockets;
-
-namespace MediaBrowser.Common.Net
-{
-    /// <summary>
-    /// An object that holds and IP address and subnet mask.
-    /// </summary>
-    public class IPNetAddress : IPObject
-    {
-        /// <summary>
-        /// Represents an IPNetAddress that has no value.
-        /// </summary>
-        public static readonly IPNetAddress None = new IPNetAddress(IPAddress.None);
-
-        /// <summary>
-        /// IPv4 multicast address.
-        /// </summary>
-        public static readonly IPAddress SSDPMulticastIPv4 = IPAddress.Parse("239.255.255.250");
-
-        /// <summary>
-        /// IPv6 local link multicast address.
-        /// </summary>
-        public static readonly IPAddress SSDPMulticastIPv6LinkLocal = IPAddress.Parse("ff02::C");
-
-        /// <summary>
-        /// IPv6 site local multicast address.
-        /// </summary>
-        public static readonly IPAddress SSDPMulticastIPv6SiteLocal = IPAddress.Parse("ff05::C");
-
-        /// <summary>
-        /// IP4Loopback address host.
-        /// </summary>
-        public static readonly IPNetAddress IP4Loopback = IPNetAddress.Parse("127.0.0.1/8");
-
-        /// <summary>
-        /// IP6Loopback address host.
-        /// </summary>
-        public static readonly IPNetAddress IP6Loopback = new IPNetAddress(IPAddress.IPv6Loopback);
-
-        /// <summary>
-        /// Object's IP address.
-        /// </summary>
-        private IPAddress _address;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="IPNetAddress"/> class.
-        /// </summary>
-        /// <param name="address">Address to assign.</param>
-        public IPNetAddress(IPAddress address)
-        {
-            _address = address ?? throw new ArgumentNullException(nameof(address));
-            PrefixLength = (byte)(address.AddressFamily == AddressFamily.InterNetwork ? 32 : 128);
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="IPNetAddress"/> class.
-        /// </summary>
-        /// <param name="address">IP Address.</param>
-        /// <param name="prefixLength">Mask as a CIDR.</param>
-        public IPNetAddress(IPAddress address, byte prefixLength)
-        {
-            if (address?.IsIPv4MappedToIPv6 ?? throw new ArgumentNullException(nameof(address)))
-            {
-                _address = address.MapToIPv4();
-            }
-            else
-            {
-                _address = address;
-            }
-
-            PrefixLength = prefixLength;
-        }
-
-        /// <summary>
-        /// Gets or sets the object's IP address.
-        /// </summary>
-        public override IPAddress Address
-        {
-            get
-            {
-                return _address;
-            }
-
-            set
-            {
-                _address = value ?? IPAddress.None;
-            }
-        }
-
-        /// <inheritdoc/>
-        public override byte PrefixLength { get; set; }
-
-        /// <summary>
-        /// Try to parse the address and subnet strings into an IPNetAddress object.
-        /// </summary>
-        /// <param name="addr">IP address to parse. Can be CIDR or X.X.X.X notation.</param>
-        /// <param name="ip">Resultant object.</param>
-        /// <returns>True if the values parsed successfully. False if not, resulting in the IP being null.</returns>
-        public static bool TryParse(string addr, out IPNetAddress ip)
-        {
-            if (!string.IsNullOrEmpty(addr))
-            {
-                addr = addr.Trim();
-
-                // Try to parse it as is.
-                if (IPAddress.TryParse(addr, out IPAddress? res))
-                {
-                    ip = new IPNetAddress(res);
-                    return true;
-                }
-
-                // Is it a network?
-                string[] tokens = addr.Split('/');
-
-                if (tokens.Length == 2)
-                {
-                    tokens[0] = tokens[0].TrimEnd();
-                    tokens[1] = tokens[1].TrimStart();
-
-                    if (IPAddress.TryParse(tokens[0], out res))
-                    {
-                        // Is the subnet part a cidr?
-                        if (byte.TryParse(tokens[1], out byte cidr))
-                        {
-                            ip = new IPNetAddress(res, cidr);
-                            return true;
-                        }
-
-                        // Is the subnet in x.y.a.b form?
-                        if (IPAddress.TryParse(tokens[1], out IPAddress? mask))
-                        {
-                            ip = new IPNetAddress(res, MaskToCidr(mask));
-                            return true;
-                        }
-                    }
-                }
-            }
-
-            ip = None;
-            return false;
-        }
-
-        /// <summary>
-        /// Parses the string provided, throwing an exception if it is badly formed.
-        /// </summary>
-        /// <param name="addr">String to parse.</param>
-        /// <returns>IPNetAddress object.</returns>
-        public static IPNetAddress Parse(string addr)
-        {
-            if (TryParse(addr, out IPNetAddress o))
-            {
-                return o;
-            }
-
-            throw new ArgumentException("Unable to recognise object :" + addr);
-        }
-
-        /// <inheritdoc/>
-        public override bool Contains(IPAddress address)
-        {
-            if (address == null)
-            {
-                throw new ArgumentNullException(nameof(address));
-            }
-
-            if (address.IsIPv4MappedToIPv6)
-            {
-                address = address.MapToIPv4();
-            }
-
-            var (altAddress, altPrefix) = NetworkAddressOf(address, PrefixLength);
-            return NetworkAddress.Address.Equals(altAddress) && NetworkAddress.PrefixLength >= altPrefix;
-        }
-
-        /// <inheritdoc/>
-        public override bool Contains(IPObject address)
-        {
-            if (address is IPHost addressObj && addressObj.HasAddress)
-            {
-                foreach (IPAddress addr in addressObj.GetAddresses())
-                {
-                    if (Contains(addr))
-                    {
-                        return true;
-                    }
-                }
-            }
-            else if (address is IPNetAddress netaddrObj)
-            {
-                // Have the same network address, but different subnets?
-                if (NetworkAddress.Address.Equals(netaddrObj.NetworkAddress.Address))
-                {
-                    return NetworkAddress.PrefixLength <= netaddrObj.PrefixLength;
-                }
-
-                var altAddress = NetworkAddressOf(netaddrObj.Address, PrefixLength).Address;
-                return NetworkAddress.Address.Equals(altAddress);
-            }
-
-            return false;
-        }
-
-        /// <inheritdoc/>
-        public override bool Equals(IPObject? other)
-        {
-            if (other is IPNetAddress otherObj && !Address.Equals(IPAddress.None) && !otherObj.Address.Equals(IPAddress.None))
-            {
-                return Address.Equals(otherObj.Address) &&
-                    PrefixLength == otherObj.PrefixLength;
-            }
-
-            return false;
-        }
-
-        /// <inheritdoc/>
-        public override bool Equals(IPAddress ip)
-        {
-            if (ip != null && !ip.Equals(IPAddress.None) && !Address.Equals(IPAddress.None))
-            {
-                return ip.Equals(Address);
-            }
-
-            return false;
-        }
-
-        /// <inheritdoc/>
-        public override string ToString()
-        {
-            return ToString(false);
-        }
-
-        /// <summary>
-        /// Returns a textual representation of this object.
-        /// </summary>
-        /// <param name="shortVersion">Set to true, if the subnet is to be excluded as part of the address.</param>
-        /// <returns>String representation of this object.</returns>
-        public string ToString(bool shortVersion)
-        {
-            if (!Address.Equals(IPAddress.None))
-            {
-                if (Address.Equals(IPAddress.Any))
-                {
-                    return "Any IP4 Address";
-                }
-
-                if (Address.Equals(IPAddress.IPv6Any))
-                {
-                    return "Any IP6 Address";
-                }
-
-                if (Address.Equals(IPAddress.Broadcast))
-                {
-                    return "Any Address";
-                }
-
-                if (shortVersion)
-                {
-                    return Address.ToString();
-                }
-
-                return $"{Address}/{PrefixLength}";
-            }
-
-            return string.Empty;
-        }
-
-        /// <inheritdoc/>
-        protected override IPObject CalculateNetworkAddress()
-        {
-            var (address, prefixLength) = NetworkAddressOf(_address, PrefixLength);
-            return new IPNetAddress(address, prefixLength);
-        }
-    }
-}

+ 0 - 370
MediaBrowser.Common/Net/IPObject.cs

@@ -1,370 +0,0 @@
-using System;
-using System.Net;
-using System.Net.Sockets;
-
-namespace MediaBrowser.Common.Net
-{
-    /// <summary>
-    /// Base network object class.
-    /// </summary>
-    public abstract class IPObject : IEquatable<IPObject>
-    {
-        /// <summary>
-        /// The network address of this object.
-        /// </summary>
-        private IPObject? _networkAddress;
-
-        /// <summary>
-        /// Gets or sets a user defined value that is associated with this object.
-        /// </summary>
-        public int Tag { get; set; }
-
-        /// <summary>
-        /// Gets or sets the object's IP address.
-        /// </summary>
-        public abstract IPAddress Address { get; set; }
-
-        /// <summary>
-        /// Gets the object's network address.
-        /// </summary>
-        public IPObject NetworkAddress => _networkAddress ??= CalculateNetworkAddress();
-
-        /// <summary>
-        /// Gets or sets the object's IP address.
-        /// </summary>
-        public abstract byte PrefixLength { get; set; }
-
-        /// <summary>
-        /// Gets the AddressFamily of this object.
-        /// </summary>
-        public AddressFamily AddressFamily
-        {
-            get
-            {
-                // Keep terms separate as Address performs other functions in inherited objects.
-                IPAddress address = Address;
-                return address.Equals(IPAddress.None) ? AddressFamily.Unspecified : address.AddressFamily;
-            }
-        }
-
-        /// <summary>
-        /// Returns the network address of an object.
-        /// </summary>
-        /// <param name="address">IP Address to convert.</param>
-        /// <param name="prefixLength">Subnet prefix.</param>
-        /// <returns>IPAddress.</returns>
-        public static (IPAddress Address, byte PrefixLength) NetworkAddressOf(IPAddress address, byte prefixLength)
-        {
-            if (address == null)
-            {
-                throw new ArgumentNullException(nameof(address));
-            }
-
-            if (address.IsIPv4MappedToIPv6)
-            {
-                address = address.MapToIPv4();
-            }
-
-            if (IPAddress.IsLoopback(address))
-            {
-                return (address, prefixLength);
-            }
-
-            // An ip address is just a list of bytes, each one representing a segment on the network.
-            // This separates the IP address into octets and calculates how many octets will need to be altered or set to zero dependant upon the
-            // prefix length value. eg. /16 on a 4 octet ip4 address (192.168.2.240) will result in the 2 and the 240 being zeroed out.
-            // Where there is not an exact boundary (eg /23), mod is used to calculate how many bits of this value are to be kept.
-
-            // GetAddressBytes
-            Span<byte> addressBytes = stackalloc byte[address.AddressFamily == AddressFamily.InterNetwork ? 4 : 16];
-            address.TryWriteBytes(addressBytes, out _);
-
-            int div = prefixLength / 8;
-            int mod = prefixLength % 8;
-            if (mod != 0)
-            {
-                // Prefix length is counted right to left, so subtract 8 so we know how many bits to clear.
-                mod = 8 - mod;
-
-                // Shift out the bits from the octet that we don't want, by moving right then back left.
-                addressBytes[div] = (byte)((int)addressBytes[div] >> mod << mod);
-                // Move on the next byte.
-                div++;
-            }
-
-            // Blank out the remaining octets from mod + 1 to the end of the byte array. (192.168.2.240/16 becomes 192.168.0.0)
-            for (int octet = div; octet < addressBytes.Length; octet++)
-            {
-                addressBytes[octet] = 0;
-            }
-
-            // Return the network address for the prefix.
-            return (new IPAddress(addressBytes), prefixLength);
-        }
-
-        /// <summary>
-        /// Tests to see if the ip address is an IP6 address.
-        /// </summary>
-        /// <param name="address">Value to test.</param>
-        /// <returns>True if it is.</returns>
-        public static bool IsIP6(IPAddress address)
-        {
-            if (address == null)
-            {
-                throw new ArgumentNullException(nameof(address));
-            }
-
-            if (address.IsIPv4MappedToIPv6)
-            {
-                address = address.MapToIPv4();
-            }
-
-            return !address.Equals(IPAddress.None) && (address.AddressFamily == AddressFamily.InterNetworkV6);
-        }
-
-        /// <summary>
-        /// Tests to see if the address in the private address range.
-        /// </summary>
-        /// <param name="address">Object to test.</param>
-        /// <returns>True if it contains a private address.</returns>
-        public static bool IsPrivateAddressRange(IPAddress address)
-        {
-            if (address == null)
-            {
-                throw new ArgumentNullException(nameof(address));
-            }
-
-            if (!address.Equals(IPAddress.None))
-            {
-                if (address.IsIPv4MappedToIPv6)
-                {
-                    address = address.MapToIPv4();
-                }
-
-                if (address.AddressFamily == AddressFamily.InterNetwork)
-                {
-                    // GetAddressBytes
-                    Span<byte> octet = stackalloc byte[4];
-                    address.TryWriteBytes(octet, out _);
-
-                    return (octet[0] == 10)
-                           || (octet[0] == 172 && octet[1] >= 16 && octet[1] <= 31) // RFC1918
-                           || (octet[0] == 192 && octet[1] == 168) // RFC1918
-                           || (octet[0] == 127); // RFC1122
-                }
-                else
-                {
-                    // GetAddressBytes
-                    Span<byte> octet = stackalloc byte[16];
-                    address.TryWriteBytes(octet, out _);
-
-                    uint word = (uint)(octet[0] << 8) + octet[1];
-
-                    return (word >= 0xfe80 && word <= 0xfebf) // fe80::/10 :Local link.
-                           || (word >= 0xfc00 && word <= 0xfdff); // fc00::/7 :Unique local address.
-                }
-            }
-
-            return false;
-        }
-
-        /// <summary>
-        /// Returns true if the IPAddress contains an IP6 Local link address.
-        /// </summary>
-        /// <param name="address">IPAddress object to check.</param>
-        /// <returns>True if it is a local link address.</returns>
-        /// <remarks>
-        /// See https://stackoverflow.com/questions/6459928/explain-the-instance-properties-of-system-net-ipaddress
-        /// it appears that the IPAddress.IsIPv6LinkLocal is out of date.
-        /// </remarks>
-        public static bool IsIPv6LinkLocal(IPAddress address)
-        {
-            if (address == null)
-            {
-                throw new ArgumentNullException(nameof(address));
-            }
-
-            if (address.IsIPv4MappedToIPv6)
-            {
-                address = address.MapToIPv4();
-            }
-
-            if (address.AddressFamily != AddressFamily.InterNetworkV6)
-            {
-                return false;
-            }
-
-            // GetAddressBytes
-            Span<byte> octet = stackalloc byte[16];
-            address.TryWriteBytes(octet, out _);
-            uint word = (uint)(octet[0] << 8) + octet[1];
-
-            return word >= 0xfe80 && word <= 0xfebf; // fe80::/10 :Local link.
-        }
-
-        /// <summary>
-        /// Convert a subnet mask in CIDR notation to a dotted decimal string value. IPv4 only.
-        /// </summary>
-        /// <param name="cidr">Subnet mask in CIDR notation.</param>
-        /// <param name="family">IPv4 or IPv6 family.</param>
-        /// <returns>String value of the subnet mask in dotted decimal notation.</returns>
-        public static IPAddress CidrToMask(byte cidr, AddressFamily family)
-        {
-            uint addr = 0xFFFFFFFF << (family == AddressFamily.InterNetwork ? 32 : 128 - cidr);
-            addr = ((addr & 0xff000000) >> 24)
-                   | ((addr & 0x00ff0000) >> 8)
-                   | ((addr & 0x0000ff00) << 8)
-                   | ((addr & 0x000000ff) << 24);
-            return new IPAddress(addr);
-        }
-
-        /// <summary>
-        /// Convert a mask to a CIDR. IPv4 only.
-        /// https://stackoverflow.com/questions/36954345/get-cidr-from-netmask.
-        /// </summary>
-        /// <param name="mask">Subnet mask.</param>
-        /// <returns>Byte CIDR representing the mask.</returns>
-        public static byte MaskToCidr(IPAddress mask)
-        {
-            if (mask == null)
-            {
-                throw new ArgumentNullException(nameof(mask));
-            }
-
-            byte cidrnet = 0;
-            if (!mask.Equals(IPAddress.Any))
-            {
-                // GetAddressBytes
-                Span<byte> bytes = stackalloc byte[mask.AddressFamily == AddressFamily.InterNetwork ? 4 : 16];
-                mask.TryWriteBytes(bytes, out _);
-
-                var zeroed = false;
-                for (var i = 0; i < bytes.Length; i++)
-                {
-                    for (int v = bytes[i]; (v & 0xFF) != 0; v <<= 1)
-                    {
-                        if (zeroed)
-                        {
-                            // Invalid netmask.
-                            return (byte)~cidrnet;
-                        }
-
-                        if ((v & 0x80) == 0)
-                        {
-                            zeroed = true;
-                        }
-                        else
-                        {
-                            cidrnet++;
-                        }
-                    }
-                }
-            }
-
-            return cidrnet;
-        }
-
-        /// <summary>
-        /// Tests to see if this object is a Loopback address.
-        /// </summary>
-        /// <returns>True if it is.</returns>
-        public virtual bool IsLoopback()
-        {
-            return IPAddress.IsLoopback(Address);
-        }
-
-        /// <summary>
-        /// Removes all addresses of a specific type from this object.
-        /// </summary>
-        /// <param name="family">Type of address to remove.</param>
-        public virtual void Remove(AddressFamily family)
-        {
-            // This method only performs a function in the IPHost implementation of IPObject.
-        }
-
-        /// <summary>
-        /// Tests to see if this object is an IPv6 address.
-        /// </summary>
-        /// <returns>True if it is.</returns>
-        public virtual bool IsIP6()
-        {
-            return IsIP6(Address);
-        }
-
-        /// <summary>
-        /// Returns true if this IP address is in the RFC private address range.
-        /// </summary>
-        /// <returns>True this object has a private address.</returns>
-        public virtual bool IsPrivateAddressRange()
-        {
-            return IsPrivateAddressRange(Address);
-        }
-
-        /// <summary>
-        /// Compares this to the object passed as a parameter.
-        /// </summary>
-        /// <param name="ip">Object to compare to.</param>
-        /// <returns>Equality result.</returns>
-        public virtual bool Equals(IPAddress ip)
-        {
-            if (ip != null)
-            {
-                if (ip.IsIPv4MappedToIPv6)
-                {
-                    ip = ip.MapToIPv4();
-                }
-
-                return !Address.Equals(IPAddress.None) && Address.Equals(ip);
-            }
-
-            return false;
-        }
-
-        /// <summary>
-        /// Compares this to the object passed as a parameter.
-        /// </summary>
-        /// <param name="other">Object to compare to.</param>
-        /// <returns>Equality result.</returns>
-        public virtual bool Equals(IPObject? other)
-        {
-            if (other != null)
-            {
-                return !Address.Equals(IPAddress.None) && Address.Equals(other.Address);
-            }
-
-            return false;
-        }
-
-        /// <summary>
-        /// Compares the address in this object and the address in the object passed as a parameter.
-        /// </summary>
-        /// <param name="address">Object's IP address to compare to.</param>
-        /// <returns>Comparison result.</returns>
-        public abstract bool Contains(IPObject address);
-
-        /// <summary>
-        /// Compares the address in this object and the address in the object passed as a parameter.
-        /// </summary>
-        /// <param name="address">Object's IP address to compare to.</param>
-        /// <returns>Comparison result.</returns>
-        public abstract bool Contains(IPAddress address);
-
-        /// <inheritdoc/>
-        public override int GetHashCode()
-        {
-            return Address.GetHashCode();
-        }
-
-        /// <inheritdoc/>
-        public override bool Equals(object? obj)
-        {
-            return Equals(obj as IPObject);
-        }
-
-        /// <summary>
-        /// Calculates the network address of this object.
-        /// </summary>
-        /// <returns>Returns the network address of this object.</returns>
-        protected abstract IPObject CalculateNetworkAddress();
-    }
-}

+ 135 - 174
MediaBrowser.Common/Net/NetworkExtensions.cs

@@ -1,6 +1,9 @@
 using System;
 using System;
-using System.Collections.ObjectModel;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.Net;
 using System.Net;
+using System.Net.Sockets;
+using System.Text.RegularExpressions;
 
 
 namespace MediaBrowser.Common.Net
 namespace MediaBrowser.Common.Net
 {
 {
@@ -10,251 +13,209 @@ namespace MediaBrowser.Common.Net
     public static class NetworkExtensions
     public static class NetworkExtensions
     {
     {
         /// <summary>
         /// <summary>
-        /// Add an address to the collection.
+        /// Returns true if the IPAddress contains an IP6 Local link address.
         /// </summary>
         /// </summary>
-        /// <param name="source">The <see cref="Collection{IPObject}"/>.</param>
-        /// <param name="ip">Item to add.</param>
-        public static void AddItem(this Collection<IPObject> source, IPAddress ip)
+        /// <param name="address">IPAddress object to check.</param>
+        /// <returns>True if it is a local link address.</returns>
+        /// <remarks>
+        /// See https://stackoverflow.com/questions/6459928/explain-the-instance-properties-of-system-net-ipaddress
+        /// it appears that the IPAddress.IsIPv6LinkLocal is out of date.
+        /// </remarks>
+        public static bool IsIPv6LinkLocal(IPAddress address)
         {
         {
-            if (!source.ContainsAddress(ip))
+            if (address == null)
             {
             {
-                source.Add(new IPNetAddress(ip, 32));
+                throw new ArgumentNullException(nameof(address));
             }
             }
-        }
 
 
-        /// <summary>
-        /// Adds a network to the collection.
-        /// </summary>
-        /// <param name="source">The <see cref="Collection{IPObject}"/>.</param>
-        /// <param name="item">Item to add.</param>
-        /// <param name="itemsAreNetworks">If <c>true</c> the values are treated as subnets.
-        /// If <b>false</b> items are addresses.</param>
-        public static void AddItem(this Collection<IPObject> source, IPObject item, bool itemsAreNetworks = true)
-        {
-            if (!source.ContainsAddress(item) || !itemsAreNetworks)
+            if (address.IsIPv4MappedToIPv6)
             {
             {
-                source.Add(item);
+                address = address.MapToIPv4();
             }
             }
-        }
 
 
-        /// <summary>
-        /// Converts this object to a string.
-        /// </summary>
-        /// <param name="source">The <see cref="Collection{IPObject}"/>.</param>
-        /// <returns>Returns a string representation of this object.</returns>
-        public static string AsString(this Collection<IPObject> source)
-        {
-            return $"[{string.Join(',', source)}]";
-        }
-
-        /// <summary>
-        /// Returns true if the collection contains an item with the ip address,
-        /// or the ip address falls within any of the collection's network ranges.
-        /// </summary>
-        /// <param name="source">The <see cref="Collection{IPObject}"/>.</param>
-        /// <param name="item">The item to look for.</param>
-        /// <returns>True if the collection contains the item.</returns>
-        public static bool ContainsAddress(this Collection<IPObject> source, IPAddress item)
-        {
-            if (source.Count == 0)
+            if (address.AddressFamily != AddressFamily.InterNetworkV6)
             {
             {
                 return false;
                 return false;
             }
             }
 
 
-            if (item == null)
-            {
-                throw new ArgumentNullException(nameof(item));
-            }
-
-            if (item.IsIPv4MappedToIPv6)
-            {
-                item = item.MapToIPv4();
-            }
+            // GetAddressBytes
+            Span<byte> octet = stackalloc byte[16];
+            address.TryWriteBytes(octet, out _);
+            uint word = (uint)(octet[0] << 8) + octet[1];
 
 
-            foreach (var i in source)
-            {
-                if (i.Contains(item))
-                {
-                    return true;
-                }
-            }
+            return word >= 0xfe80 && word <= 0xfebf; // fe80::/10 :Local link.
+        }
 
 
-            return false;
+        /// <summary>
+        /// Convert a subnet mask in CIDR notation to a dotted decimal string value. IPv4 only.
+        /// </summary>
+        /// <param name="cidr">Subnet mask in CIDR notation.</param>
+        /// <param name="family">IPv4 or IPv6 family.</param>
+        /// <returns>String value of the subnet mask in dotted decimal notation.</returns>
+        public static IPAddress CidrToMask(byte cidr, AddressFamily family)
+        {
+            uint addr = 0xFFFFFFFF << (family == AddressFamily.InterNetwork ? 32 : 128 - cidr);
+            addr = ((addr & 0xff000000) >> 24)
+                   | ((addr & 0x00ff0000) >> 8)
+                   | ((addr & 0x0000ff00) << 8)
+                   | ((addr & 0x000000ff) << 24);
+            return new IPAddress(addr);
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Returns true if the collection contains an item with the ip address,
-        /// or the ip address falls within any of the collection's network ranges.
+        /// Convert a subnet mask to a CIDR. IPv4 only.
+        /// https://stackoverflow.com/questions/36954345/get-cidr-from-netmask.
         /// </summary>
         /// </summary>
-        /// <param name="source">The <see cref="Collection{IPObject}"/>.</param>
-        /// <param name="item">The item to look for.</param>
-        /// <returns>True if the collection contains the item.</returns>
-        public static bool ContainsAddress(this Collection<IPObject> source, IPObject item)
+        /// <param name="mask">Subnet mask.</param>
+        /// <returns>Byte CIDR representing the mask.</returns>
+        public static byte MaskToCidr(IPAddress mask)
         {
         {
-            if (source.Count == 0)
+            if (mask == null)
             {
             {
-                return false;
+                throw new ArgumentNullException(nameof(mask));
             }
             }
 
 
-            if (item == null)
+            byte cidrnet = 0;
+            if (!mask.Equals(IPAddress.Any))
             {
             {
-                throw new ArgumentNullException(nameof(item));
-            }
+                // GetAddressBytes
+                Span<byte> bytes = stackalloc byte[mask.AddressFamily == AddressFamily.InterNetwork ? 4 : 16];
+                mask.TryWriteBytes(bytes, out _);
 
 
-            foreach (var i in source)
-            {
-                if (i.Contains(item))
+                var zeroed = false;
+                for (var i = 0; i < bytes.Length; i++)
                 {
                 {
-                    return true;
+                    for (int v = bytes[i]; (v & 0xFF) != 0; v <<= 1)
+                    {
+                        if (zeroed)
+                        {
+                            // Invalid netmask.
+                            return (byte)~cidrnet;
+                        }
+
+                        if ((v & 0x80) == 0)
+                        {
+                            zeroed = true;
+                        }
+                        else
+                        {
+                            cidrnet++;
+                        }
+                    }
                 }
                 }
             }
             }
 
 
-            return false;
+            return cidrnet;
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compares two Collection{IPObject} objects. The order is ignored.
+        /// Converts an IPAddress into a string.
+        /// Ipv6 addresses are returned in [ ], with their scope removed.
         /// </summary>
         /// </summary>
-        /// <param name="source">The <see cref="Collection{IPObject}"/>.</param>
-        /// <param name="dest">Item to compare to.</param>
-        /// <returns>True if both are equal.</returns>
-        public static bool Compare(this Collection<IPObject> source, Collection<IPObject> dest)
+        /// <param name="address">Address to convert.</param>
+        /// <returns>URI safe conversion of the address.</returns>
+        public static string FormatIpString(IPAddress? address)
         {
         {
-            if (dest == null || source.Count != dest.Count)
+            if (address == null)
             {
             {
-                return false;
+                return string.Empty;
             }
             }
 
 
-            foreach (var sourceItem in source)
+            var str = address.ToString();
+            if (address.AddressFamily == AddressFamily.InterNetworkV6)
             {
             {
-                bool found = false;
-                foreach (var destItem in dest)
+                int i = str.IndexOf('%', StringComparison.Ordinal);
+                if (i != -1)
                 {
                 {
-                    if (sourceItem.Equals(destItem))
-                    {
-                        found = true;
-                        break;
-                    }
+                    str = str.Substring(0, i);
                 }
                 }
 
 
-                if (!found)
-                {
-                    return false;
-                }
+                return $"[{str}]";
             }
             }
 
 
-            return true;
+            return str;
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Returns a collection containing the subnets of this collection given.
+        /// Attempts to parse a host string.
         /// </summary>
         /// </summary>
-        /// <param name="source">The <see cref="Collection{IPObject}"/>.</param>
-        /// <returns>Collection{IPObject} object containing the subnets.</returns>
-        public static Collection<IPObject> AsNetworks(this Collection<IPObject> source)
+        /// <param name="host">Host name to parse.</param>
+        /// <param name="addresses">Object representing the string, if it has successfully been parsed.</param>
+        /// <param name="isIpv4Enabled"><c>true</c> if IPv4 is enabled.</param>
+        /// <param name="isIpv6Enabled"><c>true</c> if IPv6 is enabled.</param>
+        /// <returns><c>true</c> if the parsing is successful, <c>false</c> if not.</returns>
+        public static bool TryParseHost(string host, [NotNullWhen(true)] out IPAddress[] addresses, bool isIpv4Enabled = true, bool isIpv6Enabled = false)
         {
         {
-            if (source == null)
+            if (string.IsNullOrWhiteSpace(host))
             {
             {
-                throw new ArgumentNullException(nameof(source));
+                addresses = Array.Empty<IPAddress>();
+                return false;
             }
             }
 
 
-            Collection<IPObject> res = new Collection<IPObject>();
+            host = host.Trim();
 
 
-            foreach (IPObject i in source)
+            // See if it's an IPv6 with port address e.g. [::1] or [::1]:120.
+            if (host[0] == '[')
             {
             {
-                if (i is IPNetAddress nw)
-                {
-                    // Add the subnet calculated from the interface address/mask.
-                    var na = nw.NetworkAddress;
-                    na.Tag = i.Tag;
-                    res.AddItem(na);
-                }
-                else if (i is IPHost ipHost)
+                int i = host.IndexOf(']', StringComparison.Ordinal);
+                if (i != -1)
                 {
                 {
-                    // Flatten out IPHost and add all its ip addresses.
-                    foreach (var addr in ipHost.GetAddresses())
-                    {
-                        IPNetAddress host = new IPNetAddress(addr)
-                        {
-                            Tag = i.Tag
-                        };
-
-                        res.AddItem(host);
-                    }
+                    return TryParseHost(host.Remove(i)[1..], out addresses);
                 }
                 }
-            }
-
-            return res;
-        }
 
 
-        /// <summary>
-        /// Excludes all the items from this list that are found in excludeList.
-        /// </summary>
-        /// <param name="source">The <see cref="Collection{IPObject}"/>.</param>
-        /// <param name="excludeList">Items to exclude.</param>
-        /// <param name="isNetwork">Collection is a network collection.</param>
-        /// <returns>A new collection, with the items excluded.</returns>
-        public static Collection<IPObject> Exclude(this Collection<IPObject> source, Collection<IPObject> excludeList, bool isNetwork)
-        {
-            if (source.Count == 0 || excludeList == null)
-            {
-                return new Collection<IPObject>(source);
+                addresses = Array.Empty<IPAddress>();
+                return false;
             }
             }
 
 
-            Collection<IPObject> results = new Collection<IPObject>();
+            var hosts = host.Split(':');
 
 
-            bool found;
-            foreach (var outer in source)
+            if (hosts.Length <= 2)
             {
             {
-                found = 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-]+\.?)(:(\d){1,5}){0,1}$";
 
 
-                foreach (var inner in excludeList)
+                // Is hostname or hostname:port
+                if (Regex.IsMatch(hosts[0], pattern))
                 {
                 {
-                    if (outer.Equals(inner))
+                    try
+                    {
+                        addresses = Dns.GetHostAddresses(hosts[0]);
+                        return true;
+                    }
+                    catch (SocketException)
                     {
                     {
-                        found = true;
-                        break;
+                        // Log and then ignore socket errors, as the result value will just be an empty array.
+                        Console.WriteLine("GetHostAddresses failed.");
                     }
                     }
                 }
                 }
 
 
-                if (!found)
-                {
-                    results.AddItem(outer, isNetwork);
-                }
-            }
+                // Is an IP4 or IP4:port
+                host = hosts[0].Split('/')[0];
 
 
-            return results;
-        }
+                if (IPAddress.TryParse(host, out var address))
+                {
+                    if (((address.AddressFamily == AddressFamily.InterNetwork) && (!isIpv4Enabled && isIpv6Enabled)) ||
+                        ((address.AddressFamily == AddressFamily.InterNetworkV6) && (isIpv4Enabled && !isIpv6Enabled)))
+                    {
+                        addresses = Array.Empty<IPAddress>();
+                        return false;
+                    }
 
 
-        /// <summary>
-        /// Returns all items that co-exist in this object and target.
-        /// </summary>
-        /// <param name="source">The <see cref="Collection{IPObject}"/>.</param>
-        /// <param name="target">Collection to compare with.</param>
-        /// <returns>A collection containing all the matches.</returns>
-        public static Collection<IPObject> ThatAreContainedInNetworks(this Collection<IPObject> source, Collection<IPObject> target)
-        {
-            if (source.Count == 0)
-            {
-                return new Collection<IPObject>();
-            }
+                    addresses = new[] { address };
 
 
-            if (target == null)
-            {
-                throw new ArgumentNullException(nameof(target));
+                    // Host name is an ip4 address, so fake resolve.
+                    return true;
+                }
             }
             }
-
-            Collection<IPObject> nc = new Collection<IPObject>();
-
-            foreach (IPObject i in source)
+            else if (hosts.Length <= 9 && IPAddress.TryParse(host.Split('/')[0], out var address)) // 8 octets + port
             {
             {
-                if (target.ContainsAddress(i))
-                {
-                    nc.AddItem(i);
-                }
+                addresses = new[] { address };
+                return true;
             }
             }
 
 
-            return nc;
+            addresses = Array.Empty<IPAddress>();
+            return false;
         }
         }
     }
     }
 }
 }

+ 2 - 3
MediaBrowser.Controller/IServerApplicationHost.cs

@@ -4,7 +4,6 @@
 
 
 using System.Net;
 using System.Net;
 using MediaBrowser.Common;
 using MediaBrowser.Common;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Model.System;
 using MediaBrowser.Model.System;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http;
 
 
@@ -75,10 +74,10 @@ namespace MediaBrowser.Controller
         /// <summary>
         /// <summary>
         /// Gets an URL that can be used to access the API over LAN.
         /// Gets an URL that can be used to access the API over LAN.
         /// </summary>
         /// </summary>
-        /// <param name="hostname">An optional hostname to use.</param>
+        /// <param name="ipAddress">An optional IP address to use.</param>
         /// <param name="allowHttps">A value indicating whether to allow HTTPS.</param>
         /// <param name="allowHttps">A value indicating whether to allow HTTPS.</param>
         /// <returns>The API URL.</returns>
         /// <returns>The API URL.</returns>
-        string GetApiUrlForLocalAccess(IPObject hostname = null, bool allowHttps = true);
+        string GetApiUrlForLocalAccess(IPAddress ipAddress = null, bool allowHttps = true);
 
 
         /// <summary>
         /// <summary>
         /// Gets a local (LAN) URL that can be used to access the API.
         /// Gets a local (LAN) URL that can be used to access the API.

+ 4 - 3
RSSDP/SsdpDevicePublisher.cs

@@ -7,6 +7,7 @@ using System.Net;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
+using Microsoft.AspNetCore.HttpOverrides;
 
 
 namespace Rssdp.Infrastructure
 namespace Rssdp.Infrastructure
 {
 {
@@ -297,9 +298,9 @@ namespace Rssdp.Infrastructure
                     foreach (var device in deviceList)
                     foreach (var device in deviceList)
                     {
                     {
                         var root = device.ToRootDevice();
                         var root = device.ToRootDevice();
-                        var source = new IPNetAddress(root.Address, root.PrefixLength);
-                        var destination = new IPNetAddress(remoteEndPoint.Address, root.PrefixLength);
-                        if (!_sendOnlyMatchedHost || source.NetworkAddress.Equals(destination.NetworkAddress))
+                        var source = new IPData(root.Address, new IPNetwork(root.Address, root.PrefixLength), root.FriendlyName);
+                        var destination = new IPData(remoteEndPoint.Address, new IPNetwork(root.Address, root.PrefixLength));
+                        if (!_sendOnlyMatchedHost || source.Address.Equals(destination.Address))
                         {
                         {
                             SendDeviceSearchResponses(device, remoteEndPoint, receivedOnlocalIpAddress, cancellationToken);
                             SendDeviceSearchResponses(device, remoteEndPoint, receivedOnlocalIpAddress, cancellationToken);
                         }
                         }

+ 0 - 49
tests/Jellyfin.Networking.Tests/IPNetAddressTests.cs

@@ -1,49 +0,0 @@
-using FsCheck;
-using FsCheck.Xunit;
-using MediaBrowser.Common.Net;
-using Xunit;
-
-namespace Jellyfin.Networking.Tests
-{
-    public static class IPNetAddressTests
-    {
-        /// <summary>
-        /// Checks IP address formats.
-        /// </summary>
-        /// <param name="address">IP 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 static void TryParse_ValidIPStrings_True(string address)
-            => Assert.True(IPNetAddress.TryParse(address, out _));
-
-        [Property]
-        public static Property TryParse_IPv4Address_True(IPv4Address address)
-            => IPNetAddress.TryParse(address.Item.ToString(), out _).ToProperty();
-
-        [Property]
-        public static Property TryParse_IPv6Address_True(IPv6Address address)
-            => IPNetAddress.TryParse(address.Item.ToString(), out _).ToProperty();
-
-        /// <summary>
-        /// All should be invalid address strings.
-        /// </summary>
-        /// <param name="address">Invalid address strings.</param>
-        [Theory]
-        [InlineData("256.128.0.0.0.1")]
-        [InlineData("127.0.0.1#")]
-        [InlineData("localhost!")]
-        [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")]
-        [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")]
-        public static void TryParse_InvalidAddressString_False(string address)
-            => Assert.False(IPNetAddress.TryParse(address, out _));
-    }
-}

+ 5 - 5
tests/Jellyfin.Networking.Tests/IPHostTests.cs → tests/Jellyfin.Networking.Tests/NetworkExtensionsTests.cs

@@ -5,7 +5,7 @@ using Xunit;
 
 
 namespace Jellyfin.Networking.Tests
 namespace Jellyfin.Networking.Tests
 {
 {
-    public static class IPHostTests
+    public static class NetworkExtensionsTests
     {
     {
         /// <summary>
         /// <summary>
         /// Checks IP address formats.
         /// Checks IP address formats.
@@ -27,15 +27,15 @@ namespace Jellyfin.Networking.Tests
         [InlineData("192.168.1.2/255.255.255.0")]
         [InlineData("192.168.1.2/255.255.255.0")]
         [InlineData("192.168.1.2/24")]
         [InlineData("192.168.1.2/24")]
         public static void TryParse_ValidHostStrings_True(string address)
         public static void TryParse_ValidHostStrings_True(string address)
-            => Assert.True(IPHost.TryParse(address, out _));
+            => Assert.True(NetworkExtensions.TryParseHost(address, out _, true, true));
 
 
         [Property]
         [Property]
         public static Property TryParse_IPv4Address_True(IPv4Address address)
         public static Property TryParse_IPv4Address_True(IPv4Address address)
-            => IPHost.TryParse(address.Item.ToString(), out _).ToProperty();
+            => NetworkExtensions.TryParseHost(address.Item.ToString(), out _, true, true).ToProperty();
 
 
         [Property]
         [Property]
         public static Property TryParse_IPv6Address_True(IPv6Address address)
         public static Property TryParse_IPv6Address_True(IPv6Address address)
-            => IPHost.TryParse(address.Item.ToString(), out _).ToProperty();
+            => NetworkExtensions.TryParseHost(address.Item.ToString(), out _, true, true).ToProperty();
 
 
         /// <summary>
         /// <summary>
         /// All should be invalid address strings.
         /// All should be invalid address strings.
@@ -48,6 +48,6 @@ namespace Jellyfin.Networking.Tests
         [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")]
         [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")]
         [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")]
         [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")]
         public static void TryParse_InvalidAddressString_False(string address)
         public static void TryParse_InvalidAddressString_False(string address)
-            => Assert.False(IPHost.TryParse(address, out _));
+            => Assert.False(NetworkExtensions.TryParseHost(address, out _, true, true));
     }
     }
 }
 }

+ 36 - 217
tests/Jellyfin.Networking.Tests/NetworkParseTests.cs

@@ -1,10 +1,13 @@
 using System;
 using System;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
+using System.Globalization;
+using System.Linq;
 using System.Net;
 using System.Net;
 using Jellyfin.Networking.Configuration;
 using Jellyfin.Networking.Configuration;
 using Jellyfin.Networking.Manager;
 using Jellyfin.Networking.Manager;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
+using Microsoft.AspNetCore.HttpOverrides;
 using Microsoft.Extensions.Logging.Abstractions;
 using Microsoft.Extensions.Logging.Abstractions;
 using Moq;
 using Moq;
 using Xunit;
 using Xunit;
@@ -46,6 +49,8 @@ namespace Jellyfin.Networking.Tests
             {
             {
                 EnableIPV6 = true,
                 EnableIPV6 = true,
                 EnableIPV4 = true,
                 EnableIPV4 = true,
+                IgnoreVirtualInterfaces = true,
+                VirtualInterfaceNames = "veth",
                 LocalNetworkSubnets = lan?.Split(';') ?? throw new ArgumentNullException(nameof(lan))
                 LocalNetworkSubnets = lan?.Split(';') ?? throw new ArgumentNullException(nameof(lan))
             };
             };
 
 
@@ -53,135 +58,7 @@ namespace Jellyfin.Networking.Tests
             using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
             using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
             NetworkManager.MockNetworkSettings = string.Empty;
             NetworkManager.MockNetworkSettings = string.Empty;
 
 
-            Assert.Equal(nm.GetInternalBindAddresses().AsString(), value);
-        }
-
-        /// <summary>
-        /// Test collection parsing.
-        /// </summary>
-        /// <param name="settings">Collection to parse.</param>
-        /// <param name="result1">Included addresses from the collection.</param>
-        /// <param name="result2">Included IP4 addresses from the collection.</param>
-        /// <param name="result3">Excluded addresses from the collection.</param>
-        /// <param name="result4">Excluded IP4 addresses from the collection.</param>
-        /// <param name="result5">Network addresses of the collection.</param>
-        [Theory]
-        [InlineData(
-            "127.0.0.1#",
-            "[]",
-            "[]",
-            "[]",
-            "[]",
-            "[]")]
-        [InlineData(
-            "!127.0.0.1",
-            "[]",
-            "[]",
-            "[127.0.0.1/32]",
-            "[127.0.0.1/32]",
-            "[]")]
-        [InlineData(
-            "",
-            "[]",
-            "[]",
-            "[]",
-            "[]",
-            "[]")]
-        [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,::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,::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]",
-            "[]",
-            "[]",
-            "[192.158.0.0/16,192.0.0.0/8]")]
-        public void TestCollections(string settings, string result1, string result2, string result3, string result4, string result5)
-        {
-            if (settings == null)
-            {
-                throw new ArgumentNullException(nameof(settings));
-            }
-
-            var conf = new NetworkConfiguration()
-            {
-                EnableIPV6 = true,
-                EnableIPV4 = true,
-            };
-
-            using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
-
-            // Test included.
-            Collection<IPObject> nc = nm.CreateIPCollection(settings.Split(','), false);
-            Assert.Equal(nc.AsString(), result1);
-
-            // Test excluded.
-            nc = nm.CreateIPCollection(settings.Split(','), true);
-            Assert.Equal(nc.AsString(), result3);
-
-            conf.EnableIPV6 = false;
-            nm.UpdateSettings(conf);
-
-            // Test IP4 included.
-            nc = nm.CreateIPCollection(settings.Split(','), false);
-            Assert.Equal(nc.AsString(), result2);
-
-            // Test IP4 excluded.
-            nc = nm.CreateIPCollection(settings.Split(','), true);
-            Assert.Equal(nc.AsString(), result4);
-
-            conf.EnableIPV6 = true;
-            nm.UpdateSettings(conf);
-
-            // Test network addresses of collection.
-            nc = nm.CreateIPCollection(settings.Split(','), false);
-            nc = nc.AsNetworks();
-            Assert.Equal(nc.AsString(), result5);
-        }
-
-        /// <summary>
-        /// Union two collections.
-        /// </summary>
-        /// <param name="settings">Source.</param>
-        /// <param name="compare">Destination.</param>
-        /// <param name="result">Result.</param>
-        [Theory]
-        [InlineData("127.0.0.1", "fd23:184f:2029:0:3139:7386:67d7:d517/64,fd23:184f:2029:0:c0f0:8a8a:7605:fffa/128,fe80::3139:7386:67d7:d517%16/64,192.168.1.208/24,::1/128,127.0.0.1/8", "[127.0.0.1/32]")]
-        [InlineData("127.0.0.1", "127.0.0.1/8", "[127.0.0.1/32]")]
-        public void UnionCheck(string settings, string compare, string result)
-        {
-            if (settings == null)
-            {
-                throw new ArgumentNullException(nameof(settings));
-            }
-
-            if (compare == null)
-            {
-                throw new ArgumentNullException(nameof(compare));
-            }
-
-            if (result == null)
-            {
-                throw new ArgumentNullException(nameof(result));
-            }
-
-            var conf = new NetworkConfiguration()
-            {
-                EnableIPV6 = true,
-                EnableIPV4 = true,
-            };
-
-            using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
-
-            Collection<IPObject> nc1 = nm.CreateIPCollection(settings.Split(','), false);
-            Collection<IPObject> nc2 = nm.CreateIPCollection(compare.Split(','), false);
-
-            Assert.Equal(nc1.ThatAreContainedInNetworks(nc2).AsString(), result);
+            Assert.Equal(value, "[" + String.Join(",", nm.GetInternalBindAddresses().Select(x => x.Address + "/" + x.Subnet.PrefixLength)) + "]");
         }
         }
 
 
         [Theory]
         [Theory]
@@ -194,8 +71,11 @@ namespace Jellyfin.Networking.Tests
         [InlineData("127.0.0.1/8", "127.0.0.1")]
         [InlineData("127.0.0.1/8", "127.0.0.1")]
         public void IpV4SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress)
         public void IpV4SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress)
         {
         {
-            var ipAddressObj = IPNetAddress.Parse(netMask);
-            Assert.True(ipAddressObj.Contains(IPAddress.Parse(ipAddress)));
+            var split = netMask.Split("/");
+            var mask = int.Parse(split[1], CultureInfo.InvariantCulture);
+            var ipa = IPAddress.Parse(split[0]);
+            var ipn = new IPNetwork(ipa, mask);
+            Assert.True(ipn.Contains(IPAddress.Parse(ipAddress)));
         }
         }
 
 
         [Theory]
         [Theory]
@@ -207,8 +87,11 @@ namespace Jellyfin.Networking.Tests
         [InlineData("10.128.240.50/30", "10.127.240.51")]
         [InlineData("10.128.240.50/30", "10.127.240.51")]
         public void IpV4SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress)
         public void IpV4SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress)
         {
         {
-            var ipAddressObj = IPNetAddress.Parse(netMask);
-            Assert.False(ipAddressObj.Contains(IPAddress.Parse(ipAddress)));
+            var split = netMask.Split("/");
+            var mask = int.Parse(split[1], CultureInfo.InvariantCulture);
+            var ipa = IPAddress.Parse(split[0]);
+            var ipn = new IPNetwork(ipa, mask);
+            Assert.False(ipn.Contains(IPAddress.Parse(ipAddress)));
         }
         }
 
 
         [Theory]
         [Theory]
@@ -219,8 +102,11 @@ namespace Jellyfin.Networking.Tests
         [InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")]
         [InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")]
         public void IpV6SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress)
         public void IpV6SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress)
         {
         {
-            var ipAddressObj = IPNetAddress.Parse(netMask);
-            Assert.True(ipAddressObj.Contains(IPAddress.Parse(ipAddress)));
+            var split = netMask.Split("/");
+            var mask = int.Parse(split[1], CultureInfo.InvariantCulture);
+            var ipa = IPAddress.Parse(split[0]);
+            var ipn = new IPNetwork(ipa, mask);
+            Assert.True(ipn.Contains(IPAddress.Parse(ipAddress)));
         }
         }
 
 
         [Theory]
         [Theory]
@@ -231,86 +117,18 @@ namespace Jellyfin.Networking.Tests
         [InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0001")]
         [InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0001")]
         public void IpV6SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress)
         public void IpV6SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress)
         {
         {
-            var ipAddressObj = IPNetAddress.Parse(netMask);
-            Assert.False(ipAddressObj.Contains(IPAddress.Parse(ipAddress)));
-        }
-
-        [Theory]
-        [InlineData("10.0.0.0/255.0.0.0", "10.10.10.1/32")]
-        [InlineData("10.0.0.0/8", "10.10.10.1/32")]
-        [InlineData("10.0.0.0/255.0.0.0", "10.10.10.1")]
-
-        [InlineData("10.10.0.0/255.255.0.0", "10.10.10.1/32")]
-        [InlineData("10.10.0.0/16", "10.10.10.1/32")]
-        [InlineData("10.10.0.0/255.255.0.0", "10.10.10.1")]
-
-        [InlineData("10.10.10.0/255.255.255.0", "10.10.10.1/32")]
-        [InlineData("10.10.10.0/24", "10.10.10.1/32")]
-        [InlineData("10.10.10.0/255.255.255.0", "10.10.10.1")]
-
-        public void TestSubnetContains(string network, string ip)
-        {
-            Assert.True(IPNetAddress.TryParse(network, out var networkObj));
-            Assert.True(IPNetAddress.TryParse(ip, out var ipObj));
-            Assert.True(networkObj.Contains(ipObj));
-        }
-
-        [Theory]
-        [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "172.168.1.2/24", "172.168.1.2/24")]
-        [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "172.168.1.2/24, 10.10.10.1", "172.168.1.2/24,10.10.10.1/24")]
-        [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "192.168.1.2/255.255.255.0, 10.10.10.1", "192.168.1.2/24,10.10.10.1/24")]
-        [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "192.168.1.2/24, 100.10.10.1", "192.168.1.2/24")]
-        [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "194.168.1.2/24, 100.10.10.1", "")]
-
-        public void TestCollectionEquality(string source, string dest, string result)
-        {
-            if (source == null)
-            {
-                throw new ArgumentNullException(nameof(source));
-            }
-
-            if (dest == null)
-            {
-                throw new ArgumentNullException(nameof(dest));
-            }
-
-            if (result == null)
-            {
-                throw new ArgumentNullException(nameof(result));
-            }
-
-            var conf = new NetworkConfiguration()
-            {
-                EnableIPV6 = true,
-                EnableIPV4 = true
-            };
-
-            using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
-
-            // Test included, IP6.
-            Collection<IPObject> ncSource = nm.CreateIPCollection(source.Split(','));
-            Collection<IPObject> ncDest = nm.CreateIPCollection(dest.Split(','));
-            Collection<IPObject> ncResult = ncSource.ThatAreContainedInNetworks(ncDest);
-            Collection<IPObject> resultCollection = nm.CreateIPCollection(result.Split(','));
-            Assert.True(ncResult.Compare(resultCollection));
-        }
-
-        [Theory]
-        [InlineData("10.1.1.1/32", "10.1.1.1")]
-        [InlineData("192.168.1.254/32", "192.168.1.254/255.255.255.255")]
-
-        public void TestEquals(string source, string dest)
-        {
-            Assert.True(IPNetAddress.Parse(source).Equals(IPNetAddress.Parse(dest)));
-            Assert.True(IPNetAddress.Parse(dest).Equals(IPNetAddress.Parse(source)));
+            var split = netMask.Split("/");
+            var mask = int.Parse(split[1], CultureInfo.InvariantCulture);
+            var ipa = IPAddress.Parse(split[0]);
+            var ipn = new IPNetwork(ipa, mask);
+            Assert.False(ipn.Contains(IPAddress.Parse(ipAddress)));
         }
         }
 
 
         [Theory]
         [Theory]
-
         // Testing bind interfaces.
         // Testing bind interfaces.
         // On my system eth16 is internal, eth11 external (Windows defines the indexes).
         // On my system eth16 is internal, eth11 external (Windows defines the indexes).
         //
         //
-        // This test is to replicate how DNLA requests work throughout the system.
+        // This test is to replicate how DLNA requests work throughout the system.
 
 
         // User on internal network, we're bound internal and external - so result is internal.
         // User on internal network, we're bound internal and external - so result is internal.
         [InlineData("192.168.1.1", "eth16,eth11", false, "eth16")]
         [InlineData("192.168.1.1", "eth16,eth11", false, "eth16")]
@@ -357,14 +175,14 @@ namespace Jellyfin.Networking.Tests
             using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
             using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
             NetworkManager.MockNetworkSettings = string.Empty;
             NetworkManager.MockNetworkSettings = string.Empty;
 
 
-            _ = nm.TryParseInterface(result, out Collection<IPObject>? resultObj);
+            _ = nm.TryParseInterface(result, out Collection<IPData>? resultObj);
 
 
             // Check to see if dns resolution is working. If not, skip test.
             // Check to see if dns resolution is working. If not, skip test.
-            _ = IPHost.TryParse(source, out var host);
+            _ = NetworkExtensions.TryParseHost(source, out var host);
 
 
-            if (resultObj != null && host?.HasAddress == true)
+            if (resultObj != null && host.Length > 0)
             {
             {
-                result = ((IPNetAddress)resultObj[0]).ToString(true);
+                result = resultObj.First().Address.ToString();
                 var intf = nm.GetBindInterface(source, out _);
                 var intf = nm.GetBindInterface(source, out _);
 
 
                 Assert.Equal(intf, result);
                 Assert.Equal(intf, result);
@@ -394,7 +212,7 @@ namespace Jellyfin.Networking.Tests
         [InlineData("jellyfin.org", "192.168.1.0/24", "eth16", false, "0.0.0.0=http://helloworld.com", "http://helloworld.com")]
         [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.
         // User on external network, no binding - so result is the 1st external which is overriden.
-        [InlineData("jellyfin.org", "192.168.1.0/24", "", false, "0.0.0.0 = http://helloworld.com", "http://helloworld.com")]
+        [InlineData("jellyfin.org", "192.168.1.0/24", "", false, "0.0.0.0=http://helloworld.com", "http://helloworld.com")]
 
 
         // User assumed to be internal, no binding - so result is the 1st internal.
         // User assumed to be internal, no binding - so result is the 1st internal.
         [InlineData("", "192.168.1.0/24", "", false, "0.0.0.0=http://helloworld.com", "eth16")]
         [InlineData("", "192.168.1.0/24", "", false, "0.0.0.0=http://helloworld.com", "eth16")]
@@ -426,15 +244,15 @@ namespace Jellyfin.Networking.Tests
             using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
             using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
             NetworkManager.MockNetworkSettings = string.Empty;
             NetworkManager.MockNetworkSettings = string.Empty;
 
 
-            if (nm.TryParseInterface(result, out Collection<IPObject>? resultObj) && resultObj != null)
+            if (nm.TryParseInterface(result, out Collection<IPData>? resultObj) && resultObj != null)
             {
             {
                 // Parse out IPAddresses so we can do a string comparison. (Ignore subnet masks).
                 // Parse out IPAddresses so we can do a string comparison. (Ignore subnet masks).
-                result = ((IPNetAddress)resultObj[0]).ToString(true);
+                result = resultObj.First().Address.ToString();
             }
             }
 
 
             var intf = nm.GetBindInterface(source, out int? _);
             var intf = nm.GetBindInterface(source, out int? _);
 
 
-            Assert.Equal(intf, result);
+            Assert.Equal(result, intf);
         }
         }
 
 
         [Theory]
         [Theory]
@@ -461,6 +279,7 @@ namespace Jellyfin.Networking.Tests
         [InlineData("185.10.10.10", "79.2.3.4", false)]
         [InlineData("185.10.10.10", "79.2.3.4", false)]
         [InlineData("185.10.10.10", "185.10.10.10", true)]
         [InlineData("185.10.10.10", "185.10.10.10", true)]
         [InlineData("", "100.100.100.100", false)]
         [InlineData("", "100.100.100.100", false)]
+
         public void HasRemoteAccess_GivenBlacklist_BlacklistTheIps(string addresses, string remoteIp, bool denied)
         public void HasRemoteAccess_GivenBlacklist_BlacklistTheIps(string addresses, string remoteIp, bool denied)
         {
         {
             // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely.
             // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely.

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä