aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBond_009 <bond.009@outlook.com>2023-10-23 23:45:01 +0200
committerBond_009 <bond.009@outlook.com>2023-11-14 21:13:51 +0100
commit99e0d46ad93c1f2e62aed67c26b92f256610f1a6 (patch)
treead176928606b259131196cac346d6fee4263c138
parenteb022c49ccb310ee46d8d7b7f46678312b13abc6 (diff)
Use System.Net.IPNetwork
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs363
-rw-r--r--Jellyfin.Networking/Manager/NetworkManager.cs17
-rw-r--r--Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs3
-rw-r--r--MediaBrowser.Common/Net/NetworkConstants.cs1
-rw-r--r--MediaBrowser.Common/Net/NetworkUtils.cs3
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj1
-rw-r--r--MediaBrowser.Model/Net/IPData.cs5
-rw-r--r--src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj2
-rw-r--r--tests/Jellyfin.Server.Tests/ParseNetworkTests.cs3
9 files changed, 379 insertions, 19 deletions
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
new file mode 100644
index 000000000..dbc47d981
--- /dev/null
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -0,0 +1,363 @@
+#nullable disable
+
+#pragma warning disable CS1591
+
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+using Emby.Dlna.PlayTo;
+using Emby.Dlna.Ssdp;
+using Jellyfin.Networking.Configuration;
+using Jellyfin.Networking.Extensions;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Globalization;
+using Microsoft.Extensions.Logging;
+using Rssdp;
+using Rssdp.Infrastructure;
+
+namespace Emby.Dlna.Main
+{
+ public sealed class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
+ {
+ private readonly IServerConfigurationManager _config;
+ private readonly ILogger<DlnaEntryPoint> _logger;
+ private readonly IServerApplicationHost _appHost;
+ private readonly ISessionManager _sessionManager;
+ private readonly IHttpClientFactory _httpClientFactory;
+ private readonly ILibraryManager _libraryManager;
+ private readonly IUserManager _userManager;
+ private readonly IDlnaManager _dlnaManager;
+ private readonly IImageProcessor _imageProcessor;
+ private readonly IUserDataManager _userDataManager;
+ private readonly ILocalizationManager _localization;
+ private readonly IMediaSourceManager _mediaSourceManager;
+ private readonly IMediaEncoder _mediaEncoder;
+ private readonly IDeviceDiscovery _deviceDiscovery;
+ private readonly ISsdpCommunicationsServer _communicationsServer;
+ private readonly INetworkManager _networkManager;
+ private readonly object _syncLock = new();
+ private readonly bool _disabled;
+
+ private PlayToManager _manager;
+ private SsdpDevicePublisher _publisher;
+
+ private bool _disposed;
+
+ public DlnaEntryPoint(
+ IServerConfigurationManager config,
+ ILoggerFactory loggerFactory,
+ IServerApplicationHost appHost,
+ ISessionManager sessionManager,
+ IHttpClientFactory httpClientFactory,
+ ILibraryManager libraryManager,
+ IUserManager userManager,
+ IDlnaManager dlnaManager,
+ IImageProcessor imageProcessor,
+ IUserDataManager userDataManager,
+ ILocalizationManager localizationManager,
+ IMediaSourceManager mediaSourceManager,
+ IDeviceDiscovery deviceDiscovery,
+ IMediaEncoder mediaEncoder,
+ ISsdpCommunicationsServer communicationsServer,
+ INetworkManager networkManager)
+ {
+ _config = config;
+ _appHost = appHost;
+ _sessionManager = sessionManager;
+ _httpClientFactory = httpClientFactory;
+ _libraryManager = libraryManager;
+ _userManager = userManager;
+ _dlnaManager = dlnaManager;
+ _imageProcessor = imageProcessor;
+ _userDataManager = userDataManager;
+ _localization = localizationManager;
+ _mediaSourceManager = mediaSourceManager;
+ _deviceDiscovery = deviceDiscovery;
+ _mediaEncoder = mediaEncoder;
+ _communicationsServer = communicationsServer;
+ _networkManager = networkManager;
+ _logger = loggerFactory.CreateLogger<DlnaEntryPoint>();
+
+ var netConfig = config.GetConfiguration<NetworkConfiguration>(NetworkConfigurationStore.StoreKey);
+ _disabled = appHost.ListenWithHttps && netConfig.RequireHttps;
+
+ if (_disabled && _config.GetDlnaConfiguration().EnableServer)
+ {
+ _logger.LogError("The DLNA specification does not support HTTPS.");
+ }
+ }
+
+ public async Task RunAsync()
+ {
+ await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
+
+ if (_disabled)
+ {
+ // No use starting as dlna won't work, as we're running purely on HTTPS.
+ return;
+ }
+
+ ReloadComponents();
+
+ _config.NamedConfigurationUpdated += OnNamedConfigurationUpdated;
+ }
+
+ private void OnNamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
+ {
+ if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
+ {
+ ReloadComponents();
+ }
+ }
+
+ private void ReloadComponents()
+ {
+ var options = _config.GetDlnaConfiguration();
+ StartDeviceDiscovery();
+
+ if (options.EnableServer)
+ {
+ StartDevicePublisher(options);
+ }
+ else
+ {
+ DisposeDevicePublisher();
+ }
+
+ if (options.EnablePlayTo)
+ {
+ StartPlayToManager();
+ }
+ else
+ {
+ DisposePlayToManager();
+ }
+ }
+
+ private void StartDeviceDiscovery()
+ {
+ try
+ {
+ ((DeviceDiscovery)_deviceDiscovery).Start(_communicationsServer);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error starting device discovery");
+ }
+ }
+
+ public void StartDevicePublisher(Configuration.DlnaOptions options)
+ {
+ if (_publisher is not null)
+ {
+ return;
+ }
+
+ try
+ {
+ _publisher = new SsdpDevicePublisher(
+ _communicationsServer,
+ Environment.OSVersion.Platform.ToString(),
+ // Can not use VersionString here since that includes OS and version
+ Environment.OSVersion.Version.ToString(),
+ _config.GetDlnaConfiguration().SendOnlyMatchedHost)
+ {
+ LogFunction = (msg) => _logger.LogDebug("{Msg}", msg),
+ SupportPnpRootDevice = false
+ };
+
+ RegisterServerEndpoints();
+
+ if (options.BlastAliveMessages)
+ {
+ _publisher.StartSendingAliveNotifications(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds));
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error registering endpoint");
+ }
+ }
+
+ private void RegisterServerEndpoints()
+ {
+ var udn = CreateUuid(_appHost.SystemId);
+ var descriptorUri = "/dlna/" + udn + "/description.xml";
+
+ // Only get bind addresses in LAN
+ // IPv6 is currently unsupported
+ var validInterfaces = _networkManager.GetInternalBindAddresses()
+ .Where(x => x.Address is not null)
+ .Where(x => x.AddressFamily != AddressFamily.InterNetworkV6)
+ .ToList();
+
+ if (validInterfaces.Count == 0)
+ {
+ // No interfaces returned, fall back to loopback
+ validInterfaces = _networkManager.GetLoopbacks().ToList();
+ }
+
+ foreach (var intf in validInterfaces)
+ {
+ var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
+
+ _logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, intf.Address);
+
+ var uri = new UriBuilder(_appHost.GetApiUrlForLocalAccess(intf.Address, false) + descriptorUri);
+
+ var device = new SsdpRootDevice
+ {
+ 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.
+ Address = intf.Address,
+ PrefixLength = NetworkExtensions.MaskToCidr(intf.Subnet.BaseAddress),
+ FriendlyName = "Jellyfin",
+ Manufacturer = "Jellyfin",
+ ModelName = "Jellyfin Server",
+ Uuid = udn
+ // This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc.
+ };
+
+ SetProperties(device, fullService);
+ _publisher.AddDevice(device);
+
+ var embeddedDevices = new[]
+ {
+ "urn:schemas-upnp-org:service:ContentDirectory:1",
+ "urn:schemas-upnp-org:service:ConnectionManager:1",
+ // "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
+ };
+
+ foreach (var subDevice in embeddedDevices)
+ {
+ var embeddedDevice = new SsdpEmbeddedDevice
+ {
+ FriendlyName = device.FriendlyName,
+ Manufacturer = device.Manufacturer,
+ ModelName = device.ModelName,
+ Uuid = udn
+ // This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc.
+ };
+
+ SetProperties(embeddedDevice, subDevice);
+ device.AddDevice(embeddedDevice);
+ }
+ }
+ }
+
+ private static string CreateUuid(string text)
+ {
+ if (!Guid.TryParse(text, out var guid))
+ {
+ guid = text.GetMD5();
+ }
+
+ return guid.ToString("D", CultureInfo.InvariantCulture);
+ }
+
+ private static void SetProperties(SsdpDevice device, string fullDeviceType)
+ {
+ var serviceParts = fullDeviceType
+ .Replace("urn:", string.Empty, StringComparison.OrdinalIgnoreCase)
+ .Replace(":1", string.Empty, StringComparison.OrdinalIgnoreCase)
+ .Split(':');
+
+ device.DeviceTypeNamespace = serviceParts[0].Replace('.', '-');
+ device.DeviceClass = serviceParts[1];
+ device.DeviceType = serviceParts[2];
+ }
+
+ private void StartPlayToManager()
+ {
+ lock (_syncLock)
+ {
+ if (_manager is not null)
+ {
+ return;
+ }
+
+ try
+ {
+ _manager = new PlayToManager(
+ _logger,
+ _sessionManager,
+ _libraryManager,
+ _userManager,
+ _dlnaManager,
+ _appHost,
+ _imageProcessor,
+ _deviceDiscovery,
+ _httpClientFactory,
+ _userDataManager,
+ _localization,
+ _mediaSourceManager,
+ _mediaEncoder);
+
+ _manager.Start();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error starting PlayTo manager");
+ }
+ }
+ }
+
+ private void DisposePlayToManager()
+ {
+ lock (_syncLock)
+ {
+ if (_manager is not null)
+ {
+ try
+ {
+ _logger.LogInformation("Disposing PlayToManager");
+ _manager.Dispose();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error disposing PlayTo manager");
+ }
+
+ _manager = null;
+ }
+ }
+ }
+
+ public void DisposeDevicePublisher()
+ {
+ if (_publisher is not null)
+ {
+ _logger.LogInformation("Disposing SsdpDevicePublisher");
+ _publisher.Dispose();
+ _publisher = null;
+ }
+ }
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ DisposeDevicePublisher();
+ DisposePlayToManager();
+ _disposed = true;
+ }
+ }
+}
diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs
index b0fe4aba6..3c03d137b 100644
--- a/Jellyfin.Networking/Manager/NetworkManager.cs
+++ b/Jellyfin.Networking/Manager/NetworkManager.cs
@@ -11,7 +11,6 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Net;
using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
@@ -530,7 +529,7 @@ namespace Jellyfin.Networking.Manager
{
foreach (var lan in _lanSubnets)
{
- var lanPrefix = lan.Prefix;
+ var lanPrefix = lan.BaseAddress;
publishedServerUrls.Add(
new PublishedServerUriOverride(
new IPData(lanPrefix, new IPNetwork(lanPrefix, lan.PrefixLength)),
@@ -541,7 +540,7 @@ namespace Jellyfin.Networking.Manager
}
else if (NetworkUtils.TryParseToSubnet(identifier, out var result) && result is not null)
{
- var data = new IPData(result.Prefix, result);
+ var data = new IPData(result.BaseAddress, result);
publishedServerUrls.Add(
new PublishedServerUriOverride(
data,
@@ -607,7 +606,7 @@ namespace Jellyfin.Networking.Manager
var parts = details.Split(',');
if (NetworkUtils.TryParseToSubnet(parts[0], out var subnet))
{
- var address = subnet.Prefix;
+ var address = subnet.BaseAddress;
var index = int.Parse(parts[1], CultureInfo.InvariantCulture);
if (address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.InterNetworkV6)
{
@@ -881,7 +880,7 @@ namespace Jellyfin.Networking.Manager
{
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 IPAddress.IsLoopback(subnet.BaseAddress) || (_lanSubnets.Any(x => x.Contains(subnet.BaseAddress)) && !_excludedSubnets.Any(x => x.Contains(subnet.BaseAddress)));
}
if (NetworkUtils.TryParseHost(address, out var addresses, IsIPv4Enabled, IsIPv6Enabled))
@@ -1112,12 +1111,12 @@ namespace Jellyfin.Networking.Manager
var logLevel = debug ? LogLevel.Debug : LogLevel.Information;
if (_logger.IsEnabled(logLevel))
{
- _logger.Log(logLevel, "Defined LAN addresses: {0}", _lanSubnets.Select(s => s.Prefix + "/" + s.PrefixLength));
- _logger.Log(logLevel, "Defined LAN exclusions: {0}", _excludedSubnets.Select(s => s.Prefix + "/" + s.PrefixLength));
- _logger.Log(logLevel, "Using LAN addresses: {0}", _lanSubnets.Where(s => !_excludedSubnets.Contains(s)).Select(s => s.Prefix + "/" + s.PrefixLength));
+ _logger.Log(logLevel, "Defined LAN addresses: {0}", _lanSubnets.Select(s => s.BaseAddress + "/" + s.PrefixLength));
+ _logger.Log(logLevel, "Defined LAN exclusions: {0}", _excludedSubnets.Select(s => s.BaseAddress + "/" + s.PrefixLength));
+ _logger.Log(logLevel, "Using LAN addresses: {0}", _lanSubnets.Where(s => !_excludedSubnets.Contains(s)).Select(s => s.BaseAddress + "/" + s.PrefixLength));
_logger.Log(logLevel, "Using bind addresses: {0}", _interfaces.OrderByDescending(x => x.AddressFamily == AddressFamily.InterNetwork).Select(x => x.Address));
_logger.Log(logLevel, "Remote IP filter is {0}", config.IsRemoteIPFilterBlacklist ? "Blocklist" : "Allowlist");
- _logger.Log(logLevel, "Filter list: {0}", _remoteAddressFilter.Select(s => s.Prefix + "/" + s.PrefixLength));
+ _logger.Log(logLevel, "Filter list: {0}", _remoteAddressFilter.Select(s => s.BaseAddress + "/" + s.PrefixLength));
}
}
}
diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index 89f9c08e7..753029f2c 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -30,7 +30,6 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure;
-using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Interfaces;
@@ -280,7 +279,7 @@ namespace Jellyfin.Server.Extensions
{
if (subnet is not null)
{
- AddIPAddress(config, options, subnet.Prefix, subnet.PrefixLength);
+ AddIPAddress(config, options, subnet.BaseAddress, subnet.PrefixLength);
}
}
else if (NetworkUtils.TryParseHost(allowedProxies[i], out var addresses, config.EnableIPv4, config.EnableIPv6))
diff --git a/MediaBrowser.Common/Net/NetworkConstants.cs b/MediaBrowser.Common/Net/NetworkConstants.cs
index 396bc8fb5..fe7ab8963 100644
--- a/MediaBrowser.Common/Net/NetworkConstants.cs
+++ b/MediaBrowser.Common/Net/NetworkConstants.cs
@@ -1,5 +1,4 @@
using System.Net;
-using Microsoft.AspNetCore.HttpOverrides;
namespace MediaBrowser.Common.Net;
diff --git a/MediaBrowser.Common/Net/NetworkUtils.cs b/MediaBrowser.Common/Net/NetworkUtils.cs
index f3bff7fa9..452cb694e 100644
--- a/MediaBrowser.Common/Net/NetworkUtils.cs
+++ b/MediaBrowser.Common/Net/NetworkUtils.cs
@@ -5,6 +5,7 @@ using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using Jellyfin.Extensions;
+using Jellyfin.Networking.Constants;
using Microsoft.AspNetCore.HttpOverrides;
namespace MediaBrowser.Common.Net;
@@ -335,7 +336,7 @@ public static partial class NetworkUtils
/// <returns>The broadcast address.</returns>
public static IPAddress GetBroadcastAddress(IPNetwork network)
{
- var addressBytes = network.Prefix.GetAddressBytes();
+ var addressBytes = network.BaseAddress.GetAddressBytes();
uint ipAddress = BitConverter.ToUInt32(addressBytes, 0);
uint ipMaskV4 = BitConverter.ToUInt32(CidrToMask(network.PrefixLength, AddressFamily.InterNetwork).GetAddressBytes(), 0);
uint broadCastIPAddress = ipAddress | ~ipMaskV4;
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 89ec156a9..1ca5e4327 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -33,7 +33,6 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.HttpOverrides" />
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="MimeTypes">
diff --git a/MediaBrowser.Model/Net/IPData.cs b/MediaBrowser.Model/Net/IPData.cs
index e9fcd6797..e016ffea1 100644
--- a/MediaBrowser.Model/Net/IPData.cs
+++ b/MediaBrowser.Model/Net/IPData.cs
@@ -1,6 +1,5 @@
using System.Net;
using System.Net.Sockets;
-using Microsoft.AspNetCore.HttpOverrides;
namespace MediaBrowser.Model.Net;
@@ -66,9 +65,9 @@ public class IPData
{
if (Address.Equals(IPAddress.None))
{
- return Subnet.Prefix.AddressFamily.Equals(IPAddress.None)
+ return Subnet.BaseAddress.AddressFamily.Equals(IPAddress.None)
? AddressFamily.Unspecified
- : Subnet.Prefix.AddressFamily;
+ : Subnet.BaseAddress.AddressFamily;
}
else
{
diff --git a/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
index f0f8e7afc..0590ded32 100644
--- a/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
+++ b/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
@@ -9,6 +9,8 @@
<TargetFramework>net8.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <!-- TODO: Remove once we update SkiaSharp > 2.88.5 -->
+ <NoWarn>NU1903</NoWarn>
</PropertyGroup>
<ItemGroup>
diff --git a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs
index e0b65cbc4..8d464d4e7 100644
--- a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs
+++ b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs
@@ -6,7 +6,6 @@ using Jellyfin.Server.Extensions;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
@@ -99,7 +98,7 @@ namespace Jellyfin.Server.Tests
Assert.Equal(knownNetworks.Length, options.KnownNetworks.Count);
foreach (var item in knownNetworks)
{
- Assert.NotNull(options.KnownNetworks.FirstOrDefault(x => x.Prefix.Equals(item.Prefix) && x.PrefixLength == item.PrefixLength));
+ Assert.NotNull(options.KnownNetworks.FirstOrDefault(x => x.BaseAddress.Equals(item.BaseAddress) && x.PrefixLength == item.PrefixLength));
}
}