aboutsummaryrefslogtreecommitdiff
path: root/src/Jellyfin.Networking
diff options
context:
space:
mode:
authorMarc Brooks <IDisposable@gmail.com>2025-02-03 19:48:59 -0600
committerGitHub <noreply@github.com>2025-02-03 19:48:59 -0600
commite8cbcde02ebd930a5eeb6c95e0875a9e30acb3e8 (patch)
tree2ecd43f232012c8f037f4cd6fee4168e46d01aa3 /src/Jellyfin.Networking
parent6dc61a430ba3a8480399309f277e5debfd6403ba (diff)
parentd376b5fbc7cf3ae7440a606a9e885d70605956bd (diff)
Merge branch 'master' into sort-nfo-data
Diffstat (limited to 'src/Jellyfin.Networking')
-rw-r--r--src/Jellyfin.Networking/Jellyfin.Networking.csproj5
-rw-r--r--src/Jellyfin.Networking/Manager/NetworkManager.cs23
-rw-r--r--src/Jellyfin.Networking/PortForwardingHost.cs192
3 files changed, 15 insertions, 205 deletions
diff --git a/src/Jellyfin.Networking/Jellyfin.Networking.csproj b/src/Jellyfin.Networking/Jellyfin.Networking.csproj
index 24b3ecaab..1a146549d 100644
--- a/src/Jellyfin.Networking/Jellyfin.Networking.csproj
+++ b/src/Jellyfin.Networking/Jellyfin.Networking.csproj
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net8.0</TargetFramework>
+ <TargetFramework>net9.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
@@ -14,7 +14,4 @@
<ProjectReference Include="..\..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
</ItemGroup>
- <ItemGroup>
- <PackageReference Include="Mono.Nat" />
- </ItemGroup>
</Project>
diff --git a/src/Jellyfin.Networking/Manager/NetworkManager.cs b/src/Jellyfin.Networking/Manager/NetworkManager.cs
index b285b836b..dd01e9533 100644
--- a/src/Jellyfin.Networking/Manager/NetworkManager.cs
+++ b/src/Jellyfin.Networking/Manager/NetworkManager.cs
@@ -27,7 +27,7 @@ public class NetworkManager : INetworkManager, IDisposable
/// <summary>
/// Threading lock for network properties.
/// </summary>
- private readonly object _initLock;
+ private readonly Lock _initLock;
private readonly ILogger<NetworkManager> _logger;
@@ -35,7 +35,7 @@ public class NetworkManager : INetworkManager, IDisposable
private readonly IConfiguration _startupConfig;
- private readonly object _networkEventLock;
+ private readonly Lock _networkEventLock;
/// <summary>
/// Holds the published server URLs and the IPs to use them on.
@@ -57,7 +57,7 @@ public class NetworkManager : INetworkManager, IDisposable
/// <summary>
/// Dictionary containing interface addresses and their subnets.
/// </summary>
- private IReadOnlyList<IPData> _interfaces;
+ private List<IPData> _interfaces;
/// <summary>
/// Unfiltered user defined LAN subnets (<see cref="NetworkConfiguration.LocalNetworkSubnets"/>)
@@ -81,7 +81,6 @@ public class NetworkManager : INetworkManager, IDisposable
/// <param name="configurationManager">The <see cref="IConfigurationManager"/> instance.</param>
/// <param name="startupConfig">The <see cref="IConfiguration"/> instance holding startup parameters.</param>
/// <param name="logger">Logger to use for messages.</param>
-#pragma warning disable CS8618 // Non-nullable field is uninitialized. : Values are set in UpdateSettings function. Compiler doesn't yet recognise this.
public NetworkManager(IConfigurationManager configurationManager, IConfiguration startupConfig, ILogger<NetworkManager> logger)
{
ArgumentNullException.ThrowIfNull(logger);
@@ -94,7 +93,7 @@ public class NetworkManager : INetworkManager, IDisposable
_interfaces = new List<IPData>();
_macAddresses = new List<PhysicalAddress>();
_publishedServerUrls = new List<PublishedServerUriOverride>();
- _networkEventLock = new object();
+ _networkEventLock = new();
_remoteAddressFilter = new List<IPNetwork>();
_ = bool.TryParse(startupConfig[DetectNetworkChangeKey], out var detectNetworkChange);
@@ -109,7 +108,6 @@ public class NetworkManager : INetworkManager, IDisposable
_configurationManager.NamedConfigurationUpdated += ConfigurationUpdated;
}
-#pragma warning restore CS8618 // Non-nullable field is uninitialized.
/// <summary>
/// Event triggered on network changes.
@@ -312,6 +310,7 @@ public class NetworkManager : INetworkManager, IDisposable
/// <summary>
/// Initializes internal LAN cache.
/// </summary>
+ [MemberNotNull(nameof(_lanSubnets), nameof(_excludedSubnets))]
private void InitializeLan(NetworkConfiguration config)
{
lock (_initLock)
@@ -591,6 +590,7 @@ public class NetworkManager : INetworkManager, IDisposable
/// Reloads all settings and re-Initializes the instance.
/// </summary>
/// <param name="configuration">The <see cref="NetworkConfiguration"/> to use.</param>
+ [MemberNotNull(nameof(_lanSubnets), nameof(_excludedSubnets))]
public void UpdateSettings(object configuration)
{
ArgumentNullException.ThrowIfNull(configuration);
@@ -702,7 +702,7 @@ public class NetworkManager : INetworkManager, IDisposable
return false;
}
}
- else if (!_lanSubnets.Any(x => x.Contains(remoteIP)))
+ else if (!IsInLocalNetwork(remoteIP))
{
// Remote not enabled. So everyone should be LAN.
return false;
@@ -973,7 +973,7 @@ public class NetworkManager : INetworkManager, IDisposable
bindPreference = string.Empty;
int? port = null;
- // Only consider subnets including the source IP, prefering specific overrides
+ // Only consider subnets including the source IP, preferring specific overrides
List<PublishedServerUriOverride> validPublishedServerUrls;
if (!isInExternalSubnet)
{
@@ -997,7 +997,9 @@ public class NetworkManager : INetworkManager, IDisposable
// Get interface matching override subnet
var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(x => data.Data.Subnet.Contains(x.Address));
- if (intf?.Address is not null)
+ if (intf?.Address is not null
+ || (data.Data.AddressFamily == AddressFamily.InterNetwork && data.Data.Address.Equals(IPAddress.Any))
+ || (data.Data.AddressFamily == AddressFamily.InterNetworkV6 && data.Data.Address.Equals(IPAddress.IPv6Any)))
{
// If matching interface is found, use override
bindPreference = data.OverrideUri;
@@ -1025,6 +1027,7 @@ public class NetworkManager : INetworkManager, IDisposable
}
_logger.LogDebug("{Source}: Matching bind address override found: {Address}", source, bindPreference);
+
return true;
}
@@ -1063,6 +1066,7 @@ public class NetworkManager : INetworkManager, IDisposable
// If none exists, this will select the first external interface if there is one.
bindAddress = externalInterfaces
.OrderByDescending(x => x.Subnet.Contains(source))
+ .ThenByDescending(x => x.Subnet.PrefixLength)
.ThenBy(x => x.Index)
.Select(x => x.Address)
.First();
@@ -1080,6 +1084,7 @@ public class NetworkManager : INetworkManager, IDisposable
// 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))
+ .ThenByDescending(x => x.Subnet.PrefixLength)
.ThenBy(x => x.Index)
.Select(x => x.Address)
.FirstOrDefault();
diff --git a/src/Jellyfin.Networking/PortForwardingHost.cs b/src/Jellyfin.Networking/PortForwardingHost.cs
deleted file mode 100644
index d01343624..000000000
--- a/src/Jellyfin.Networking/PortForwardingHost.cs
+++ /dev/null
@@ -1,192 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Net;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using Mono.Nat;
-
-namespace Jellyfin.Networking;
-
-/// <summary>
-/// <see cref="IHostedService"/> responsible for UPnP port forwarding.
-/// </summary>
-public sealed class PortForwardingHost : IHostedService, IDisposable
-{
- private readonly IServerApplicationHost _appHost;
- private readonly ILogger<PortForwardingHost> _logger;
- private readonly IServerConfigurationManager _config;
- private readonly ConcurrentDictionary<IPEndPoint, byte> _createdRules = new();
-
- private Timer? _timer;
- private string? _configIdentifier;
- private bool _disposed;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="PortForwardingHost"/> class.
- /// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="appHost">The application host.</param>
- /// <param name="config">The configuration manager.</param>
- public PortForwardingHost(
- ILogger<PortForwardingHost> logger,
- IServerApplicationHost appHost,
- IServerConfigurationManager config)
- {
- _logger = logger;
- _appHost = appHost;
- _config = config;
- }
-
- private string GetConfigIdentifier()
- {
- const char Separator = '|';
- var config = _config.GetNetworkConfiguration();
-
- return new StringBuilder(32)
- .Append(config.EnableUPnP).Append(Separator)
- .Append(config.PublicHttpPort).Append(Separator)
- .Append(config.PublicHttpsPort).Append(Separator)
- .Append(_appHost.HttpPort).Append(Separator)
- .Append(_appHost.HttpsPort).Append(Separator)
- .Append(_appHost.ListenWithHttps).Append(Separator)
- .Append(config.EnableRemoteAccess).Append(Separator)
- .ToString();
- }
-
- private void OnConfigurationUpdated(object? sender, EventArgs e)
- {
- var oldConfigIdentifier = _configIdentifier;
- _configIdentifier = GetConfigIdentifier();
-
- if (!string.Equals(_configIdentifier, oldConfigIdentifier, StringComparison.OrdinalIgnoreCase))
- {
- Stop();
- Start();
- }
- }
-
- /// <inheritdoc />
- public Task StartAsync(CancellationToken cancellationToken)
- {
- Start();
-
- _config.ConfigurationUpdated += OnConfigurationUpdated;
-
- return Task.CompletedTask;
- }
-
- /// <inheritdoc />
- public Task StopAsync(CancellationToken cancellationToken)
- {
- Stop();
-
- return Task.CompletedTask;
- }
-
- private void Start()
- {
- var config = _config.GetNetworkConfiguration();
- if (!config.EnableUPnP || !config.EnableRemoteAccess)
- {
- return;
- }
-
- _logger.LogInformation("Starting NAT discovery");
-
- NatUtility.DeviceFound += OnNatUtilityDeviceFound;
- NatUtility.StartDiscovery();
-
- _timer?.Dispose();
- _timer = new Timer(_ => _createdRules.Clear(), null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
- }
-
- private void Stop()
- {
- _logger.LogInformation("Stopping NAT discovery");
-
- NatUtility.StopDiscovery();
- NatUtility.DeviceFound -= OnNatUtilityDeviceFound;
-
- _timer?.Dispose();
- _timer = null;
- }
-
- private async void OnNatUtilityDeviceFound(object? sender, DeviceEventArgs e)
- {
- ObjectDisposedException.ThrowIf(_disposed, this);
-
- try
- {
- // On some systems the device discovered event seems to fire repeatedly
- // This check will help ensure we're not trying to port map the same device over and over
- if (!_createdRules.TryAdd(e.Device.DeviceEndpoint, 0))
- {
- return;
- }
-
- await Task.WhenAll(CreatePortMaps(e.Device)).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error creating port forwarding rules");
- }
- }
-
- private IEnumerable<Task> CreatePortMaps(INatDevice device)
- {
- var config = _config.GetNetworkConfiguration();
- yield return CreatePortMap(device, _appHost.HttpPort, config.PublicHttpPort);
-
- if (_appHost.ListenWithHttps)
- {
- yield return CreatePortMap(device, _appHost.HttpsPort, config.PublicHttpsPort);
- }
- }
-
- private async Task CreatePortMap(INatDevice device, int privatePort, int publicPort)
- {
- _logger.LogDebug(
- "Creating port map on local port {LocalPort} to public port {PublicPort} with device {DeviceEndpoint}",
- privatePort,
- publicPort,
- device.DeviceEndpoint);
-
- try
- {
- var mapping = new Mapping(Protocol.Tcp, privatePort, publicPort, 0, _appHost.Name);
- await device.CreatePortMapAsync(mapping).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError(
- ex,
- "Error creating port map on local port {LocalPort} to public port {PublicPort} with device {DeviceEndpoint}.",
- privatePort,
- publicPort,
- device.DeviceEndpoint);
- }
- }
-
- /// <inheritdoc />
- public void Dispose()
- {
- if (_disposed)
- {
- return;
- }
-
- _config.ConfigurationUpdated -= OnConfigurationUpdated;
-
- _timer?.Dispose();
- _timer = null;
-
- _disposed = true;
- }
-}