Apply review suggestions

This commit is contained in:
Shadowghost 2023-02-15 22:34:44 +01:00
parent 1cc7572445
commit 4eba16c672
10 changed files with 117 additions and 118 deletions

View File

@ -102,7 +102,10 @@ namespace Emby.Server.Implementations.EntryPoints
_udpServers.Add(new UdpServer(_logger, _appHost, _config, System.Net.IPAddress.Any, PortNumber)); _udpServers.Add(new UdpServer(_logger, _appHost, _config, System.Net.IPAddress.Any, PortNumber));
} }
_udpServers.ForEach(u => u.Start(_cancellationTokenSource.Token)); foreach (var server in _udpServers)
{
server.Start(_cancellationTokenSource.Token);
}
} }
catch (SocketException ex) catch (SocketException ex)
{ {

View File

@ -115,12 +115,12 @@ namespace Jellyfin.Networking.Configuration
/// <summary> /// <summary>
/// Gets or sets a value indicating whether IPv6 is enabled or not. /// 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;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether IPv6 is enabled or not. /// 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 access outside of the LAN is permitted. /// Gets or sets a value indicating whether access outside of the LAN is permitted.

View File

@ -14,7 +14,7 @@ namespace Jellyfin.Networking.Configuration
/// <returns>The <see cref="NetworkConfiguration"/>.</returns> /// <returns>The <see cref="NetworkConfiguration"/>.</returns>
public static NetworkConfiguration GetNetworkConfiguration(this IConfigurationManager config) public static NetworkConfiguration GetNetworkConfiguration(this IConfigurationManager config)
{ {
return config.GetConfiguration<NetworkConfiguration>("network"); return config.GetConfiguration<NetworkConfiguration>(NetworkConfigurationStore.StoreKey);
} }
} }
} }

View File

