From f536e08e14cfc82a501e927562c7c6209cef7add Mon Sep 17 00:00:00 2001 From: gnattu Date: Tue, 4 Feb 2025 07:07:21 +0800 Subject: [PATCH 1/3] Fix subnet contains check We are still using `Subnet.Contains` a lot but that does not handle IPv4 mapped to IPv6 addresses at all. It was partially fixed by #12094 in local network checking, but it may not always happen on LAN. Also make all local network checking to use IsInLocalNetwork method instead of just performing `Subnet.Contains` which is not accurate. Filter out all link-local addresses for external interface matching. --- .../Manager/NetworkManager.cs | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/Jellyfin.Networking/Manager/NetworkManager.cs b/src/Jellyfin.Networking/Manager/NetworkManager.cs index e7f097466d..ee98488d4a 100644 --- a/src/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/src/Jellyfin.Networking/Manager/NetworkManager.cs @@ -689,10 +689,10 @@ public class NetworkManager : INetworkManager, IDisposable { // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely. // If left blank, all remote addresses will be allowed. - if (_remoteAddressFilter.Any() && !_lanSubnets.Any(x => x.Contains(remoteIP))) + if (_remoteAddressFilter.Any() && !IsInLocalNetwork(remoteIP)) { // remoteAddressFilter is a whitelist or blacklist. - var matches = _remoteAddressFilter.Count(remoteNetwork => remoteNetwork.Contains(remoteIP)); + var matches = _remoteAddressFilter.Count(remoteNetwork => SubNetContainsAddress(remoteNetwork, remoteIP)); if ((!config.IsRemoteIPFilterBlacklist && matches > 0) || (config.IsRemoteIPFilterBlacklist && matches == 0)) { @@ -816,7 +816,7 @@ public class NetworkManager : INetworkManager, IDisposable _logger.LogWarning("IPv4 is disabled in Jellyfin, but enabled in the OS. This may affect how the interface is selected."); } - bool isExternal = !_lanSubnets.Any(network => network.Contains(source)); + bool isExternal = !IsInLocalNetwork(source); _logger.LogDebug("Trying to get bind address for source {Source} - External: {IsExternal}", source, isExternal); if (!skipOverrides && MatchesPublishedServerUrl(source, isExternal, out result)) @@ -863,7 +863,7 @@ public class NetworkManager : INetworkManager, IDisposable // (For systems with multiple internal network cards, and multiple subnets) foreach (var intf in availableInterfaces) { - if (intf.Subnet.Contains(source)) + if (SubNetContainsAddress(intf.Subnet, source)) { result = NetworkUtils.FormatIPString(intf.Address); _logger.LogDebug("{Source}: Found interface with matching subnet, using it as bind address: {Result}", source, result); @@ -891,21 +891,11 @@ public class NetworkManager : INetworkManager, IDisposable { if (NetworkUtils.TryParseToSubnet(address, out var subnet)) { - return IPAddress.IsLoopback(subnet.Prefix) || (_lanSubnets.Any(x => x.Contains(subnet.Prefix)) && !_excludedSubnets.Any(x => x.Contains(subnet.Prefix))); + return IsInLocalNetwork(subnet.Prefix); } - if (NetworkUtils.TryParseHost(address, out var addresses, IsIPv4Enabled, IsIPv6Enabled)) - { - foreach (var ept in addresses) - { - if (IPAddress.IsLoopback(ept) || (_lanSubnets.Any(x => x.Contains(ept)) && !_excludedSubnets.Any(x => x.Contains(ept)))) - { - return true; - } - } - } - - return false; + return NetworkUtils.TryParseHost(address, out var addresses, IsIPv4Enabled, IsIPv6Enabled) + && addresses.Any(IsInLocalNetwork); } /// @@ -919,6 +909,19 @@ public class NetworkManager : INetworkManager, IDisposable return NetworkConstants.IPv4RFC3927LinkLocal.Contains(address) || address.IsIPv6LinkLocal; } + private static bool SubNetContainsAddress(IPNetwork network, IPAddress address) + { + ArgumentNullException.ThrowIfNull(address); + ArgumentNullException.ThrowIfNull(network); + + if (address.IsIPv4MappedToIPv6) + { + address = address.MapToIPv4(); + } + + return network.Contains(address); + } + /// public bool IsInLocalNetwork(IPAddress address) { @@ -940,6 +943,11 @@ public class NetworkManager : INetworkManager, IDisposable return CheckIfLanAndNotExcluded(address); } + /// + /// Check if the address is in the LAN and not excluded. + /// + /// The IP address to check. The caller should make sure this is not an IPv4MappedToIPv6 address. + /// Boolean indicates whether the address is in LAN. private bool CheckIfLanAndNotExcluded(IPAddress address) { foreach (var lanSubnet in _lanSubnets) @@ -979,7 +987,7 @@ public class NetworkManager : INetworkManager, IDisposable { // Only use matching internal subnets // Prefer more specific (bigger subnet prefix) overrides - validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsInternalOverride && x.Data.Subnet.Contains(source)) + validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsInternalOverride && SubNetContainsAddress(x.Data.Subnet, source)) .OrderByDescending(x => x.Data.Subnet.PrefixLength) .ToList(); } @@ -987,7 +995,7 @@ public class NetworkManager : INetworkManager, IDisposable { // Only use matching external subnets // Prefer more specific (bigger subnet prefix) overrides - validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsExternalOverride && x.Data.Subnet.Contains(source)) + validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsExternalOverride && SubNetContainsAddress(x.Data.Subnet, source)) .OrderByDescending(x => x.Data.Subnet.PrefixLength) .ToList(); } @@ -995,7 +1003,7 @@ public class NetworkManager : INetworkManager, IDisposable foreach (var data in validPublishedServerUrls) { // Get interface matching override subnet - var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(x => data.Data.Subnet.Contains(x.Address)); + var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(x => SubNetContainsAddress(data.Data.Subnet, x.Address)); if (intf?.Address is not null || (data.Data.AddressFamily == AddressFamily.InterNetwork && data.Data.Address.Equals(IPAddress.Any)) @@ -1058,6 +1066,7 @@ public class NetworkManager : INetworkManager, IDisposable if (isInExternalSubnet) { var externalInterfaces = _interfaces.Where(x => !IsInLocalNetwork(x.Address)) + .Where(x => !IsLinkLocalAddress(x.Address)) .OrderBy(x => x.Index) .ToList(); if (externalInterfaces.Count > 0) @@ -1065,7 +1074,7 @@ public class NetworkManager : INetworkManager, IDisposable // Check to see if any of the external bind interfaces are in the same subnet as the source. // If none exists, this will select the first external interface if there is one. bindAddress = externalInterfaces - .OrderByDescending(x => x.Subnet.Contains(source)) + .OrderByDescending(x => SubNetContainsAddress(x.Subnet, source)) .ThenByDescending(x => x.Subnet.PrefixLength) .ThenBy(x => x.Index) .Select(x => x.Address) @@ -1083,7 +1092,7 @@ public class NetworkManager : INetworkManager, IDisposable // Check to see if any of the internal bind interfaces are in the same subnet as the source. // If none exists, this will select the first internal interface if there is one. bindAddress = _interfaces.Where(x => IsInLocalNetwork(x.Address)) - .OrderByDescending(x => x.Subnet.Contains(source)) + .OrderByDescending(x => SubNetContainsAddress(x.Subnet, source)) .ThenByDescending(x => x.Subnet.PrefixLength) .ThenBy(x => x.Index) .Select(x => x.Address) @@ -1127,7 +1136,7 @@ public class NetworkManager : INetworkManager, IDisposable // (For systems with multiple network cards and/or multiple subnets) foreach (var intf in extResult) { - if (intf.Subnet.Contains(source)) + if (SubNetContainsAddress(intf.Subnet, source)) { result = NetworkUtils.FormatIPString(intf.Address); _logger.LogDebug("{Source}: Found external interface with matching subnet, using it as bind address: {Result}", source, result); From 070d04c1b26b61f1bc6dca15a965cdd8c37dfa0d Mon Sep 17 00:00:00 2001 From: gnattu Date: Wed, 5 Feb 2025 08:04:43 +0800 Subject: [PATCH 2/3] Typo Co-authored-by: Cody Robibero --- src/Jellyfin.Networking/Manager/NetworkManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Jellyfin.Networking/Manager/NetworkManager.cs b/src/Jellyfin.Networking/Manager/NetworkManager.cs index ee98488d4a..1928401dfd 100644 --- a/src/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/src/Jellyfin.Networking/Manager/NetworkManager.cs @@ -909,7 +909,7 @@ public class NetworkManager : INetworkManager, IDisposable return NetworkConstants.IPv4RFC3927LinkLocal.Contains(address) || address.IsIPv6LinkLocal; } - private static bool SubNetContainsAddress(IPNetwork network, IPAddress address) + private static bool SubnetContainsAddress(IPNetwork network, IPAddress address) { ArgumentNullException.ThrowIfNull(address); ArgumentNullException.ThrowIfNull(network); From 99006c370f2b3b0b72447671bf24c8b09513ba88 Mon Sep 17 00:00:00 2001 From: gnattu Date: Mon, 10 Feb 2025 05:20:34 +0800 Subject: [PATCH 3/3] Fix more typo --- .../Manager/NetworkManager.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Jellyfin.Networking/Manager/NetworkManager.cs b/src/Jellyfin.Networking/Manager/NetworkManager.cs index 1928401dfd..aa6c6af0c3 100644 --- a/src/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/src/Jellyfin.Networking/Manager/NetworkManager.cs @@ -692,7 +692,7 @@ public class NetworkManager : INetworkManager, IDisposable if (_remoteAddressFilter.Any() && !IsInLocalNetwork(remoteIP)) { // remoteAddressFilter is a whitelist or blacklist. - var matches = _remoteAddressFilter.Count(remoteNetwork => SubNetContainsAddress(remoteNetwork, remoteIP)); + var matches = _remoteAddressFilter.Count(remoteNetwork => SubnetContainsAddress(remoteNetwork, remoteIP)); if ((!config.IsRemoteIPFilterBlacklist && matches > 0) || (config.IsRemoteIPFilterBlacklist && matches == 0)) { @@ -863,7 +863,7 @@ public class NetworkManager : INetworkManager, IDisposable // (For systems with multiple internal network cards, and multiple subnets) foreach (var intf in availableInterfaces) { - if (SubNetContainsAddress(intf.Subnet, source)) + if (SubnetContainsAddress(intf.Subnet, source)) { result = NetworkUtils.FormatIPString(intf.Address); _logger.LogDebug("{Source}: Found interface with matching subnet, using it as bind address: {Result}", source, result); @@ -987,7 +987,7 @@ public class NetworkManager : INetworkManager, IDisposable { // Only use matching internal subnets // Prefer more specific (bigger subnet prefix) overrides - validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsInternalOverride && SubNetContainsAddress(x.Data.Subnet, source)) + validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsInternalOverride && SubnetContainsAddress(x.Data.Subnet, source)) .OrderByDescending(x => x.Data.Subnet.PrefixLength) .ToList(); } @@ -995,7 +995,7 @@ public class NetworkManager : INetworkManager, IDisposable { // Only use matching external subnets // Prefer more specific (bigger subnet prefix) overrides - validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsExternalOverride && SubNetContainsAddress(x.Data.Subnet, source)) + validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsExternalOverride && SubnetContainsAddress(x.Data.Subnet, source)) .OrderByDescending(x => x.Data.Subnet.PrefixLength) .ToList(); } @@ -1003,7 +1003,7 @@ public class NetworkManager : INetworkManager, IDisposable foreach (var data in validPublishedServerUrls) { // Get interface matching override subnet - var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(x => SubNetContainsAddress(data.Data.Subnet, x.Address)); + var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(x => SubnetContainsAddress(data.Data.Subnet, x.Address)); if (intf?.Address is not null || (data.Data.AddressFamily == AddressFamily.InterNetwork && data.Data.Address.Equals(IPAddress.Any)) @@ -1074,7 +1074,7 @@ public class NetworkManager : INetworkManager, IDisposable // Check to see if any of the external bind interfaces are in the same subnet as the source. // If none exists, this will select the first external interface if there is one. bindAddress = externalInterfaces - .OrderByDescending(x => SubNetContainsAddress(x.Subnet, source)) + .OrderByDescending(x => SubnetContainsAddress(x.Subnet, source)) .ThenByDescending(x => x.Subnet.PrefixLength) .ThenBy(x => x.Index) .Select(x => x.Address) @@ -1092,7 +1092,7 @@ public class NetworkManager : INetworkManager, IDisposable // Check to see if any of the internal bind interfaces are in the same subnet as the source. // If none exists, this will select the first internal interface if there is one. bindAddress = _interfaces.Where(x => IsInLocalNetwork(x.Address)) - .OrderByDescending(x => SubNetContainsAddress(x.Subnet, source)) + .OrderByDescending(x => SubnetContainsAddress(x.Subnet, source)) .ThenByDescending(x => x.Subnet.PrefixLength) .ThenBy(x => x.Index) .Select(x => x.Address) @@ -1136,7 +1136,7 @@ public class NetworkManager : INetworkManager, IDisposable // (For systems with multiple network cards and/or multiple subnets) foreach (var intf in extResult) { - if (SubNetContainsAddress(intf.Subnet, source)) + if (SubnetContainsAddress(intf.Subnet, source)) { result = NetworkUtils.FormatIPString(intf.Address); _logger.LogDebug("{Source}: Found external interface with matching subnet, using it as bind address: {Result}", source, result);