diff options
| author | Tim Eisele <Ghost_of_Stone@web.de> | 2026-05-10 20:34:26 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-10 20:34:26 +0200 |
| commit | f24709f11c82bb85b70f073c89d3d21c30b1cde5 (patch) | |
| tree | 710ef955dc54141c87ad7c2797997ca6a39f7524 | |
| parent | 4f238ca9b3f57bf54dfa332d8b6f4d456651b907 (diff) | |
Print warning on invalid Subnets in Network/Proxy configuration (#16793)
Print warning on invalid Subnets in Network/Proxy configuration
| -rw-r--r-- | MediaBrowser.Common/Net/NetworkUtils.cs | 37 | ||||
| -rw-r--r-- | src/Jellyfin.Networking/Manager/NetworkManager.cs | 4 | ||||
| -rw-r--r-- | tests/Jellyfin.Networking.Tests/NetworkParseTests.cs | 38 |
3 files changed, 76 insertions, 3 deletions
diff --git a/MediaBrowser.Common/Net/NetworkUtils.cs b/MediaBrowser.Common/Net/NetworkUtils.cs index 5c854b39d5..71539b8b78 100644 --- a/MediaBrowser.Common/Net/NetworkUtils.cs +++ b/MediaBrowser.Common/Net/NetworkUtils.cs @@ -7,6 +7,7 @@ using System.Net.Sockets; using System.Text.RegularExpressions; using Jellyfin.Extensions; using MediaBrowser.Model.Net; +using Microsoft.Extensions.Logging; namespace MediaBrowser.Common.Net; @@ -166,8 +167,9 @@ public static partial class NetworkUtils /// <param name="values">Input string array to be parsed.</param> /// <param name="result">Collection of <see cref="IPNetwork"/>.</param> /// <param name="negated">Boolean signaling if negated or not negated values should be parsed.</param> + /// <param name="logger">Optional logger used to warn about entries that fail to parse.</param> /// <returns><c>True</c> if parsing was successful.</returns> - public static bool TryParseToSubnets(string[] values, [NotNullWhen(true)] out IReadOnlyList<IPData>? result, bool negated = false) + public static bool TryParseToSubnets(string[] values, [NotNullWhen(true)] out IReadOnlyList<IPData>? result, bool negated = false, ILogger? logger = null) { if (values is null || values.Length == 0) { @@ -182,12 +184,45 @@ public static partial class NetworkUtils { (tmpResult ??= new()).Add(innerResult); } + else + { + LogInvalidSubnet(logger, values[a]); + } } result = tmpResult; return result is not null; } + private static void LogInvalidSubnet(ILogger? logger, string value) + { + if (logger is null) + { + return; + } + + var trimmed = value.AsSpan().Trim(); + if (trimmed.StartsWith('!')) + { + trimmed = trimmed[1..]; + } + + var slash = trimmed.IndexOf('/'); + if (slash != -1 + && trimmed.Contains(':') + && trimmed.IndexOf("::", StringComparison.Ordinal) == -1) + { + logger.LogWarning( + "Invalid IPv6 subnet '{Subnet}': IPv6 prefix-only notation is not supported. Use the full notation including '::' (e.g. '{Example}::/{Prefix}').", + value, + trimmed[..slash].ToString(), + trimmed[(slash + 1)..].ToString()); + return; + } + + logger.LogWarning("Invalid subnet '{Subnet}' will be ignored.", value); + } + /// <summary> /// Try parsing a string into an <see cref="IPData"/>, respecting exclusions. /// Inputs without a subnet mask will be represented as <see cref="IPData"/> with a single IP. diff --git a/src/Jellyfin.Networking/Manager/NetworkManager.cs b/src/Jellyfin.Networking/Manager/NetworkManager.cs index 6a8a91fa51..0fe2fc43ad 100644 --- a/src/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/src/Jellyfin.Networking/Manager/NetworkManager.cs @@ -316,7 +316,7 @@ public class NetworkManager : INetworkManager, IDisposable var subnets = config.LocalNetworkSubnets; // If no LAN addresses are specified, all private subnets and Loopback are deemed to be the LAN - if (!NetworkUtils.TryParseToSubnets(subnets, out var lanSubnets, false) || lanSubnets.Count == 0) + if (!NetworkUtils.TryParseToSubnets(subnets, out var lanSubnets, false, _logger) || lanSubnets.Count == 0) { _logger.LogDebug("Using LAN interface addresses as user provided no LAN details."); @@ -343,7 +343,7 @@ public class NetworkManager : INetworkManager, IDisposable _lanSubnets = lanSubnets.Select(x => x.Subnet).ToArray(); } - _excludedSubnets = NetworkUtils.TryParseToSubnets(subnets, out var excludedSubnets, true) + _excludedSubnets = NetworkUtils.TryParseToSubnets(subnets, out var excludedSubnets, true, _logger) ? excludedSubnets.Select(x => x.Subnet).ToArray() : Array.Empty<IPNetwork>(); } diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index b63009d6a5..66eec077dc 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -7,6 +7,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Model.Net; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Moq; using Xunit; @@ -94,10 +95,47 @@ namespace Jellyfin.Networking.Tests [InlineData("256.128.0.0.0.1")] [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")] [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")] + [InlineData("fd23:184f:2029:0100/56")] public static void TryParseInvalidIPStringsFalse(string address) => Assert.False(NetworkUtils.TryParseToSubnet(address, out _)); /// <summary> + /// Verifies that <see cref="NetworkUtils.TryParseToSubnets"/> emits a targeted warning + /// for IPv6 prefix-only notation and a generic warning for other malformed entries. + /// </summary> + [Fact] + public static void TryParseToSubnets_InvalidEntries_LogsWarnings() + { + var logger = new Mock<ILogger>(); + + var values = new[] { "10.0.0.0/8", "fd23:184f:2029:0100/56", "not-an-address" }; + Assert.True(NetworkUtils.TryParseToSubnets(values, out var result, false, logger.Object)); + Assert.NotNull(result); + Assert.Single(result); + + // IPv6 prefix-only notation should produce a specific, actionable warning. + logger.Verify( + l => l.Log( + LogLevel.Warning, + It.IsAny<EventId>(), + It.Is<It.IsAnyType>((state, _) => state.ToString()!.Contains("IPv6 prefix-only", StringComparison.Ordinal) + && state.ToString()!.Contains("fd23:184f:2029:0100/56", StringComparison.Ordinal)), + It.IsAny<Exception>(), + It.IsAny<Func<It.IsAnyType, Exception?, string>>()), + Times.Once); + + // Other malformed entries should still produce a generic warning. + logger.Verify( + l => l.Log( + LogLevel.Warning, + It.IsAny<EventId>(), + It.Is<It.IsAnyType>((state, _) => state.ToString()!.Contains("not-an-address", StringComparison.Ordinal)), + It.IsAny<Exception>(), + It.IsAny<Func<It.IsAnyType, Exception?, string>>()), + Times.Once); + } + + /// <summary> /// Checks if IPv4 address is within a defined subnet. /// </summary> /// <param name="netMask">Network mask.</param> |