@ -111,12 +111,12 @@ namespace Jellyfin.Networking.Manager
/// <summary> /// <summary>
/// Gets a value indicating whether IP4 is enabled. /// Gets a value indicating whether IP4 is enabled.
/// </summary> /// </summary>
public bool IsIpv4Enabled => _configurationManager.GetNetworkConfiguration().EnableIPV4; public bool IsIPv4Enabled => _configurationManager.GetNetworkConfiguration().EnableIPv4;
/// <summary> /// <summary>
/// Gets a value indicating whether IP6 is enabled. /// Gets a value indicating whether IP6 is enabled.
/// </summary> /// </summary>
public bool IsIpv6Enabled => _configurationManager.GetNetworkConfiguration().EnableIPV6; public bool IsIPv6Enabled => _configurationManager.GetNetworkConfiguration().EnableIPv6;
/// <summary> /// <summary>
/// Gets a value indicating whether is all IPv6 interfaces are trusted as internal. /// Gets a value indicating whether is all IPv6 interfaces are trusted as internal.
@ -229,7 +229,7 @@ namespace Jellyfin.Networking.Manager
// Populate interface list // Populate interface list
foreach (var info in ipProperties.UnicastAddresses) foreach (var info in ipProperties.UnicastAddresses)
{ {
if (IsIpv4Enabled && info.Address.AddressFamily == AddressFamily.InterNetwork) if (IsIPv4Enabled && info.Address.AddressFamily == AddressFamily.InterNetwork)
{ {
var interfaceObject = new IPData(info.Address, new IPNetwork(info.Address, info.PrefixLength), adapter.Name); var interfaceObject = new IPData(info.Address, new IPNetwork(info.Address, info.PrefixLength), adapter.Name);
interfaceObject.Index = ipProperties.GetIPv4Properties().Index; interfaceObject.Index = ipProperties.GetIPv4Properties().Index;
@ -237,7 +237,7 @@ namespace Jellyfin.Networking.Manager
_interfaces.Add(interfaceObject); _interfaces.Add(interfaceObject);
} }
else if (IsIpv6Enabled && info.Address.AddressFamily == AddressFamily.InterNetworkV6) else if (IsIPv6Enabled && info.Address.AddressFamily == AddressFamily.InterNetworkV6)
{ {
var interfaceObject = new IPData(info.Address, new IPNetwork(info.Address, info.PrefixLength), adapter.Name); var interfaceObject = new IPData(info.Address, new IPNetwork(info.Address, info.PrefixLength), adapter.Name);
interfaceObject.Index = ipProperties.GetIPv6Properties().Index; interfaceObject.Index = ipProperties.GetIPv6Properties().Index;
@ -268,12 +268,12 @@ namespace Jellyfin.Networking.Manager
{ {
_logger.LogWarning("No interface information available. Using loopback interface(s)."); _logger.LogWarning("No interface information available. Using loopback interface(s).");
if (IsIpv4Enabled && !IsIpv6Enabled) if (IsIPv4Enabled && !IsIPv6Enabled)
{ {
_interfaces.Add(new IPData(IPAddress.Loopback, new IPNetwork(IPAddress.Loopback, 8), "lo")); _interfaces.Add(new IPData(IPAddress.Loopback, new IPNetwork(IPAddress.Loopback, 8), "lo"));
} }
if (!IsIpv4Enabled && IsIpv6Enabled) if (!IsIPv4Enabled && IsIPv6Enabled)
{ {
_interfaces.Add(new IPData(IPAddress.IPv6Loopback, new IPNetwork(IPAddress.IPv6Loopback, 128), "lo")); _interfaces.Add(new IPData(IPAddress.IPv6Loopback, new IPNetwork(IPAddress.IPv6Loopback, 128), "lo"));
} }
@ -311,14 +311,14 @@ namespace Jellyfin.Networking.Manager
{ {
_logger.LogDebug("Using LAN interface addresses as user provided no LAN details."); _logger.LogDebug("Using LAN interface addresses as user provided no LAN details.");
if (IsIpv6Enabled) if (IsIPv6Enabled)
{ {
_lanSubnets.Add(new IPNetwork(IPAddress.IPv6Loopback, 128)); // RFC 4291 (Loopback) _lanSubnets.Add(new IPNetwork(IPAddress.IPv6Loopback, 128)); // RFC 4291 (Loopback)
_lanSubnets.Add(new IPNetwork(IPAddress.Parse("fe80::"), 10)); // RFC 4291 (Site local) _lanSubnets.Add(new IPNetwork(IPAddress.Parse("fe80::"), 10)); // RFC 4291 (Site local)
_lanSubnets.Add(new IPNetwork(IPAddress.Parse("fc00::"), 7)); // RFC 4193 (Unique local) _lanSubnets.Add(new IPNetwork(IPAddress.Parse("fc00::"), 7)); // RFC 4193 (Unique local)
} }
if (IsIpv4Enabled) if (IsIPv4Enabled)
{ {
_lanSubnets.Add(new IPNetwork(IPAddress.Loopback, 8)); // RFC 5735 (Loopback) _lanSubnets.Add(new IPNetwork(IPAddress.Loopback, 8)); // RFC 5735 (Loopback)
_lanSubnets.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8)); // RFC 1918 (private) _lanSubnets.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8)); // RFC 1918 (private)
@ -382,13 +382,13 @@ namespace Jellyfin.Networking.Manager
} }
// Remove all IPv4 interfaces if IPv4 is disabled // Remove all IPv4 interfaces if IPv4 is disabled
if (!IsIpv4Enabled) if (!IsIPv4Enabled)
{ {
_interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetwork); _interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetwork);
} }
// Remove all IPv6 interfaces if IPv6 is disabled // Remove all IPv6 interfaces if IPv6 is disabled
if (!IsIpv6Enabled) if (!IsIPv6Enabled)
{ {
_interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetworkV6); _interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetworkV6);
} }
@ -470,7 +470,7 @@ namespace Jellyfin.Networking.Manager
_publishedServerUrls[new IPData(lanPrefix, new IPNetwork(lanPrefix, lan.PrefixLength))] = replacement; _publishedServerUrls[new IPData(lanPrefix, new IPNetwork(lanPrefix, lan.PrefixLength))] = replacement;
} }
} }
else if (NetworkExtensions.TryParseToSubnet(identifier, out var result) && result != null) else if (NetworkExtensions.TryParseToSubnet(identifier, out var result) && result is not null)
{ {
var data = new IPData(result.Prefix, result); var data = new IPData(result.Prefix, result);
_publishedServerUrls[data] = replacement; _publishedServerUrls[data] = replacement;
@ -492,7 +492,7 @@ namespace Jellyfin.Networking.Manager
private void ConfigurationUpdated(object? sender, ConfigurationUpdateEventArgs evt) private void ConfigurationUpdated(object? sender, ConfigurationUpdateEventArgs evt)
{ {
if (evt.Key.Equals("network", StringComparison.Ordinal)) if (evt.Key.Equals(NetworkConfigurationStore.StoreKey, StringComparison.Ordinal))
{ {
UpdateSettings((NetworkConfiguration)evt.NewConfiguration); UpdateSettings((NetworkConfiguration)evt.NewConfiguration);
} }
@ -581,8 +581,8 @@ namespace Jellyfin.Networking.Manager
// Use interface IP instead of name // Use interface IP instead of name
foreach (var iface in matchedInterfaces) foreach (var iface in matchedInterfaces)
{ {
if ((IsIpv4Enabled && iface.Address.AddressFamily == AddressFamily.InterNetwork) if ((IsIPv4Enabled && iface.Address.AddressFamily == AddressFamily.InterNetwork)
|| (IsIpv6Enabled && iface.Address.AddressFamily == AddressFamily.InterNetworkV6)) || (IsIPv6Enabled && iface.Address.AddressFamily == AddressFamily.InterNetworkV6))
{ {
result.Add(iface); result.Add(iface);
} }
@ -634,18 +634,18 @@ namespace Jellyfin.Networking.Manager
/// <inheritdoc/> /// <inheritdoc/>
public IReadOnlyList<IPData> GetLoopbacks() public IReadOnlyList<IPData> GetLoopbacks()
{ {
if (!(IsIpv4Enabled && IsIpv4Enabled)) if (!(IsIPv4Enabled && IsIPv4Enabled))
{ {
return Array.Empty<IPData>(); return Array.Empty<IPData>();
} }
var loopbackNetworks = new List<IPData>(); var loopbackNetworks = new List<IPData>();
if (IsIpv4Enabled) if (IsIPv4Enabled)
{ {
loopbackNetworks.Add(new IPData(IPAddress.Loopback, new IPNetwork(IPAddress.Loopback, 8), "lo")); loopbackNetworks.Add(new IPData(IPAddress.Loopback, new IPNetwork(IPAddress.Loopback, 8), "lo"));
} }
if (IsIpv6Enabled) if (IsIPv6Enabled)
{ {
loopbackNetworks.Add(new IPData(IPAddress.IPv6Loopback, new IPNetwork(IPAddress.IPv6Loopback, 128), "lo")); loopbackNetworks.Add(new IPData(IPAddress.IPv6Loopback, new IPNetwork(IPAddress.IPv6Loopback, 128), "lo"));
} }
@ -674,16 +674,16 @@ namespace Jellyfin.Networking.Manager
return result; return result;
} }
if (IsIpv4Enabled && IsIpv6Enabled) if (IsIPv4Enabled && IsIPv6Enabled)
{ {
// Kestrel source code shows it uses Sockets.DualMode - so this also covers IPAddress.Any by default // Kestrel source code shows it uses Sockets.DualMode - so this also covers IPAddress.Any by default
result.Add(new IPData(IPAddress.IPv6Any, new IPNetwork(IPAddress.IPv6Any, 0))); result.Add(new IPData(IPAddress.IPv6Any, new IPNetwork(IPAddress.IPv6Any, 0)));
} }
else if (IsIpv4Enabled) else if (IsIPv4Enabled)
{ {
result.Add(new IPData(IPAddress.Any, new IPNetwork(IPAddress.Any, 0))); result.Add(new IPData(IPAddress.Any, new IPNetwork(IPAddress.Any, 0)));
} }
else if (IsIpv6Enabled) else if (IsIPv6Enabled)
{ {
// Cannot use IPv6Any as Kestrel will bind to IPv4 addresses too. // Cannot use IPv6Any as Kestrel will bind to IPv4 addresses too.
foreach (var iface in _interfaces) foreach (var iface in _interfaces)
@ -701,7 +701,7 @@ namespace Jellyfin.Networking.Manager
/// <inheritdoc/> /// <inheritdoc/>
public string GetBindInterface(string source, out int? port) public string GetBindInterface(string source, out int? port)
{ {
if (!NetworkExtensions.TryParseHost(source, out var addresses, IsIpv4Enabled, IsIpv6Enabled)) if (!NetworkExtensions.TryParseHost(source, out var addresses, IsIPv4Enabled, IsIPv6Enabled))
{ {
addresses = Array.Empty<IPAddress>(); addresses = Array.Empty<IPAddress>();
} }
@ -726,14 +726,14 @@ namespace Jellyfin.Networking.Manager
string result; string result;
if (source != null) if (source is not null)
{ {
if (IsIpv4Enabled && !IsIpv6Enabled && source.AddressFamily == AddressFamily.InterNetworkV6) if (IsIPv4Enabled && !IsIPv6Enabled && source.AddressFamily == AddressFamily.InterNetworkV6)
{ {
_logger.LogWarning("IPv6 is disabled in Jellyfin, but enabled in the OS. This may affect how the interface is selected."); _logger.LogWarning("IPv6 is disabled in Jellyfin, but enabled in the OS. This may affect how the interface is selected.");
} }
if (!IsIpv4Enabled && IsIpv6Enabled && source.AddressFamily == AddressFamily.InterNetwork) if (!IsIPv4Enabled && IsIPv6Enabled && source.AddressFamily == AddressFamily.InterNetwork)
{ {
_logger.LogWarning("IPv4 is disabled in Jellyfin, but enabled in the OS. This may affect how the interface is selected."); _logger.LogWarning("IPv4 is disabled in Jellyfin, but enabled in the OS. This may affect how the interface is selected.");
} }
@ -759,6 +759,7 @@ namespace Jellyfin.Networking.Manager
} }
// Get the first LAN interface address that's not excluded and not a loopback address. // Get the first LAN interface address that's not excluded and not a loopback address.
// Get all available interfaces, prefer local interfaces
var availableInterfaces = _interfaces.Where(x => !IPAddress.IsLoopback(x.Address)) var availableInterfaces = _interfaces.Where(x => !IPAddress.IsLoopback(x.Address))
.OrderByDescending(x => IsInLocalNetwork(x.Address)) .OrderByDescending(x => IsInLocalNetwork(x.Address))
.ThenBy(x => x.Index) .ThenBy(x => x.Index)
@ -766,6 +767,7 @@ namespace Jellyfin.Networking.Manager
if (availableInterfaces.Count > 0) if (availableInterfaces.Count > 0)
{ {
// If no source address is given, use the preferred (first) interface
if (source is null) if (source is null)
{ {
result = NetworkExtensions.FormatIpString(availableInterfaces.First().Address); result = NetworkExtensions.FormatIpString(availableInterfaces.First().Address);
@ -773,16 +775,6 @@ namespace Jellyfin.Networking.Manager
return result; return result;
} }
foreach (var intf in availableInterfaces)
{
if (intf.Address.Equals(source))
{
result = NetworkExtensions.FormatIpString(intf.Address);
_logger.LogDebug("{Source}: Found matching interface to use as bind address: {Result}", source, result);
return result;
}
}
// Does the request originate in one of the interface subnets? // Does the request originate in one of the interface subnets?
// (For systems with multiple internal network cards, and multiple subnets) // (For systems with multiple internal network cards, and multiple subnets)
foreach (var intf in availableInterfaces) foreach (var intf in availableInterfaces)
@ -790,14 +782,19 @@ namespace Jellyfin.Networking.Manager
if (intf.Subnet.Contains(source)) if (intf.Subnet.Contains(source))
{ {
result = NetworkExtensions.FormatIpString(intf.Address); result = NetworkExtensions.FormatIpString(intf.Address);
_logger.LogDebug("{Source}: Found internal interface with matching subnet, using it as bind address: {Result}", source, result); _logger.LogDebug("{Source}: Found interface with matching subnet, using it as bind address: {Result}", source, result);
return result; return result;
} }
} }
// Fallback to first available interface
result = NetworkExtensions.FormatIpString(availableInterfaces[0].Address);
_logger.LogDebug("{Source}: No matching interfaces found, using preferred interface as bind address: {Result}", source, result);
return result;
} }
// There isn't any others, so we'll use the loopback. // There isn't any others, so we'll use the loopback.
result = IsIpv4Enabled && !IsIpv6Enabled ? "127.0.0.1" : "::1"; result = IsIPv4Enabled && !IsIPv6Enabled ? "127.0.0.1" : "::1";
_logger.LogWarning("{Source}: Only loopback {Result} returned, using that as bind address.", source, result); _logger.LogWarning("{Source}: Only loopback {Result} returned, using that as bind address.", source, result);
return result; return result;
} }
@ -819,12 +816,12 @@ namespace Jellyfin.Networking.Manager
return IPAddress.IsLoopback(subnet.Prefix) || (_lanSubnets.Any(x => x.Contains(subnet.Prefix)) && !_excludedSubnets.Any(x => x.Contains(subnet.Prefix))); return IPAddress.IsLoopback(subnet.Prefix) || (_lanSubnets.Any(x => x.Contains(subnet.Prefix)) && !_excludedSubnets.Any(x => x.Contains(subnet.Prefix)));
} }
if (NetworkExtensions.TryParseHost(address, out var addresses, IsIpv4Enabled, IsIpv6Enabled)) if (NetworkExtensions.TryParseHost(address, out var addresses, IsIPv4Enabled, IsIPv6Enabled))
{ {
bool match = false; bool match = false;
foreach (var ept in addresses) foreach (var ept in addresses)
{ {
match |= IPAddress.IsLoopback(ept) || (_lanSubnets.Any(x => x.Contains(ept)) && !_excludedSubnets.Any(x => x.Contains(ept))); match = match || IPAddress.IsLoopback(ept) || (_lanSubnets.Any(x => x.Contains(ept)) && !_excludedSubnets.Any(x => x.Contains(ept)));
if (match) if (match)
{ {
@ -860,15 +857,29 @@ namespace Jellyfin.Networking.Manager
bool match = false; bool match = false;
foreach (var lanSubnet in _lanSubnets) foreach (var lanSubnet in _lanSubnets)
{ {
match |= lanSubnet.Contains(address); match = lanSubnet.Contains(address);
if (match)
{
break;
}
}
if (!match)
{
return match;
} }
foreach (var excludedSubnet in _excludedSubnets) foreach (var excludedSubnet in _excludedSubnets)
{ {
match &= !excludedSubnet.Contains(address); match = match && !excludedSubnet.Contains(address);
if (!match)
{
break;
}
} }
NetworkExtensions.IsIPv6LinkLocal(address);
return match; return match;
} }
@ -905,7 +916,7 @@ namespace Jellyfin.Networking.Manager
// Get address interface. // Get address interface.
var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(x => data.Key.Subnet.Contains(x.Address)); var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(x => data.Key.Subnet.Contains(x.Address));
if (intf?.Address != null) if (intf?.Address is not null)
{ {
// Match IP address. // Match IP address.
bindPreference = data.Value; bindPreference = data.Value;
@ -930,7 +941,7 @@ namespace Jellyfin.Networking.Manager
} }
} }
if (port != null) if (port is not null)
{ {
_logger.LogDebug("{Source}: Matching bind address override found: {Address}:{Port}", source, bindPreference, port); _logger.LogDebug("{Source}: Matching bind address override found: {Address}:{Port}", source, bindPreference, port);
} }
@ -981,7 +992,7 @@ namespace Jellyfin.Networking.Manager
.Select(x => x.Address) .Select(x => x.Address)
.FirstOrDefault(); .FirstOrDefault();
if (bindAddress != null) if (bindAddress is not null)
{ {
result = NetworkExtensions.FormatIpString(bindAddress); result = NetworkExtensions.FormatIpString(bindAddress);
_logger.LogDebug("{Source}: External request received, matching external bind address found: {Result}", source, result); _logger.LogDebug("{Source}: External request received, matching external bind address found: {Result}", source, result);
@ -1001,7 +1012,7 @@ namespace Jellyfin.Networking.Manager
.Select(x => x.Address) .Select(x => x.Address)
.FirstOrDefault(); .FirstOrDefault();
if (bindAddress != null) if (bindAddress is not null)
{ {
result = NetworkExtensions.FormatIpString(bindAddress); result = NetworkExtensions.FormatIpString(bindAddress);
_logger.LogDebug("{Source}: Internal request received, matching internal bind address found: {Result}", source, result); _logger.LogDebug("{Source}: Internal request received, matching internal bind address found: {Result}", source, result);

View File

@ -367,7 +367,7 @@ namespace Jellyfin.Server.Extensions
private static void AddIpAddress(NetworkConfiguration config, ForwardedHeadersOptions options, IPAddress addr, int prefixLength) private static void AddIpAddress(NetworkConfiguration config, ForwardedHeadersOptions options, IPAddress addr, int prefixLength)
{ {
if ((!config.EnableIPV4 && addr.AddressFamily == AddressFamily.InterNetwork) || (!config.EnableIPV6 && addr.AddressFamily == AddressFamily.InterNetworkV6)) if ((!config.EnableIPv4 && addr.AddressFamily == AddressFamily.InterNetwork) || (!config.EnableIPv6 && addr.AddressFamily == AddressFamily.InterNetworkV6))
{ {
return; return;
} }

View File

@ -19,12 +19,12 @@ namespace MediaBrowser.Common.Net
/// <summary> /// <summary>
/// Gets a value indicating whether IPv4 is enabled. /// Gets a value indicating whether IPv4 is enabled.
/// </summary> /// </summary>
bool IsIpv4Enabled { get; } bool IsIPv4Enabled { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether IPv6 is enabled. /// Gets a value indicating whether IPv6 is enabled.
/// </summary> /// </summary>
bool IsIpv6Enabled { get; } bool IsIPv6Enabled { get; }
/// <summary> /// <summary>
/// Calculates the list of interfaces to use for Kestrel. /// Calculates the list of interfaces to use for Kestrel.
@ -119,7 +119,7 @@ namespace MediaBrowser.Common.Net
/// <param name="intf">Interface name.</param> /// <param name="intf">Interface name.</param>
/// <param name="result">Resulting object's IP addresses, if successful.</param> /// <param name="result">Resulting object's IP addresses, if successful.</param>
/// <returns>Success of the operation.</returns> /// <returns>Success of the operation.</returns>
bool TryParseInterface(string intf, out List<IPData>? result); bool TryParseInterface(string intf, out List<IPData> result);
/// <summary> /// <summary>
/// Returns all internal (LAN) bind interface addresses. /// Returns all internal (LAN) bind interface addresses.

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -170,36 +171,9 @@ namespace MediaBrowser.Common.Net
for (int a = 0; a < values.Length; a++) for (int a = 0; a < values.Length; a++)
{ {
string[] v = values[a].Trim().Split("/"); if (TryParseToSubnet(values[a], out var innerResult, negated))
var address = IPAddress.None;
if (negated && v[0].StartsWith('!'))
{ {
_ = IPAddress.TryParse(v[0][1..], out address); result.Add(innerResult);
}
else if (!negated)
{
_ = IPAddress.TryParse(v[0][0..], out address);
}
if (address != IPAddress.None && address is not null)
{
if (v.Length > 1 && int.TryParse(v[1], out var netmask))
{
result.Add(new IPNetwork(address, netmask));
}
else if (v.Length > 1 && IPAddress.TryParse(v[1], out var netmaskAddress))
{
result.Add(new IPNetwork(address, NetworkExtensions.MaskToCidr(netmaskAddress)));
}
else if (address.AddressFamily == AddressFamily.InterNetwork)
{
result.Add(new IPNetwork(address, 32));
}
else if (address.AddressFamily == AddressFamily.InterNetworkV6)
{
result.Add(new IPNetwork(address, 128));
}
} }
} }
@ -228,28 +202,33 @@ namespace MediaBrowser.Common.Net
return false; return false;
} }
string[] v = value.Trim().Split("/"); var splitString = value.Trim().Split("/");
var ipBlock = splitString[0];
var address = IPAddress.None; var address = IPAddress.None;
if (negated && v[0].StartsWith('!')) if (negated && ipBlock.StartsWith('!') && IPAddress.TryParse(ipBlock[1..], out var tmpAddress))
{ {
_ = IPAddress.TryParse(v[0][1..], out address); address = tmpAddress;
} }
else if (!negated) else if (!negated && IPAddress.TryParse(ipBlock, out tmpAddress))
{ {
_ = IPAddress.TryParse(v[0][0..], out address); address = tmpAddress;
} }
if (address != IPAddress.None && address is not null) if (address != IPAddress.None && address is not null)
{ {
if (v.Length > 1 && int.TryParse(v[1], out var netmask)) if (splitString.Length > 1)
{
var subnetBlock = splitString[1];
if (int.TryParse(subnetBlock, out var netmask))
{ {
result = new IPNetwork(address, netmask); result = new IPNetwork(address, netmask);
} }
else if (v.Length > 1 && IPAddress.TryParse(v[1], out var netmaskAddress)) else if (IPAddress.TryParse(subnetBlock, out var netmaskAddress))
{ {
result = new IPNetwork(address, NetworkExtensions.MaskToCidr(netmaskAddress)); result = new IPNetwork(address, NetworkExtensions.MaskToCidr(netmaskAddress));
} }
}
else if (address.AddressFamily == AddressFamily.InterNetwork) else if (address.AddressFamily == AddressFamily.InterNetwork)
{ {
result = new IPNetwork(address, 32); result = new IPNetwork(address, 32);
@ -353,7 +332,13 @@ namespace MediaBrowser.Common.Net
/// <returns>The broadcast address.</returns> /// <returns>The broadcast address.</returns>
public static IPAddress GetBroadcastAddress(IPNetwork network) public static IPAddress GetBroadcastAddress(IPNetwork network)
{ {
uint ipAddress = BitConverter.ToUInt32(network.Prefix.GetAddressBytes(), 0); var addressBytes = network.Prefix.GetAddressBytes();
if (BitConverter.IsLittleEndian)
{
addressBytes.Reverse();
}
uint ipAddress = BitConverter.ToUInt32(addressBytes, 0);
uint ipMaskV4 = BitConverter.ToUInt32(CidrToMask(network.PrefixLength, AddressFamily.InterNetwork).GetAddressBytes(), 0); uint ipMaskV4 = BitConverter.ToUInt32(CidrToMask(network.PrefixLength, AddressFamily.InterNetwork).GetAddressBytes(), 0);
uint broadCastIpAddress = ipAddress | ~ipMaskV4; uint broadCastIpAddress = ipAddress | ~ipMaskV4;

View File

@ -23,8 +23,8 @@ namespace Jellyfin.Networking.Tests
var ip = IPAddress.Parse(value); var ip = IPAddress.Parse(value);
var conf = new NetworkConfiguration() var conf = new NetworkConfiguration()
{ {
EnableIPV6 = true, EnableIPv6 = true,
EnableIPV4 = true, EnableIPv4 = true,
LocalNetworkSubnets = network.Split(',') LocalNetworkSubnets = network.Split(',')
}; };
@ -50,8 +50,8 @@ namespace Jellyfin.Networking.Tests
var ip = IPAddress.Parse(value); var ip = IPAddress.Parse(value);
var conf = new NetworkConfiguration() var conf = new NetworkConfiguration()
{ {
EnableIPV6 = true, EnableIPv6 = true,
EnableIPV4 = true, EnableIPv4 = true,
LocalNetworkSubnets = network.Split(',') LocalNetworkSubnets = network.Split(',')
}; };

View File

@ -47,8 +47,8 @@ namespace Jellyfin.Networking.Tests
{ {
var conf = new NetworkConfiguration() var conf = new NetworkConfiguration()
{ {
EnableIPV6 = true, EnableIPv6 = true,
EnableIPV4 = true, EnableIPv4 = true,
LocalNetworkSubnets = lan?.Split(';') ?? throw new ArgumentNullException(nameof(lan)) LocalNetworkSubnets = lan?.Split(';') ?? throw new ArgumentNullException(nameof(lan))
}; };
@ -107,7 +107,7 @@ namespace Jellyfin.Networking.Tests
[InlineData("10.128.240.50/30", "10.128.240.51")] [InlineData("10.128.240.50/30", "10.128.240.51")]
[InlineData("10.128.240.50/255.255.255.252", "10.128.240.51")] [InlineData("10.128.240.50/255.255.255.252", "10.128.240.51")]
[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 ipa = IPAddress.Parse(ipAddress); var ipa = IPAddress.Parse(ipAddress);
Assert.True(NetworkExtensions.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress))); Assert.True(NetworkExtensions.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress)));
@ -127,7 +127,7 @@ namespace Jellyfin.Networking.Tests
[InlineData("10.128.240.50/30", "10.128.239.50")] [InlineData("10.128.240.50/30", "10.128.239.50")]
[InlineData("10.128.240.50/30", "10.127.240.51")] [InlineData("10.128.240.50/30", "10.127.240.51")]
[InlineData("10.128.240.50/255.255.255.252", "10.127.240.51")] [InlineData("10.128.240.50/255.255.255.252", "10.127.240.51")]
public void IpV4SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress) public void IPv4SubnetMaskDoesNotMatchInvalidIPAddress(string netMask, string ipAddress)
{ {
var ipa = IPAddress.Parse(ipAddress); var ipa = IPAddress.Parse(ipAddress);
Assert.False(NetworkExtensions.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress))); Assert.False(NetworkExtensions.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress)));
@ -144,7 +144,7 @@ namespace Jellyfin.Networking.Tests
[InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:0001:0000:0000:0000")] [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:0001:0000:0000:0000")]
[InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFF0")] [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFF0")]
[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)
{ {
Assert.True(NetworkExtensions.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress))); Assert.True(NetworkExtensions.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress)));
} }
@ -155,7 +155,7 @@ namespace Jellyfin.Networking.Tests
[InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0013:0001:0000:0000:0000")] [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0013:0001:0000:0000:0000")]
[InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0011:FFFF:FFFF:FFFF:FFF0")] [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0011:FFFF:FFFF:FFFF:FFF0")]
[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)
{ {
Assert.False(NetworkExtensions.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress))); Assert.False(NetworkExtensions.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress)));
} }
@ -194,8 +194,8 @@ namespace Jellyfin.Networking.Tests
var conf = new NetworkConfiguration() var conf = new NetworkConfiguration()
{ {
LocalNetworkAddresses = bindAddresses.Split(','), LocalNetworkAddresses = bindAddresses.Split(','),
EnableIPV6 = ipv6enabled, EnableIPv6 = ipv6enabled,
EnableIPV4 = true EnableIPv4 = true
}; };
NetworkManager.MockNetworkSettings = "192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11"; NetworkManager.MockNetworkSettings = "192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11";
@ -256,8 +256,8 @@ namespace Jellyfin.Networking.Tests
{ {
LocalNetworkSubnets = lan.Split(','), LocalNetworkSubnets = lan.Split(','),
LocalNetworkAddresses = bindAddresses.Split(','), LocalNetworkAddresses = bindAddresses.Split(','),
EnableIPV6 = ipv6enabled, EnableIPv6 = ipv6enabled,
EnableIPV4 = true, EnableIPv4 = true,
PublishedServerUriBySubnet = new string[] { publishedServers } PublishedServerUriBySubnet = new string[] { publishedServers }
}; };
@ -281,13 +281,13 @@ namespace Jellyfin.Networking.Tests
[InlineData("185.10.10.10", "185.10.10.10", false)] [InlineData("185.10.10.10", "185.10.10.10", false)]
[InlineData("", "100.100.100.100", false)] [InlineData("", "100.100.100.100", false)]
public void HasRemoteAccess_GivenWhitelist_AllowsOnlyIpsInWhitelist(string addresses, string remoteIp, bool denied) public void HasRemoteAccess_GivenWhitelist_AllowsOnlyIPsInWhitelist(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.
// If left blank, all remote addresses will be allowed. // If left blank, all remote addresses will be allowed.
var conf = new NetworkConfiguration() var conf = new NetworkConfiguration()
{ {
EnableIPV4 = true, EnableIPv4 = true,
RemoteIPFilter = addresses.Split(','), RemoteIPFilter = addresses.Split(','),
IsRemoteIPFilterBlacklist = false IsRemoteIPFilterBlacklist = false
}; };
@ -301,13 +301,13 @@ namespace Jellyfin.Networking.Tests
[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.
// If left blank, all remote addresses will be allowed. // If left blank, all remote addresses will be allowed.
var conf = new NetworkConfiguration() var conf = new NetworkConfiguration()
{ {
EnableIPV4 = true, EnableIPv4 = true,
RemoteIPFilter = addresses.Split(','), RemoteIPFilter = addresses.Split(','),
IsRemoteIPFilterBlacklist = true IsRemoteIPFilterBlacklist = true
}; };
@ -326,7 +326,7 @@ namespace Jellyfin.Networking.Tests
{ {
var conf = new NetworkConfiguration var conf = new NetworkConfiguration
{ {
EnableIPV4 = true, EnableIPv4 = true,
LocalNetworkSubnets = lan.Split(','), LocalNetworkSubnets = lan.Split(','),
LocalNetworkAddresses = bind.Split(',') LocalNetworkAddresses = bind.Split(',')
}; };
@ -350,7 +350,7 @@ namespace Jellyfin.Networking.Tests
{ {
var conf = new NetworkConfiguration var conf = new NetworkConfiguration
{ {
EnableIPV4 = true, EnableIPv4 = true,
LocalNetworkSubnets = lan.Split(','), LocalNetworkSubnets = lan.Split(','),
LocalNetworkAddresses = bind.Split(',') LocalNetworkAddresses = bind.Split(',')
}; };

View File

@ -77,8 +77,8 @@ namespace Jellyfin.Server.Tests
var settings = new NetworkConfiguration var settings = new NetworkConfiguration
{ {
EnableIPV4 = ip4, EnableIPv4 = ip4,
EnableIPV6 = ip6 EnableIPv6 = ip6
}; };
ForwardedHeadersOptions options = new ForwardedHeadersOptions(); ForwardedHeadersOptions options = new ForwardedHeadersOptions();
@ -116,8 +116,8 @@ namespace Jellyfin.Server.Tests
{ {
var conf = new NetworkConfiguration() var conf = new NetworkConfiguration()
{ {
EnableIPV6 = true, EnableIPv6 = true,
EnableIPV4 = true, EnableIPv4 = true,
}; };
return new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>()); return new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());