From 9ef79d190b2490a03c566bfaaf963fbba7d124a9 Mon Sep 17 00:00:00 2001 From: Jim Cartlidge Date: Sat, 12 Sep 2020 16:41:37 +0100 Subject: Large number of files --- RSSDP/RSSDP.csproj | 1 + RSSDP/SsdpCommunicationsServer.cs | 5 +++-- RSSDP/SsdpDevicePublisher.cs | 11 +++++------ 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'RSSDP') diff --git a/RSSDP/RSSDP.csproj b/RSSDP/RSSDP.csproj index 664663bd76..3266a05cbf 100644 --- a/RSSDP/RSSDP.csproj +++ b/RSSDP/RSSDP.csproj @@ -6,6 +6,7 @@ + diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index a4be32e7d2..e28a2f48d4 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -7,6 +7,7 @@ using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Networking.Manager; using MediaBrowser.Common.Net; using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; @@ -352,7 +353,7 @@ namespace Rssdp.Infrastructure if (_enableMultiSocketBinding) { - foreach (var address in _networkManager.GetLocalIpAddresses()) + foreach (var address in _networkManager.GetAllBindInterfaces()) { if (address.AddressFamily == AddressFamily.InterNetworkV6) { @@ -362,7 +363,7 @@ namespace Rssdp.Infrastructure try { - sockets.Add(_SocketFactory.CreateSsdpUdpSocket(address, _LocalPort)); + sockets.Add(_SocketFactory.CreateSsdpUdpSocket(address.Address, _LocalPort)); } catch (Exception ex) { diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index 1a8577d8da..43fccdad42 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -5,7 +5,9 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Networking.Manager; using MediaBrowser.Common.Net; +using NetworkCollection; namespace Rssdp.Infrastructure { @@ -300,17 +302,14 @@ namespace Rssdp.Infrastructure foreach (var device in deviceList) { - if (!_sendOnlyMatchedHost || - _networkManager.IsInSameSubnet(device.ToRootDevice().Address, remoteEndPoint.Address, device.ToRootDevice().SubnetMask)) + var ip1 = new IPNetAddress(device.ToRootDevice().Address, device.ToRootDevice().SubnetMask); + var ip2 = new IPNetAddress(remoteEndPoint.Address, device.ToRootDevice().SubnetMask); + if (!_sendOnlyMatchedHost || ip1.NetworkAddress.Equals(ip2.NetworkAddress)) { SendDeviceSearchResponses(device, remoteEndPoint, receivedOnlocalIpAddress, cancellationToken); } } } - else - { - // WriteTrace(String.Format("Sending 0 search responses.")); - } }); } -- cgit v1.2.3 From b44455ad0d6e78b5baed535c06a7f7d49116d1ee Mon Sep 17 00:00:00 2001 From: Jim Cartlidge Date: Mon, 14 Sep 2020 15:46:38 +0100 Subject: Update based on PR1 changes. --- Emby.Dlna/Main/DlnaEntryPoint.cs | 2 +- Emby.Server.Implementations/ApplicationHost.cs | 62 ++- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 2 +- .../TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 2 +- .../LiveTv/TunerHosts/M3UTunerHost.cs | 2 +- Jellyfin.Api/Auth/BaseAuthorizationHandler.cs | 2 +- .../DefaultAuthorizationHandler.cs | 2 +- .../Auth/DownloadPolicy/DownloadHandler.cs | 2 +- ...FirstTimeOrIgnoreParentalControlSetupHandler.cs | 2 +- .../FirstTimeSetupOrDefaultHandler.cs | 2 +- .../FirstTimeSetupOrElevatedHandler.cs | 2 +- .../IgnoreParentalControlHandler.cs | 2 +- .../LocalAccessOrRequiresElevationHandler.cs | 2 +- .../Auth/LocalAccessPolicy/LocalAccessHandler.cs | 2 +- .../RequiresElevationHandler.cs | 2 +- Jellyfin.Api/Controllers/SystemController.cs | 2 +- Jellyfin.Api/Controllers/UserController.cs | 2 +- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 2 +- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 2 +- Jellyfin.Networking/Manager/INetworkManager.cs | 189 --------- Jellyfin.Networking/Manager/NetworkManager.cs | 463 +++++++++++---------- .../Users/UserManager.cs | 3 +- Jellyfin.Server/CoreAppHost.cs | 2 +- .../IpBasedAccessValidationMiddleware.cs | 2 +- .../Middleware/LanFilteringMiddleware.cs | 2 +- Jellyfin.Server/Program.cs | 4 +- MediaBrowser.Common/MediaBrowser.Common.csproj | 3 +- MediaBrowser.Common/Net/INetworkManager.cs | 221 ++++++++++ MediaBrowser.Controller/IServerApplicationHost.cs | 26 +- RSSDP/SsdpCommunicationsServer.cs | 2 +- RSSDP/SsdpDevicePublisher.cs | 2 +- .../LocalAccessPolicy/LocalAccessHandlerTests.cs | 2 +- 32 files changed, 572 insertions(+), 447 deletions(-) delete mode 100644 Jellyfin.Networking/Manager/INetworkManager.cs create mode 100644 MediaBrowser.Common/Net/INetworkManager.cs (limited to 'RSSDP') diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 98f50c09af..a5da2fc5c8 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -9,7 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Emby.Dlna.PlayTo; using Emby.Dlna.Ssdp; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index cc04cb03f5..67a352fc93 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -160,6 +160,11 @@ namespace Emby.Server.Implementations } } + /// + /// Gets the singleton instance. + /// + public INetworkManager NetManager { get; internal set; } + /// /// Occurs when [has pending restart changed]. /// @@ -189,11 +194,6 @@ namespace Emby.Server.Implementations /// The plugins. public IReadOnlyList Plugins => _plugins; - /// - /// Gets the NetworkManager object. - /// - private readonly INetworkManager _networkManager; - /// /// Gets the logger factory. /// @@ -267,7 +267,7 @@ namespace Emby.Server.Implementations ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager); - _networkManager = new NetworkManager((IServerConfigurationManager)ConfigurationManager, LoggerFactory.CreateLogger()); + NetManager = new NetworkManager((IServerConfigurationManager)ConfigurationManager, LoggerFactory.CreateLogger()); Logger = LoggerFactory.CreateLogger(); @@ -524,7 +524,7 @@ namespace Emby.Server.Implementations ServiceCollection.AddSingleton(_fileSystemManager); ServiceCollection.AddSingleton(); - ServiceCollection.AddSingleton(_networkManager); + ServiceCollection.AddSingleton(NetManager); ServiceCollection.AddSingleton(); @@ -1116,7 +1116,7 @@ namespace Emby.Server.Implementations } public IEnumerable GetWakeOnLanInfo() - => _networkManager.GetMacAddresses() + => NetManager.GetMacAddresses() .Select(i => new WakeOnLanInfo(i)) .ToList(); @@ -1138,7 +1138,47 @@ namespace Emby.Server.Implementations public bool ListenWithHttps => Certificate != null && ServerConfigurationManager.Configuration.EnableHttps; /// - public string GetSmartApiUrl(object source) + public string GetSmartApiUrl(IPAddress ipAddress, int? port = null) + { + // Published server ends with a / + if (_startupOptions.PublishedServerUrl != null) + { + // Published server ends with a '/', so we need to remove it. + return _startupOptions.PublishedServerUrl.ToString().Trim('/'); + } + + string smart = NetManager.GetBindInterface(ipAddress, out port); + // If the smartAPI doesn't start with http then treat it as a host or ip. + if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + return smart.Trim('/'); + } + + return GetLocalApiUrl(smart.Trim('/'), null, port); + } + + /// + public string GetSmartApiUrl(HttpRequest request, int? port = null) + { + // Published server ends with a / + if (_startupOptions.PublishedServerUrl != null) + { + // Published server ends with a '/', so we need to remove it. + return _startupOptions.PublishedServerUrl.ToString().Trim('/'); + } + + string smart = NetManager.GetBindInterface(request, out port); + // If the smartAPI doesn't start with http then treat it as a host or ip. + if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + return smart.Trim('/'); + } + + return GetLocalApiUrl(smart.Trim('/'), request.Scheme, port); + } + + /// + public string GetSmartApiUrl(string hostname, int? port = null) { // Published server ends with a / if (_startupOptions.PublishedServerUrl != null) @@ -1147,7 +1187,7 @@ namespace Emby.Server.Implementations return _startupOptions.PublishedServerUrl.ToString().Trim('/'); } - string smart = _networkManager.GetBindInterface(source, out int? port); + string smart = NetManager.GetBindInterface(hostname, out port); // If the smartAPI doesn't start with http then treat it as a host or ip. if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase)) @@ -1155,7 +1195,7 @@ namespace Emby.Server.Implementations return smart.Trim('/'); } - return GetLocalApiUrl(smart.Trim('/'), source is HttpRequest request ? request.Scheme : null, port); + return GetLocalApiUrl(smart.Trim('/'), null, port); } /// diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 1cf129ad2d..27937d2f84 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -10,7 +10,7 @@ using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 02ee302d0d..efb6d162ac 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -7,7 +7,7 @@ using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index f297ecd5df..531a785a07 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; diff --git a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs index 08746b346b..bd76b93bf4 100644 --- a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs +++ b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs @@ -1,7 +1,7 @@ using System.Security.Claims; using Jellyfin.Api.Helpers; using Jellyfin.Data.Enums; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; diff --git a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs index 69e6a8fb2d..dfa366796f 100644 --- a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs +++ b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs b/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs index d1297119cd..3183d13186 100644 --- a/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs +++ b/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs b/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs index 53b5d47787..1cee962e9f 100644 --- a/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs +++ b/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs index abdf2858d0..214198e001 100644 --- a/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs +++ b/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs index ada8a0d4ec..9867ea4ca6 100644 --- a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs +++ b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; using Jellyfin.Api.Constants; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; diff --git a/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs b/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs index 475e3cdac4..affd955515 100644 --- a/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs +++ b/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs b/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs index d022c9067f..fab464b503 100644 --- a/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs +++ b/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; using Jellyfin.Api.Constants; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs b/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs index 418d63de68..801ee2f4c6 100644 --- a/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs +++ b/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs b/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs index a1cddbca3e..7adf72c3d3 100644 --- a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs +++ b/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; using Jellyfin.Api.Constants; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs index 6876b47b44..b87e275d0f 100644 --- a/Jellyfin.Api/Controllers/SystemController.cs +++ b/Jellyfin.Api/Controllers/SystemController.cs @@ -8,7 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 152e650bc0..c60497e304 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -7,7 +7,7 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Authentication; diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 3be8734b94..f81fb88fb1 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -8,7 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Models.StreamingDtos; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index d63e3ab118..9e4def774a 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; diff --git a/Jellyfin.Networking/Manager/INetworkManager.cs b/Jellyfin.Networking/Manager/INetworkManager.cs deleted file mode 100644 index ba571750b9..0000000000 --- a/Jellyfin.Networking/Manager/INetworkManager.cs +++ /dev/null @@ -1,189 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.NetworkInformation; -using MediaBrowser.Model.Configuration; -using NetworkCollection; - -namespace Jellyfin.Networking.Manager -{ - /// - /// Interface for the NetworkManager class. - /// - public interface INetworkManager - { - /// - /// Event triggered on network changes. - /// - event EventHandler NetworkChanged; - - /// - /// Gets the Published server override list. - /// - Dictionary PublishedServerOverrides { get; } - - /// - /// Gets a value indicating whether is all IPv6 interfaces are trusted as internal. - /// - public bool TrustAllIP6Interfaces { get; } - - /// - /// Gets returns the remote address filter. - /// - NetCollection RemoteAddressFilter { get; } - - /// - /// Calculates the list of interfaces to use for Kestrel. - /// - /// A NetCollection object containing all the interfaces to bind. - /// If all the interfaces are specified, and none are excluded, it returns zero items - /// to represent any address. - NetCollection GetAllBindInterfaces(); - - /// - /// Returns a collection containing the loopback interfaces. - /// - /// Netcollection. - public NetCollection GetLoopbacks(); - - /// - /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo) - /// If no bind addresses are specified, an internal interface address is selected. - /// The priority of selection is as follows:- - /// - /// The value contained in the startup parameter --published-server-url. - /// - /// If the user specified custom subnet overrides, the correct subnet for the source address. - /// - /// If the user specified bind interfaces to use:- - /// The bind interface that contains the source subnet. - /// The first bind interface specified that suits best first the source's endpoint. eg. external or internal. - /// - /// If the source is from a public subnet address range and the user hasn't specified any bind addresses:- - /// The first public interface that isn't a loopback and contains the source subnet. - /// The first public interface that isn't a loopback. Priority is given to interfaces with gateways. - /// An internal interface if there are no public ip addresses. - /// - /// If the source is from a private subnet address range and the user hasn't specified any bind addresses:- - /// The first private interface that contains the source subnet. - /// The first private interface that isn't a loopback. Priority is given to interfaces with gateways. - /// - /// If no interfaces meet any of these criteria, then a loopback address is returned. - /// - /// Interface that have been specifically excluded from binding are not used in any of the calculations. - /// - /// Source of the request. - /// Optional port returned, if it's part of an override. - /// IP Address to use, or loopback address if all else fails. - string GetBindInterface(object? source, out int? port); - - /// - /// Checks to see if the ip address is specifically excluded in LocalNetworkAddresses. - /// - /// IP address to check. - /// True if it is. - bool IsExcludedInterface(IPAddress address); - - /// - /// Get a list of all the MAC addresses associated with active interfaces. - /// - /// List of MAC addresses. - List GetMacAddresses(); - - /// - /// Checks to see if the IP Address provided matches an interface that has a gateway. - /// - /// IP to check. Can be an IPAddress or an IPObject. - /// Result of the check. - public bool IsGatewayInterface(object? addressObj); - - /// - /// Returns true if the address is a private address. - /// The config option TrustIP6Interfaces overrides this functions behaviour. - /// - /// Address to check. - /// True or False. - bool IsPrivateAddressRange(IPObject address); - - /// - /// Returns true if the address is part of the user defined LAN. - /// The config option TrustIP6Interfaces overrides this functions behaviour. - /// - /// IP to check. - /// True if endpoint is within the LAN range. - bool IsInLocalNetwork(string address); - - /// - /// Returns true if the address is part of the user defined LAN. - /// The config option TrustIP6Interfaces overrides this functions behaviour. - /// - /// IP to check. - /// True if endpoint is within the LAN range. - bool IsInLocalNetwork(IPObject address); - - /// - /// Returns true if the address is part of the user defined LAN. - /// The config option TrustIP6Interfaces overrides this functions behaviour. - /// - /// IP to check. - /// True if endpoint is within the LAN range. - bool IsInLocalNetwork(IPAddress address); - - /// - /// Attempts to convert the token to an IP address, permitting for interface descriptions and indexes. - /// eg. "eth1", or "TP-LINK Wireless USB Adapter". - /// - /// Token to parse. - /// Resultant object if successful. - /// Success of the operation. - bool TryParseInterface(string token, out IPNetAddress result); - - /// - /// Parses an array of strings into a NetCollection. - /// - /// Values to parse. - /// When true, only include values in []. When false, ignore bracketed values. - /// IPCollection object containing the value strings. - NetCollection CreateIPCollection(string[] values, bool bracketed = false); - - /// - /// Returns all the internal Bind interface addresses. - /// - /// An internal list of interfaces addresses. - NetCollection GetInternalBindAddresses(); - - /// - /// Checks to see if an IP address is still a valid interface address. - /// - /// IP address to check. - /// True if it is. - bool IsValidInterfaceAddress(IPAddress address); - - /// - /// Returns true if the IP address is in the excluded list. - /// - /// IP to check. - /// True if excluded. - bool IsExcluded(IPAddress ip); - - /// - /// Returns true if the IP address is in the excluded list. - /// - /// IP to check. - /// True if excluded. - bool IsExcluded(EndPoint ip); - - /// - /// Gets the filtered LAN ip addresses. - /// - /// Optional filter for the list. - /// Returns a filtered list of LAN addresses. - NetCollection GetFilteredLANSubnets(NetCollection? filter = null); - - /// - /// Reloads all settings and re-initialises the instance. - /// - /// to use. - public void UpdateSettings(ServerConfiguration config); - } -} diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 36a0a94a09..760938c40f 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -7,6 +7,7 @@ using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; using MediaBrowser.Model.Configuration; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; @@ -19,8 +20,6 @@ namespace Jellyfin.Networking.Manager /// public class NetworkManager : INetworkManager, IDisposable { - private static NetworkManager? _instance; - /// /// Contains the description of the interface along with its index. /// @@ -48,7 +47,7 @@ namespace Jellyfin.Networking.Manager /// /// Holds the bind address overrides. /// - private readonly Dictionary _overrideUrls; + private readonly Dictionary _publishedServerUrls; /// /// Used to stop "event-racing conditions". @@ -105,7 +104,7 @@ namespace Jellyfin.Networking.Manager _interfaceAddresses = new NetCollection(unique: false); _macAddresses = new List(); _interfaceNames = new SortedList(); - _overrideUrls = new Dictionary(); + _publishedServerUrls = new Dictionary(); UpdateSettings((ServerConfiguration)_configurationManager.CommonConfiguration); if (!IsIP6Enabled && !IsIP4Enabled) @@ -117,8 +116,6 @@ namespace Jellyfin.Networking.Manager NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged; _configurationManager.ConfigurationUpdated += ConfigurationUpdated; - - Instance = this; } #pragma warning restore CS8618 // Non-nullable field is uninitialized. @@ -127,19 +124,6 @@ namespace Jellyfin.Networking.Manager /// public event EventHandler? NetworkChanged; - /// - /// Gets the singleton of this object. - /// - public static NetworkManager Instance - { - get => GetInstance(); - - internal set - { - _instance = value; - } - } - /// /// Gets the unique network location signature, which is updated on every network change. /// @@ -176,7 +160,7 @@ namespace Jellyfin.Networking.Manager /// /// Gets the Published server override list. /// - public Dictionary PublishedServerOverrides => _overrideUrls; + public Dictionary PublishedServerUrls => _publishedServerUrls; /// public void Dispose() @@ -198,9 +182,12 @@ namespace Jellyfin.Networking.Manager /// public bool IsGatewayInterface(object? addressObj) { - var address = (addressObj is IPAddress addressIP) ? - addressIP : (addressObj is IPObject addressIPObj) ? - addressIPObj.Address : IPAddress.None; + var address = addressObj switch + { + IPAddress addressIp => addressIp, + IPObject addressIpObj => addressIpObj.Address, + _ => IPAddress.None + }; lock (_intLock) { @@ -320,243 +307,127 @@ namespace Jellyfin.Networking.Manager } /// - public string GetBindInterface(object? source, out int? port) + public string GetBindInterface(string source, out int? port) { - bool chromeCast = false; - port = null; - // Parse the source object in an attempt to discover where the request originated. - IPObject sourceAddr; - if (source is HttpRequest sourceReq) + if (!string.IsNullOrEmpty(source)) { - port = sourceReq.Host.Port; - if (IPHost.TryParse(sourceReq.Host.Host, out IPHost host)) + if (string.Equals(source, "chromecast", StringComparison.OrdinalIgnoreCase)) { - sourceAddr = host; - } - else - { - // Assume it's external, as we cannot resolve the host. - sourceAddr = IPHost.None; - } - } - else if (source is string sourceStr && !string.IsNullOrEmpty(sourceStr)) - { - if (string.Equals(sourceStr, "chromecast", StringComparison.OrdinalIgnoreCase)) - { - chromeCast = true; // Just assign a variable so has source = true; - sourceAddr = IPNetAddress.IP4Loopback; + return GetBindInterface(IPNetAddress.IP4Loopback, out port); } - if (IPHost.TryParse(sourceStr, out IPHost host)) + if (IPHost.TryParse(source, out IPHost host)) { - sourceAddr = host; - } - else - { - // Assume it's external, as we cannot resolve the host. - sourceAddr = IPHost.None; + return GetBindInterface(host, out port); } } - else if (source is IPAddress sourceIP) + + return GetBindInterface(IPHost.None, out port); + } + + /// + public string GetBindInterface(IPAddress source, out int? port) + { + return GetBindInterface(new IPNetAddress(source), out port); + } + + /// + public string GetBindInterface(HttpRequest source, out int? port) + { + string result; + + if (source != null && IPHost.TryParse(source.Host.Host, out IPHost host)) { - sourceAddr = new IPNetAddress(sourceIP); + result = GetBindInterface(host, out port); + port ??= source.Host.Port; } else { - // If we have no idea, then assume it came from an external address. - sourceAddr = IPHost.None; + result = GetBindInterface(IPNetAddress.None, out port); + port ??= source?.Host.Port; } + return result; + } + + /// + public string GetBindInterface(IPObject source, out int? port) + { + port = null; + bool isChromeCast = source == IPNetAddress.IP4Loopback; // Do we have a source? - bool haveSource = !sourceAddr.Address.Equals(IPAddress.None); + bool haveSource = !source.Address.Equals(IPAddress.None); + bool isExternal = false; if (haveSource) { - if (!IsIP6Enabled && sourceAddr.AddressFamily == AddressFamily.InterNetworkV6) + if (!IsIP6Enabled && source.AddressFamily == AddressFamily.InterNetworkV6) { _logger.LogWarning("IPv6 is disabled in JellyFin, but enabled in the OS. This may affect how the interface is selected."); } - if (!IsIP4Enabled && sourceAddr.AddressFamily == AddressFamily.InterNetwork) + if (!IsIP4Enabled && source.AddressFamily == AddressFamily.InterNetwork) { _logger.LogWarning("IPv4 is disabled in JellyFin, but enabled in the OS. This may affect how the interface is selected."); } - } - bool isExternal = haveSource && !IsInLocalNetwork(sourceAddr); + isExternal = !IsInLocalNetwork(source); - string bindPreference = string.Empty; - if (haveSource) - { - // Check for user override. - foreach (var addr in _overrideUrls) + if (MatchesPublishedServerUrl(source, isExternal, isChromeCast, out string result, out port)) { - // Remaining. Match anything. - if (addr.Key.Equals(IPAddress.Broadcast)) - { - bindPreference = addr.Value; - break; - } - else if ((addr.Key.Equals(IPAddress.Any) || addr.Key.Equals(IPAddress.IPv6Any)) && (isExternal || chromeCast)) - { - // External. - bindPreference = addr.Value; - break; - } - else if (addr.Key.Contains(sourceAddr)) - { - // Match ip address. - bindPreference = addr.Value; - break; - } + _logger.LogInformation("{0}: Using BindAddress {1}:{2}", source, result, port); + return result; } } _logger.LogDebug("GetBindInterface: Souce: {0}, External: {1}:", haveSource, isExternal); - if (!string.IsNullOrEmpty(bindPreference)) - { - // Has it got a port defined? - var parts = bindPreference.Split(':'); - if (parts.Length > 1) - { - if (int.TryParse(parts[1], out int p)) - { - bindPreference = parts[0]; - port = p; - } - } - - _logger.LogInformation("{0}: Using BindAddress {1}:{2}", sourceAddr, bindPreference, port); - return bindPreference; - } - - string ipresult; - // No preference given, so move on to bind addresses. lock (_intLock) { - var nc = _bindAddresses.Exclude(_bindExclusions).Where(p => !p.IsLoopback()); - - int count = nc.Count(); - if (count == 1 && (_bindAddresses[0].Equals(IPAddress.Any) || _bindAddresses.Equals(IPAddress.IPv6Any))) - { - // Ignore IPAny addresses. - count = 0; - } - - if (count != 0) + if (MatchesBindInterface(source, isExternal, out string result)) { - // Check to see if any of the bind interfaces are in the same subnet. - - IEnumerable bindResult; - IPAddress? defaultGateway = null; - - if (isExternal) - { - // Find all external bind addresses. Store the default gateway, but check to see if there is a better match first. - bindResult = nc.Where(p => !IsInLocalNetwork(p)).OrderBy(p => p.Tag); - defaultGateway = bindResult.FirstOrDefault()?.Address; - bindResult = bindResult.Where(p => p.Contains(sourceAddr)).OrderBy(p => p.Tag); - } - else - { - // Look for the best internal address. - bindResult = nc.Where(p => IsInLocalNetwork(p) && p.Contains(sourceAddr)).OrderBy(p => p.Tag); - } - - if (bindResult.Any()) - { - ipresult = FormatIP6String(bindResult.First().Address); - _logger.LogDebug("{0}: GetBindInterface: Has source, found a match bind interface subnets. {1}", sourceAddr, ipresult); - return ipresult; - } - - if (isExternal && defaultGateway != null) - { - ipresult = FormatIP6String(defaultGateway); - _logger.LogDebug("{0}: GetBindInterface: Using first user defined external interface. {1}", sourceAddr, ipresult); - return ipresult; - } - - ipresult = FormatIP6String(nc.First().Address); - _logger.LogDebug("{0}: GetBindInterface: Selected first user defined interface. {1}", sourceAddr, ipresult); - - if (isExternal) - { - // TODO: remove this after testing. - _logger.LogWarning("{0}: External request received, however, only an internal interface bind found.", sourceAddr); - } - - return ipresult; + return result; } - if (isExternal) + if (isExternal && MatchesExternalInterface(source, out result)) { - // Get the first WAN interface address that isn't a loopback. - var extResult = _interfaceAddresses - .Exclude(_bindExclusions) - .Where(p => !IsInLocalNetwork(p)) - .OrderBy(p => p.Tag); - - if (extResult.Any()) - { - // Does the request originate in one of the interface subnets? - // (For systems with multiple internal network cards, and multiple subnets) - foreach (var intf in extResult) - { - if (!IsInLocalNetwork(intf) && intf.Contains(sourceAddr)) - { - ipresult = FormatIP6String(intf.Address); - _logger.LogDebug("{0}: GetBindInterface: Selected best external on interface on range. {1}", sourceAddr, ipresult); - return ipresult; - } - } - - ipresult = FormatIP6String(extResult.First().Address); - _logger.LogDebug("{0}: GetBindInterface: Selected first external interface. {0}", sourceAddr, ipresult); - return ipresult; - } - - // Have to return something, so return an internal address - - // TODO: remove this after testing. - _logger.LogWarning("{0}: External request received, however, no WAN interface found.", sourceAddr); + return result; } // Get the first LAN interface address that isn't a loopback. - var result = _interfaceAddresses + var interfaces = new NetCollection(_interfaceAddresses .Exclude(_bindExclusions) .Where(p => IsInLocalNetwork(p)) - .OrderBy(p => p.Tag); + .OrderBy(p => p.Tag)); - if (result.Any()) + if (interfaces.Count > 0) { if (haveSource) { // Does the request originate in one of the interface subnets? // (For systems with multiple internal network cards, and multiple subnets) - foreach (var intf in result) + foreach (var intf in interfaces) { - if (intf.Contains(sourceAddr)) + if (intf.Contains(source)) { - ipresult = FormatIP6String(intf.Address); - _logger.LogDebug("{0}: GetBindInterface: Has source, matched best internal interface on range. {1}", sourceAddr, ipresult); - return ipresult; + result = FormatIP6String(intf.Address); + _logger.LogDebug("{0}: GetBindInterface: Has source, matched best internal interface on range. {1}", source, result); + return result; } } } - ipresult = FormatIP6String(result.First().Address); - _logger.LogDebug("{0}: GetBindInterface: Matched first internal interface. {1}", sourceAddr, ipresult); - return ipresult; + result = FormatIP6String(interfaces.First().Address); + _logger.LogDebug("{0}: GetBindInterface: Matched first internal interface. {1}", source, result); + return result; } // There isn't any others, so we'll use the loopback. - ipresult = IsIP6Enabled ? "::" : "127.0.0.1"; - _logger.LogWarning("{0}: GetBindInterface: Loopback return.", sourceAddr, ipresult); - return ipresult; + result = IsIP6Enabled ? "::" : "127.0.0.1"; + _logger.LogWarning("{0}: GetBindInterface: Loopback return.", source, result); + return result; } } @@ -771,16 +642,6 @@ namespace Jellyfin.Networking.Manager } } - private static NetworkManager GetInstance() - { - if (_instance == null) - { - throw new ApplicationException("NetworkManager is not initialised."); - } - - return _instance; - } - private void ConfigurationUpdated(object? sender, EventArgs args) { UpdateSettings((ServerConfiguration)_configurationManager.CommonConfiguration); @@ -944,7 +805,7 @@ namespace Jellyfin.Networking.Manager { lock (_intLock) { - _overrideUrls.Clear(); + _publishedServerUrls.Clear(); } return; @@ -952,7 +813,7 @@ namespace Jellyfin.Networking.Manager lock (_intLock) { - _overrideUrls.Clear(); + _publishedServerUrls.Clear(); foreach (var entry in overrides) { @@ -966,15 +827,15 @@ namespace Jellyfin.Networking.Manager var replacement = parts[1].Trim(); if (string.Equals(parts[0], "remaining", StringComparison.OrdinalIgnoreCase)) { - _overrideUrls[new IPNetAddress(IPAddress.Broadcast)] = replacement; + _publishedServerUrls[new IPNetAddress(IPAddress.Broadcast)] = replacement; } else if (string.Equals(parts[0], "external", StringComparison.OrdinalIgnoreCase)) { - _overrideUrls[new IPNetAddress(IPAddress.Any)] = replacement; + _publishedServerUrls[new IPNetAddress(IPAddress.Any)] = replacement; } else if (TryParseInterface(parts[0], out IPNetAddress address)) { - _overrideUrls[address] = replacement; + _publishedServerUrls[address] = replacement; } else { @@ -1199,5 +1060,179 @@ namespace Jellyfin.Networking.Manager } } } + + /// + /// Attempts to match the source against a user defined bind interface. + /// + /// IP source address to use. + /// True if the source is in the external subnet. + /// True if the request is for a chromecast device. + /// The published server url that matches the source address. + /// The resultant port, if one exists. + /// True if a match is found. + private bool MatchesPublishedServerUrl(IPObject source, bool isExternal, bool isChromeCast, out string bindPreference, out int? port) + { + bindPreference = string.Empty; + port = null; + + // Check for user override. + foreach (var addr in _publishedServerUrls) + { + // Remaining. Match anything. + if (addr.Key.Equals(IPAddress.Broadcast)) + { + bindPreference = addr.Value; + break; + } + else if ((addr.Key.Equals(IPAddress.Any) || addr.Key.Equals(IPAddress.IPv6Any)) && (isExternal || isChromeCast)) + { + // External. + bindPreference = addr.Value; + break; + } + else if (addr.Key.Contains(source)) + { + // Match ip address. + bindPreference = addr.Value; + break; + } + } + + if (!string.IsNullOrEmpty(bindPreference)) + { + // Has it got a port defined? + var parts = bindPreference.Split(':'); + if (parts.Length > 1) + { + if (int.TryParse(parts[1], out int p)) + { + bindPreference = parts[0]; + port = p; + } + } + + return true; + } + + return false; + } + + /// + /// Attempts to match the source against a user defined bind interface. + /// + /// IP source address to use. + /// True if the source is in the external subnet. + /// The result, if a match is found. + /// True if a match is found. + private bool MatchesBindInterface(IPObject source, bool isExternal, out string result) + { + result = string.Empty; + var nc = new NetCollection(_bindAddresses.Exclude(_bindExclusions).Where(p => !p.IsLoopback())); + + int count = nc.Count; + if (count == 1 && (_bindAddresses[0].Equals(IPAddress.Any) || _bindAddresses[0].Equals(IPAddress.IPv6Any))) + { + // Ignore IPAny addresses. + count = 0; + } + + if (count != 0) + { + // Check to see if any of the bind interfaces are in the same subnet. + + NetCollection bindResult; + IPAddress? defaultGateway = null; + IPAddress? bindAddress; + + if (isExternal) + { + // Find all external bind addresses. Store the default gateway, but check to see if there is a better match first. + bindResult = new NetCollection(nc + .Where(p => !IsInLocalNetwork(p)) + .OrderBy(p => p.Tag)); + defaultGateway = bindResult.FirstOrDefault()?.Address; + bindAddress = bindResult + .Where(p => p.Contains(source)) + .OrderBy(p => p.Tag) + .FirstOrDefault()?.Address; + } + else + { + // Look for the best internal address. + bindAddress = nc + .Where(p => IsInLocalNetwork(p) && (p.Contains(source) || p.Equals(IPAddress.None))) + .OrderBy(p => p.Tag) + .FirstOrDefault()?.Address; + } + + if (bindAddress != null) + { + result = FormatIP6String(bindAddress); + _logger.LogDebug("{0}: GetBindInterface: Has source, found a match bind interface subnets. {1}", source, result); + return true; + } + + if (isExternal && defaultGateway != null) + { + result = FormatIP6String(defaultGateway); + _logger.LogDebug("{0}: GetBindInterface: Using first user defined external interface. {1}", source, result); + return true; + } + + result = FormatIP6String(nc.First().Address); + _logger.LogDebug("{0}: GetBindInterface: Selected first user defined interface. {1}", source, result); + + if (isExternal) + { + // TODO: remove this after testing. + _logger.LogWarning("{0}: External request received, however, only an internal interface bind found.", source); + } + + return true; + } + + return false; + } + + /// + /// Attempts to match the source against am external interface. + /// + /// IP source address to use. + /// The result, if a match is found. + /// True if a match is found. + private bool MatchesExternalInterface(IPObject source, out string result) + { + result = string.Empty; + // Get the first WAN interface address that isn't a loopback. + var extResult = new NetCollection(_interfaceAddresses + .Exclude(_bindExclusions) + .Where(p => !IsInLocalNetwork(p)) + .OrderBy(p => p.Tag)); + + if (extResult.Count > 0) + { + // Does the request originate in one of the interface subnets? + // (For systems with multiple internal network cards, and multiple subnets) + foreach (var intf in extResult) + { + if (!IsInLocalNetwork(intf) && intf.Contains(source)) + { + result = FormatIP6String(intf.Address); + _logger.LogDebug("{0}: GetBindInterface: Selected best external on interface on range. {1}", source, result); + return true; + } + } + + result = FormatIP6String(extResult.First().Address); + _logger.LogDebug("{0}: GetBindInterface: Selected first external interface. {0}", source, result); + return true; + } + + // Have to return something, so return an internal address + + // TODO: remove this after testing. + _logger.LogWarning("{0}: External request received, however, no WAN interface found.", source); + return false; + } } } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 8c19665cc1..f73c917a0b 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -12,10 +12,11 @@ using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Data.Events; using Jellyfin.Data.Events.Users; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common; using MediaBrowser.Common.Cryptography; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Events; diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 566ba0ad85..e2d7305af4 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -5,7 +5,7 @@ using System.Reflection; using Emby.Drawing; using Emby.Server.Implementations; using Jellyfin.Drawing.Skia; -using Jellyfin.Networking.Manager; + using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Activity; using Jellyfin.Server.Implementations.Events; diff --git a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs index ff82fe6cc6..e927a147aa 100644 --- a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs +++ b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs @@ -1,6 +1,6 @@ using System.Linq; using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; diff --git a/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs b/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs index 87c82bf583..fa34a167b3 100644 --- a/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs +++ b/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs @@ -2,7 +2,7 @@ using System; using System.Linq; using System.Net; using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 8549e39dbc..939f61656f 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -13,7 +13,7 @@ using CommandLine; using Emby.Server.Implementations; using Emby.Server.Implementations.IO; using Jellyfin.Api.Controllers; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Extensions; using Microsoft.AspNetCore.Hosting; @@ -272,7 +272,7 @@ namespace Jellyfin.Server return builder .UseKestrel((builderContext, options) => { - NetCollection addresses = NetworkManager.Instance.GetAllBindInterfaces(); + NetCollection addresses = appHost.NetManager.GetAllBindInterfaces(); bool flagged = false; foreach (IPObject netAdd in addresses) diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 70dcc2397c..0fda47788b 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -20,8 +20,9 @@ - + + diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs new file mode 100644 index 0000000000..32c017aee6 --- /dev/null +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -0,0 +1,221 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.NetworkInformation; +using MediaBrowser.Model.Configuration; +using Microsoft.AspNetCore.Http; +using NetworkCollection; + +namespace MediaBrowser.Common.Net +{ + /// + /// Interface for the NetworkManager class. + /// + public interface INetworkManager + { + /// + /// Event triggered on network changes. + /// + event EventHandler NetworkChanged; + + /// + /// Gets the published server urls list. + /// + Dictionary PublishedServerUrls { get; } + + /// + /// Gets a value indicating whether is all IPv6 interfaces are trusted as internal. + /// + public bool TrustAllIP6Interfaces { get; } + + /// + /// Gets the remote address filter. + /// + NetCollection RemoteAddressFilter { get; } + + /// + /// Calculates the list of interfaces to use for Kestrel. + /// + /// A NetCollection object containing all the interfaces to bind. + /// If all the interfaces are specified, and none are excluded, it returns zero items + /// to represent any address. + NetCollection GetAllBindInterfaces(); + + /// + /// Returns a collection containing the loopback interfaces. + /// + /// Netcollection. + public NetCollection GetLoopbacks(); + + /// + /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo) + /// If no bind addresses are specified, an internal interface address is selected. + /// The priority of selection is as follows:- + /// + /// The value contained in the startup parameter --published-server-url. + /// + /// If the user specified custom subnet overrides, the correct subnet for the source address. + /// + /// If the user specified bind interfaces to use:- + /// The bind interface that contains the source subnet. + /// The first bind interface specified that suits best first the source's endpoint. eg. external or internal. + /// + /// If the source is from a public subnet address range and the user hasn't specified any bind addresses:- + /// The first public interface that isn't a loopback and contains the source subnet. + /// The first public interface that isn't a loopback. Priority is given to interfaces with gateways. + /// An internal interface if there are no public ip addresses. + /// + /// If the source is from a private subnet address range and the user hasn't specified any bind addresses:- + /// The first private interface that contains the source subnet. + /// The first private interface that isn't a loopback. Priority is given to interfaces with gateways. + /// + /// If no interfaces meet any of these criteria, then a loopback address is returned. + /// + /// Interface that have been specifically excluded from binding are not used in any of the calculations. + /// + /// Source of the request. + /// Optional port returned, if it's part of an override. + /// IP Address to use, or loopback address if all else fails. + string GetBindInterface(IPObject source, out int? port); + + /// + /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo) + /// If no bind addresses are specified, an internal interface address is selected. + /// (See above). + /// + /// Source of the request. + /// Optional port returned, if it's part of an override. + /// IP Address to use, or loopback address if all else fails. + string GetBindInterface(HttpRequest source, out int? port); + + /// + /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo) + /// If no bind addresses are specified, an internal interface address is selected. + /// (See above). + /// + /// IP address of the request. + /// Optional port returned, if it's part of an override. + /// IP Address to use, or loopback address if all else fails. + string GetBindInterface(IPAddress source, out int? port); + + /// + /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo) + /// If no bind addresses are specified, an internal interface address is selected. + /// (See above). + /// + /// Source of the request. + /// Optional port returned, if it's part of an override. + /// IP Address to use, or loopback address if all else fails. + string GetBindInterface(string source, out int? port); + + /// + /// Checks to see if the ip address is specifically excluded in LocalNetworkAddresses. + /// + /// IP address to check. + /// True if it is. + bool IsExcludedInterface(IPAddress address); + + /// + /// Get a list of all the MAC addresses associated with active interfaces. + /// + /// List of MAC addresses. + List GetMacAddresses(); + + /// + /// Checks to see if the IP Address provided matches an interface that has a gateway. + /// + /// IP to check. Can be an IPAddress or an IPObject. + /// Result of the check. + public bool IsGatewayInterface(object? addressObj); + + /// + /// Returns true if the address is a private address. + /// The config option TrustIP6Interfaces overrides this functions behaviour. + /// + /// Address to check. + /// True or False. + bool IsPrivateAddressRange(IPObject address); + + /// + /// Returns true if the address is part of the user defined LAN. + /// The config option TrustIP6Interfaces overrides this functions behaviour. + /// + /// IP to check. + /// True if endpoint is within the LAN range. + bool IsInLocalNetwork(string address); + + /// + /// Returns true if the address is part of the user defined LAN. + /// The config option TrustIP6Interfaces overrides this functions behaviour. + /// + /// IP to check. + /// True if endpoint is within the LAN range. + bool IsInLocalNetwork(IPObject address); + + /// + /// Returns true if the address is part of the user defined LAN. + /// The config option TrustIP6Interfaces overrides this functions behaviour. + /// + /// IP to check. + /// True if endpoint is within the LAN range. + bool IsInLocalNetwork(IPAddress address); + + /// + /// Attempts to convert the token to an IP address, permitting for interface descriptions and indexes. + /// eg. "eth1", or "TP-LINK Wireless USB Adapter". + /// + /// Token to parse. + /// Resultant object if successful. + /// Success of the operation. + bool TryParseInterface(string token, out IPNetAddress result); + + /// + /// Parses an array of strings into a NetCollection. + /// + /// Values to parse. + /// When true, only include values in []. When false, ignore bracketed values. + /// IPCollection object containing the value strings. + NetCollection CreateIPCollection(string[] values, bool bracketed = false); + + /// + /// Returns all the internal Bind interface addresses. + /// + /// An internal list of interfaces addresses. + NetCollection GetInternalBindAddresses(); + + /// + /// Checks to see if an IP address is still a valid interface address. + /// + /// IP address to check. + /// True if it is. + bool IsValidInterfaceAddress(IPAddress address); + + /// + /// Returns true if the IP address is in the excluded list. + /// + /// IP to check. + /// True if excluded. + bool IsExcluded(IPAddress ip); + + /// + /// Returns true if the IP address is in the excluded list. + /// + /// IP to check. + /// True if excluded. + bool IsExcluded(EndPoint ip); + + /// + /// Gets the filtered LAN ip addresses. + /// + /// Optional filter for the list. + /// Returns a filtered list of LAN addresses. + NetCollection GetFilteredLANSubnets(NetCollection? filter = null); + + /// + /// Reloads all settings and re-initialises the instance. + /// + /// to use. + public void UpdateSettings(ServerConfiguration config); + } +} diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index f147e6a86c..7bc1006fc0 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -63,12 +63,28 @@ namespace MediaBrowser.Controller PublicSystemInfo GetPublicSystemInfo(IPAddress address); /// - /// Gets a local (LAN) URL that can be used to access the API. The hostname used is the first valid configured - /// HTTPS will be preferred when available. + /// Gets a URL specific for the request. /// - /// The source of the request. - /// The server URL. - string GetSmartApiUrl(object source); + /// The instance. + /// Optional port number. + /// An accessible URL. + string GetSmartApiUrl(HttpRequest request, int? port = null); + + /// + /// Gets a URL specific for the request. + /// + /// The remote of the connection. + /// Optional port number. + /// An accessible URL. + string GetSmartApiUrl(IPAddress remoteAddr, int? port = null); + + /// + /// Gets a URL specific for the request. + /// + /// The hostname used in the connection. + /// Optional port number. + /// An accessible URL. + string GetSmartApiUrl(string hostname, int? port = null); /// /// Gets a localhost URL that can be used to access the API using the loop-back IP address. diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index e28a2f48d4..932f6bbb40 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -7,7 +7,7 @@ using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Net; using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index 43fccdad42..84456f7dcd 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Net; using NetworkCollection; diff --git a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs index 2c7f0c4f9b..05dd8f325e 100644 --- a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs @@ -4,7 +4,7 @@ using AutoFixture; using AutoFixture.AutoMoq; using Jellyfin.Api.Auth.LocalAccessPolicy; using Jellyfin.Api.Constants; -using Jellyfin.Networking.Manager; + using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; -- cgit v1.2.3 From 38b8110a3ec33267bb2b9e0b75ed0e2a704f41ca Mon Sep 17 00:00:00 2001 From: Jim Cartlidge Date: Mon, 14 Sep 2020 15:55:25 +0100 Subject: Removing blank lines. --- Emby.Dlna/Main/DlnaEntryPoint.cs | 1 - .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 1 - .../LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 1 - Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs | 1 - Jellyfin.Api/Auth/BaseAuthorizationHandler.cs | 1 - .../Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs | 1 - Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs | 1 - .../FirstTimeOrIgnoreParentalControlSetupHandler.cs | 1 - .../FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs | 1 - .../FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs | 1 - .../Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs | 1 - .../LocalAccessOrRequiresElevationHandler.cs | 1 - Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs | 1 - Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs | 1 - Jellyfin.Api/Controllers/UserController.cs | 1 - Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 1 - Jellyfin.Api/Helpers/MediaInfoHelper.cs | 1 - Jellyfin.Server.Implementations/Users/UserManager.cs | 2 -- Jellyfin.Server/CoreAppHost.cs | 1 - Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs | 1 - Jellyfin.Server/Middleware/LanFilteringMiddleware.cs | 1 - Jellyfin.Server/Program.cs | 1 - RSSDP/SsdpCommunicationsServer.cs | 1 - RSSDP/SsdpDevicePublisher.cs | 1 - .../Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs | 1 - 25 files changed, 26 deletions(-) (limited to 'RSSDP') diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index a5da2fc5c8..35ec15623c 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Emby.Dlna.PlayTo; using Emby.Dlna.Ssdp; - using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 27937d2f84..28e30fac8b 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -10,7 +10,6 @@ using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; - using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index efb6d162ac..a8d2a27f78 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -7,7 +7,6 @@ using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; - using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 531a785a07..8107bc427b 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; - using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; diff --git a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs index bd76b93bf4..e245b57681 100644 --- a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs +++ b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs @@ -1,7 +1,6 @@ using System.Security.Claims; using Jellyfin.Api.Helpers; using Jellyfin.Data.Enums; - using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; diff --git a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs index dfa366796f..e988ca6cd1 100644 --- a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs +++ b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; - using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs b/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs index 3183d13186..cea5ac4734 100644 --- a/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs +++ b/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; - using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs b/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs index 1cee962e9f..96cf4db4b5 100644 --- a/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs +++ b/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; - using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs index 214198e001..9815e252ee 100644 --- a/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs +++ b/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; - using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs index 9867ea4ca6..decbe0c035 100644 --- a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs +++ b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; using Jellyfin.Api.Constants; - using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; diff --git a/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs b/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs index affd955515..3eb3ac6cdc 100644 --- a/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs +++ b/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; - using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs b/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs index fab464b503..7bede142e0 100644 --- a/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs +++ b/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; using Jellyfin.Api.Constants; - using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs b/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs index 801ee2f4c6..2999c6c8a8 100644 --- a/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs +++ b/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; - using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs b/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs index 7adf72c3d3..b235c4b63b 100644 --- a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs +++ b/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; using Jellyfin.Api.Constants; - using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index c60497e304..11cf41b838 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -7,7 +7,6 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; - using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Authentication; diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index f81fb88fb1..1d330a3357 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -8,7 +8,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Models.StreamingDtos; - using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index 9e4def774a..b4b6e2d35c 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; - using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index f73c917a0b..ffd0066ee3 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -1,4 +1,3 @@ -#nullable enable #pragma warning disable CA1307 using System; @@ -12,7 +11,6 @@ using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Data.Events; using Jellyfin.Data.Events.Users; - using MediaBrowser.Common; using MediaBrowser.Common.Cryptography; using MediaBrowser.Common.Extensions; diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index e2d7305af4..c2cb677eb5 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -5,7 +5,6 @@ using System.Reflection; using Emby.Drawing; using Emby.Server.Implementations; using Jellyfin.Drawing.Skia; - using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Activity; using Jellyfin.Server.Implementations.Events; diff --git a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs index e927a147aa..7f6b6bcce9 100644 --- a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs +++ b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs @@ -1,6 +1,5 @@ using System.Linq; using System.Threading.Tasks; - using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; diff --git a/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs b/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs index fa34a167b3..7963d0d8c6 100644 --- a/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs +++ b/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs @@ -2,7 +2,6 @@ using System; using System.Linq; using System.Net; using System.Threading.Tasks; - using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 939f61656f..989cc439e6 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -13,7 +13,6 @@ using CommandLine; using Emby.Server.Implementations; using Emby.Server.Implementations.IO; using Jellyfin.Api.Controllers; - using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Extensions; using Microsoft.AspNetCore.Hosting; diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 932f6bbb40..ea9e9a6fb5 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -7,7 +7,6 @@ using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; - using MediaBrowser.Common.Net; using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index 84456f7dcd..c70294b38e 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; - using MediaBrowser.Common.Net; using NetworkCollection; diff --git a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs index 05dd8f325e..553e6a9cab 100644 --- a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs @@ -4,7 +4,6 @@ using AutoFixture; using AutoFixture.AutoMoq; using Jellyfin.Api.Auth.LocalAccessPolicy; using Jellyfin.Api.Constants; - using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; -- cgit v1.2.3 From 53af1e34553917d00e17465f345ea530f04beddf Mon Sep 17 00:00:00 2001 From: Jim Cartlidge Date: Sun, 4 Oct 2020 09:56:33 +0100 Subject: Updatig netcollection & re-inserting BOM --- Emby.Dlna/Main/DlnaEntryPoint.cs | 14 +++++++------- Emby.Server.Implementations/ApplicationHost.cs | 8 ++++---- .../LiveTv/LiveTvMediaSourceProvider.cs | 3 +-- .../LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 3 ++- Emby.Server.Implementations/Udp/UdpServer.cs | 2 +- Jellyfin.Networking/Jellyfin.Networking.csproj | 2 +- RSSDP/SsdpCommunicationsServer.cs | 2 +- RSSDP/SsdpDevicePublisher.cs | 7 ++++--- 8 files changed, 21 insertions(+), 20 deletions(-) (limited to 'RSSDP') diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 33a953563b..a644d2c8cc 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -271,33 +271,33 @@ namespace Emby.Dlna.Main bindAddresses = _networkManager.GetLoopbacks(); } - foreach (var addr in bindAddresses) + foreach (IPNetAddress address in bindAddresses) { - if (addr.AddressFamily == AddressFamily.InterNetworkV6) + if (address.AddressFamily == AddressFamily.InterNetworkV6) { // Not supporting IPv6 right now continue; } // Limit to LAN addresses only - if (!_networkManager.IsInLocalNetwork(addr)) + if (!_networkManager.IsInLocalNetwork(address)) { continue; } var fullService = "urn:schemas-upnp-org:device:MediaServer:1"; - _logger.LogInformation("Registering publisher for {0} on {1}", fullService, addr); + _logger.LogInformation("Registering publisher for {0} on {1}", fullService, address); var descriptorUri = "/dlna/" + udn + "/description.xml"; - var uri = new Uri(_appHost.GetSmartApiUrl(addr.Address) + descriptorUri); + var uri = new Uri(_appHost.GetSmartApiUrl(address.Address) + descriptorUri); var device = new SsdpRootDevice { CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info. Location = uri, // Must point to the URL that serves your devices UPnP description document. - Address = addr.Address, - SubnetMask = ((IPNetAddress)addr).Mask, // MIGRATION: This fields is going. + Address = address.Address, + SubnetMask = address.Mask, FriendlyName = "Jellyfin", Manufacturer = "Jellyfin", ModelName = "Jellyfin Server", diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 66e48ddc65..77584e9d0d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -262,8 +262,8 @@ namespace Emby.Server.Implementations IServiceCollection serviceCollection) { _xmlSerializer = new MyXmlSerializer(); - _jsonSerializer = new JsonSerializer(); - + _jsonSerializer = new JsonSerializer(); + ServiceCollection = serviceCollection; ApplicationPaths = applicationPaths; @@ -1079,7 +1079,7 @@ namespace Emby.Server.Implementations { // No metafile, so lets see if the folder is versioned. metafile = dir.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)[^1]; - + int versionIndex = dir.LastIndexOf('_'); if (versionIndex != -1 && Version.TryParse(dir.Substring(versionIndex + 1), out Version ver)) { @@ -1248,7 +1248,7 @@ namespace Emby.Server.Implementations OperatingSystem = OperatingSystem.Id.ToString(), ServerName = FriendlyName, LocalAddress = GetSmartApiUrl(source), - StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted + StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted }; } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index 598cf0af7c..3a738fd5d2 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -76,7 +76,6 @@ namespace Emby.Server.Implementations.LiveTv } var list = sources.ToList(); - var serverUrl = _appHost.GetSmartApiUrl(string.Empty); foreach (var source in list) { @@ -103,7 +102,7 @@ namespace Emby.Server.Implementations.LiveTv // Dummy this up so that direct play checks can still run if (string.IsNullOrEmpty(source.Path) && source.Protocol == MediaProtocol.Http) { - source.Path = serverUrl; + source.Path = _appHost.GetSmartApiUrl(string.Empty); } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index a8d2a27f78..cfc5278ece 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -16,6 +16,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; +using NetworkCollection.Udp; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { @@ -57,7 +58,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var mediaSource = OriginalMediaSource; var uri = new Uri(mediaSource.Path); - var localPort = 50000; // Will return to random after next PR. + var localPort = UdpHelper.GetRandomUnusedUdpPort(); Directory.CreateDirectory(Path.GetDirectoryName(TempFilePath)); diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index 3dc34da5cf..4fd7ac0c18 100644 --- a/Emby.Server.Implementations/Udp/UdpServer.cs +++ b/Emby.Server.Implementations/Udp/UdpServer.cs @@ -49,7 +49,7 @@ namespace Emby.Server.Implementations.Udp { string localUrl = !string.IsNullOrEmpty(_config[AddressOverrideConfigKey]) ? _config[AddressOverrideConfigKey] - : _appHost.GetSmartApiUrl(string.Empty); // MIGRATION: Temp value. + : _appHost.GetSmartApiUrl(((IPEndPoint)endpoint).Address); if (!string.IsNullOrEmpty(localUrl)) { diff --git a/Jellyfin.Networking/Jellyfin.Networking.csproj b/Jellyfin.Networking/Jellyfin.Networking.csproj index 06d387dc87..7f01f149ec 100644 --- a/Jellyfin.Networking/Jellyfin.Networking.csproj +++ b/Jellyfin.Networking/Jellyfin.Networking.csproj @@ -28,7 +28,7 @@ - + diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index ea9e9a6fb5..8f1f0fa613 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -352,7 +352,7 @@ namespace Rssdp.Infrastructure if (_enableMultiSocketBinding) { - foreach (var address in _networkManager.GetAllBindInterfaces()) + foreach (var address in _networkManager.GetInternalBindAddresses()) { if (address.AddressFamily == AddressFamily.InterNetworkV6) { diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index c70294b38e..ca382c905a 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -301,9 +301,10 @@ namespace Rssdp.Infrastructure foreach (var device in deviceList) { - var ip1 = new IPNetAddress(device.ToRootDevice().Address, device.ToRootDevice().SubnetMask); - var ip2 = new IPNetAddress(remoteEndPoint.Address, device.ToRootDevice().SubnetMask); - if (!_sendOnlyMatchedHost || ip1.NetworkAddress.Equals(ip2.NetworkAddress)) + var root = device.ToRootDevice(); + var source = new IPNetAddress(root.Address, root.SubnetMask); + var destination = new IPNetAddress(remoteEndPoint.Address, root.SubnetMask); + if (!_sendOnlyMatchedHost || source.NetworkAddress.Equals(destination.NetworkAddress)) { SendDeviceSearchResponses(device, remoteEndPoint, receivedOnlocalIpAddress, cancellationToken); } -- cgit v1.2.3 From ec57eeff2b53081e28118fa29c435d2352cdb636 Mon Sep 17 00:00:00 2001 From: Greenback Date: Fri, 30 Oct 2020 14:06:11 +0000 Subject: Updated to latest version of code. --- Emby.Dlna/Main/DlnaEntryPoint.cs | 2 +- Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs | 2 +- RSSDP/SsdpDevicePublisher.cs | 4 ++-- RSSDP/SsdpRootDevice.cs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'RSSDP') diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index a644d2c8cc..504bde996b 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -297,7 +297,7 @@ namespace Emby.Dlna.Main CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info. Location = uri, // Must point to the URL that serves your devices UPnP description document. Address = address.Address, - SubnetMask = address.Mask, + PrefixLength = address.PrefixLength, FriendlyName = "Jellyfin", Manufacturer = "Jellyfin", ModelName = "Jellyfin Server", diff --git a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs index 6f636819fc..c3533d7956 100644 --- a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs +++ b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs @@ -52,7 +52,7 @@ namespace Jellyfin.Server.Middleware if (remoteAddressFilter.Count > 0 && !networkManager.IsInLocalNetwork(remoteIp)) { // remoteAddressFilter is a whitelist or blacklist. - bool isListed = remoteAddressFilter.Contains(remoteIp); + bool isListed = remoteAddressFilter.ContainsAddress(remoteIp); if (!serverConfigurationManager.GetNetworkConfiguration().IsRemoteIPFilterBlacklist) { // Black list, so flip over. diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index ca382c905a..ae175d8c9d 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -302,8 +302,8 @@ namespace Rssdp.Infrastructure foreach (var device in deviceList) { var root = device.ToRootDevice(); - var source = new IPNetAddress(root.Address, root.SubnetMask); - var destination = new IPNetAddress(remoteEndPoint.Address, root.SubnetMask); + var source = new IPNetAddress(root.Address, root.PrefixLength); + var destination = new IPNetAddress(remoteEndPoint.Address, root.PrefixLength); if (!_sendOnlyMatchedHost || source.NetworkAddress.Equals(destination.NetworkAddress)) { SendDeviceSearchResponses(device, remoteEndPoint, receivedOnlocalIpAddress, cancellationToken); diff --git a/RSSDP/SsdpRootDevice.cs b/RSSDP/SsdpRootDevice.cs index 8937ec331f..d5abe4a4ca 100644 --- a/RSSDP/SsdpRootDevice.cs +++ b/RSSDP/SsdpRootDevice.cs @@ -45,9 +45,9 @@ namespace Rssdp public IPAddress Address { get; set; } /// - /// Gets or sets the SubnetMask used to check if the received message from same interface with this device/tree. Required. + /// Gets or sets the prefix length used to check if the received message from same interface with this device/tree. Required. /// - public IPAddress SubnetMask { get; set; } + public byte PrefixLength { get; set; } /// /// The base URL to use for all relative url's provided in other propertise (and those of child devices). Optional. -- cgit v1.2.3 From 83af636c610744903b709117f7f2a7b7e34da1f0 Mon Sep 17 00:00:00 2001 From: Greenback Date: Sat, 31 Oct 2020 18:21:46 +0000 Subject: Updated with new NetManager --- Emby.Dlna/Main/DlnaEntryPoint.cs | 4 +- .../TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 25 +- Jellyfin.Networking/Jellyfin.Networking.csproj | 4 - Jellyfin.Networking/Manager/NetworkManager.cs | 49 ++- .../IpBasedAccessValidationMiddleware.cs | 3 +- .../Middleware/LanFilteringMiddleware.cs | 1 - Jellyfin.Server/Program.cs | 4 +- MediaBrowser.Common/MediaBrowser.Common.csproj | 1 - MediaBrowser.Common/Net/INetworkManager.cs | 4 +- MediaBrowser.Common/Net/IPHost.cs | 447 +++++++++++++++++++++ MediaBrowser.Common/Net/IPNetAddress.cs | 277 +++++++++++++ MediaBrowser.Common/Net/IPObject.cs | 395 ++++++++++++++++++ MediaBrowser.Common/Net/NetworkExtensions.cs | 254 ++++++++++++ MediaBrowser.sln | 7 + RSSDP/SsdpDevicePublisher.cs | 1 - .../Jellyfin.Networking.Tests.csproj | 28 ++ .../NetworkTesting/UnitTesting.cs | 425 ++++++++++++++++++++ 17 files changed, 1895 insertions(+), 34 deletions(-) create mode 100644 MediaBrowser.Common/Net/IPHost.cs create mode 100644 MediaBrowser.Common/Net/IPNetAddress.cs create mode 100644 MediaBrowser.Common/Net/IPObject.cs create mode 100644 MediaBrowser.Common/Net/NetworkExtensions.cs create mode 100644 tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj create mode 100644 tests/Jellyfin.Networking.Tests/NetworkTesting/UnitTesting.cs (limited to 'RSSDP') diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 504bde996b..be618be2b3 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Emby.Dlna.PlayTo; using Emby.Dlna.Ssdp; +using Jellyfin.Networking.Manager; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; @@ -26,7 +27,6 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Net; using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; -using NetworkCollection; using Rssdp; using Rssdp.Infrastructure; using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; @@ -261,7 +261,7 @@ namespace Emby.Dlna.Main { var udn = CreateUuid(_appHost.SystemId); - var bindAddresses = new NetCollection( + var bindAddresses = NetworkManager.CreateCollection( _networkManager.GetInternalBindAddresses() .Where(i => i.AddressFamily == AddressFamily.InterNetwork || (i.AddressFamily == AddressFamily.InterNetworkV6 && i.Address.ScopeId != 0))); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index cfc5278ece..c4176eb7ba 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net; +using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; @@ -16,7 +18,6 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; -using NetworkCollection.Udp; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { @@ -51,6 +52,25 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun EnableStreamSharing = true; } + /// + /// Returns an unused UDP port number in the range specified. + /// + /// Upper and Lower boundary of ports to select. + /// System.Int32. + private static int GetUdpPortFromRange((int Min, int Max) range) + { + var properties = IPGlobalProperties.GetIPGlobalProperties(); + + // Get active udp listeners. + var udpListenerPorts = properties.GetActiveUdpListeners() + .Where(n => n.Port >= range.Min && n.Port <= range.Max) + .Select(n => n.Port); + + return Enumerable.Range(range.Min, range.Max) + .Where(i => !udpListenerPorts.Contains(i)) + .FirstOrDefault(); + } + public override async Task Open(CancellationToken openCancellationToken) { LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested(); @@ -58,7 +78,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var mediaSource = OriginalMediaSource; var uri = new Uri(mediaSource.Path); - var localPort = UdpHelper.GetRandomUnusedUdpPort(); + // Temporary Code to reduce PR size. + var localPort = GetUdpPortFromRange((49152, 65535)); Directory.CreateDirectory(Path.GetDirectoryName(TempFilePath)); diff --git a/Jellyfin.Networking/Jellyfin.Networking.csproj b/Jellyfin.Networking/Jellyfin.Networking.csproj index 330d36a80d..1747a1dc7a 100644 --- a/Jellyfin.Networking/Jellyfin.Networking.csproj +++ b/Jellyfin.Networking/Jellyfin.Networking.csproj @@ -23,10 +23,6 @@ ../jellyfin.ruleset - - - - diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 616774d043..76ac02d791 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -13,8 +13,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -using NetworkCollection; -using NetworkCollection.Udp; +using NetCollection = System.Collections.ObjectModel.Collection; namespace Jellyfin.Networking.Manager { @@ -154,6 +153,22 @@ namespace Jellyfin.Networking.Manager /// public Dictionary PublishedServerUrls => _publishedServerUrls; + /// + /// Creates a new network collection. + /// + /// Items to assign the collection, or null. + /// The collection created. + public static NetCollection CreateCollection(IEnumerable? source) + { + var result = new NetCollection(); + if (source != null) + { + return result.AddRange(source); + } + + return result; + } + /// public void Dispose() { @@ -162,10 +177,10 @@ namespace Jellyfin.Networking.Manager } /// - public List GetMacAddresses() + public IReadOnlyCollection GetMacAddresses() { // Populated in construction - so always has values. - return _macAddresses.ToList(); + return _macAddresses.AsReadOnly(); } /// @@ -187,12 +202,12 @@ namespace Jellyfin.Networking.Manager NetCollection nc = new NetCollection(); if (IsIP4Enabled) { - nc.Add(IPAddress.Loopback); + nc.AddItem(IPAddress.Loopback); } if (IsIP6Enabled) { - nc.Add(IPAddress.IPv6Loopback); + nc.AddItem(IPAddress.IPv6Loopback); } return nc; @@ -276,12 +291,12 @@ namespace Jellyfin.Networking.Manager if (IsIP4Enabled) { - result.Add(IPAddress.Any); + result.AddItem(IPAddress.Any); } if (IsIP6Enabled) { - result.Add(IPAddress.IPv6Any); + result.AddItem(IPAddress.IPv6Any); } return result; @@ -375,7 +390,7 @@ namespace Jellyfin.Networking.Manager } // Get the first LAN interface address that isn't a loopback. - var interfaces = new NetCollection(_interfaceAddresses + var interfaces = CreateCollection(_interfaceAddresses .Exclude(_bindExclusions) .Where(p => IsInLocalNetwork(p)) .OrderBy(p => p.Tag)); @@ -418,11 +433,11 @@ namespace Jellyfin.Networking.Manager if (_bindExclusions.Count > 0) { // Return all the internal interfaces except the ones excluded. - return new NetCollection(_internalInterfaces.Where(p => !_bindExclusions.Contains(p))); + return CreateCollection(_internalInterfaces.Where(p => !_bindExclusions.Contains(p))); } // No bind address, so return all internal interfaces. - return new NetCollection(_internalInterfaces.Where(p => !p.IsLoopback())); + return CreateCollection(_internalInterfaces.Where(p => !p.IsLoopback())); } return new NetCollection(_bindAddresses); @@ -572,7 +587,7 @@ namespace Jellyfin.Networking.Manager } TrustAllIP6Interfaces = config.TrustAllIP6Interfaces; - UdpHelper.EnableMultiSocketBinding = config.EnableMultiSocketBinding; + // UdpHelper.EnableMultiSocketBinding = config.EnableMultiSocketBinding; if (string.IsNullOrEmpty(MockNetworkSettings)) { @@ -941,7 +956,7 @@ namespace Jellyfin.Networking.Manager { _logger.LogDebug("Using LAN interface addresses as user provided no LAN details."); // Internal interfaces must be private and not excluded. - _internalInterfaces = new NetCollection(_interfaceAddresses.Where(i => IsPrivateAddressRange(i) && !_excludedSubnets.Contains(i))); + _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsPrivateAddressRange(i) && !_excludedSubnets.Contains(i))); // Subnets are the same as the calculated internal interface. _lanSubnets = new NetCollection(); @@ -976,7 +991,7 @@ namespace Jellyfin.Networking.Manager } // Internal interfaces must be private, not excluded and part of the LocalNetworkSubnet. - _internalInterfaces = new NetCollection(_interfaceAddresses.Where(i => IsInLocalNetwork(i) && !_excludedSubnets.Contains(i) && _lanSubnets.Contains(i))); + _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsInLocalNetwork(i) && !_excludedSubnets.Contains(i) && _lanSubnets.Contains(i))); } _logger.LogInformation("Defined LAN addresses : {0}", _lanSubnets); @@ -1082,7 +1097,7 @@ namespace Jellyfin.Networking.Manager IPHost host = new IPHost(Dns.GetHostName()); foreach (var a in host.GetAddresses()) { - _interfaceAddresses.Add(a); + _interfaceAddresses.AddItem(a); } if (_interfaceAddresses.Count == 0) @@ -1189,7 +1204,7 @@ namespace Jellyfin.Networking.Manager if (isExternal) { // Find all external bind addresses. Store the default gateway, but check to see if there is a better match first. - bindResult = new NetCollection(nc + bindResult = CreateCollection(nc .Where(p => !IsInLocalNetwork(p)) .OrderBy(p => p.Tag)); defaultGateway = bindResult.FirstOrDefault()?.Address; @@ -1246,7 +1261,7 @@ namespace Jellyfin.Networking.Manager { result = string.Empty; // Get the first WAN interface address that isn't a loopback. - var extResult = new NetCollection(_interfaceAddresses + var extResult = CreateCollection(_interfaceAddresses .Exclude(_bindExclusions) .Where(p => !IsInLocalNetwork(p)) .OrderBy(p => p.Tag)); diff --git a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs index c3533d7956..525cd9ffe2 100644 --- a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs +++ b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs @@ -5,7 +5,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using Microsoft.AspNetCore.Http; -using NetworkCollection; namespace Jellyfin.Server.Middleware { @@ -47,7 +46,7 @@ namespace Jellyfin.Server.Middleware { // 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. - NetCollection remoteAddressFilter = networkManager.RemoteAddressFilter; + var remoteAddressFilter = networkManager.RemoteAddressFilter; if (remoteAddressFilter.Count > 0 && !networkManager.IsInLocalNetwork(remoteIp)) { diff --git a/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs b/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs index 1f4e80053d..8065054a1e 100644 --- a/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs +++ b/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs @@ -7,7 +7,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using Microsoft.AspNetCore.Http; -using NetworkCollection; namespace Jellyfin.Server.Middleware { diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index fd300da7f1..61f7da16a6 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -14,6 +14,7 @@ using Emby.Server.Implementations; using Emby.Server.Implementations.IO; using Jellyfin.Api.Controllers; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Extensions; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Core; @@ -23,7 +24,6 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using NetworkCollection; using Serilog; using Serilog.Extensions.Logging; using SQLitePCL; @@ -271,7 +271,7 @@ namespace Jellyfin.Server return builder .UseKestrel((builderContext, options) => { - NetCollection addresses = appHost.NetManager.GetAllBindInterfaces(); + var addresses = appHost.NetManager.GetAllBindInterfaces(); bool flagged = false; foreach (IPObject netAdd in addresses) diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 0b8f431c04..777136f8bf 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -22,7 +22,6 @@ - diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index f60f369d6a..a7beabbdcb 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Net; using System.Net.NetworkInformation; using Microsoft.AspNetCore.Http; -using NetworkCollection; +using NetCollection = System.Collections.ObjectModel.Collection; namespace MediaBrowser.Common.Net { @@ -130,7 +130,7 @@ namespace MediaBrowser.Common.Net /// Get a list of all the MAC addresses associated with active interfaces. /// /// List of MAC addresses. - List GetMacAddresses(); + IReadOnlyCollection GetMacAddresses(); /// /// Checks to see if the IP Address provided matches an interface that has a gateway. diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs new file mode 100644 index 0000000000..80052727af --- /dev/null +++ b/MediaBrowser.Common/Net/IPHost.cs @@ -0,0 +1,447 @@ +#nullable enable +using System; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Net +{ + /// + /// Object that holds a host name. + /// + public class IPHost : IPObject + { + /// + /// Represents an IPHost that has no value. + /// + public static readonly IPHost None = new IPHost(string.Empty, IPAddress.None); + + /// + /// Time when last resolved. + /// + private long _lastResolved; + + /// + /// Gets the IP Addresses, attempting to resolve the name, if there are none. + /// + private IPAddress[] _addresses; + + /// + /// Initializes a new instance of the class. + /// + /// Host name to assign. + public IPHost(string name) + { + HostName = name ?? throw new ArgumentNullException(nameof(name)); + _addresses = Array.Empty(); + Resolved = false; + } + + /// + /// Initializes a new instance of the class. + /// + /// Host name to assign. + /// Address to assign. + private IPHost(string name, IPAddress address) + { + HostName = name ?? throw new ArgumentNullException(nameof(name)); + _addresses = new IPAddress[] { address ?? throw new ArgumentNullException(nameof(address)) }; + Resolved = !address.Equals(IPAddress.None); + } + + /// + /// Gets or sets the object's first IP address. + /// + public override IPAddress Address + { + get + { + return ResolveHost() ? this[0] : IPAddress.None; + } + + set + { + // Not implemented. + } + } + + /// + /// Gets or sets the object's first IP's subnet prefix. + /// The setter does nothing, but shouldn't raise an exception. + /// + public override byte PrefixLength + { + get + { + return (byte)(ResolveHost() ? 128 : 0); + } + + set + { + // Not implemented. + } + } + + /// + /// Gets or sets timeout value before resolve required, in minutes. + /// + public byte Timeout { get; set; } = 30; + + /// + /// Gets a value indicating whether the address has a value. + /// + public bool HasAddress + { + get + { + return _addresses.Length > 0; + } + } + + /// + /// Gets the host name of this object. + /// + public string HostName { get; } + + /// + /// Gets a value indicating whether this host has attempted to be resolved. + /// + public bool Resolved { get; private set; } + + /// + /// Gets or sets the IP Addresses associated with this object. + /// + /// Index of address. + public IPAddress this[int index] + { + get + { + ResolveHost(); + return index >= 0 && index < _addresses.Length ? _addresses[index] : IPAddress.None; + } + } + + /// + /// Attempts to parse the host string. + /// + /// Host name to parse. + /// Object representing the string, if it has successfully been parsed. + /// Success result of the parsing. + public static bool TryParse(string host, out IPHost hostObj) + { + if (!string.IsNullOrEmpty(host)) + { + // See if it's an IPv6 with port address e.g. [::1]:120. + int i = host.IndexOf("]:", StringComparison.OrdinalIgnoreCase); + if (i != -1) + { + return TryParse(host.Remove(i - 1).TrimStart(' ', '['), out hostObj); + } + else + { + // See if it's an IPv6 in [] with no port. + i = host.IndexOf("]", StringComparison.OrdinalIgnoreCase); + if (i != -1) + { + return TryParse(host.Remove(i - 1).TrimStart(' ', '['), out hostObj); + } + + // Is it a host or IPv4 with port? + string[] hosts = host.Split(':'); + + if (hosts.Length > 2) + { + hostObj = new IPHost(string.Empty, IPAddress.None); + return false; + } + + // Remove port from IPv4 if it exists. + host = hosts[0]; + + if (string.Equals("localhost", host, StringComparison.OrdinalIgnoreCase)) + { + hostObj = new IPHost(host, new IPAddress(Ipv4Loopback)); + return true; + } + + if (IPNetAddress.TryParse(host, out IPNetAddress netIP)) + { + // Host name is an ip address, so fake resolve. + hostObj = new IPHost(host, netIP.Address); + return true; + } + } + + // Only thing left is to see if it's a host string. + if (!string.IsNullOrEmpty(host)) + { + // Use regular expression as CheckHostName isn't RFC5892 compliant. + // Modified from gSkinner's expression at https://stackoverflow.com/questions/11809631/fully-qualified-domain-name-validation + Regex re = new Regex(@"^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){0,127}(?![0-9]*$)[a-z0-9-]+\.?)$", RegexOptions.IgnoreCase | RegexOptions.Multiline); + if (re.Match(host).Success) + { + hostObj = new IPHost(host); + return true; + } + } + } + + hostObj = IPHost.None; + return false; + } + + /// + /// Attempts to parse the host string. + /// + /// Host name to parse. + /// Object representing the string, if it has successfully been parsed. + public static IPHost Parse(string host) + { + if (!string.IsNullOrEmpty(host) && IPHost.TryParse(host, out IPHost res)) + { + return res; + } + + throw new InvalidCastException("Host does not contain a valid value. {host}"); + } + + /// + /// Attempts to parse the host string, ensuring that it resolves only to a specific IP type. + /// + /// Host name to parse. + /// Addressfamily filter. + /// Object representing the string, if it has successfully been parsed. + public static IPHost Parse(string host, AddressFamily family) + { + if (!string.IsNullOrEmpty(host) && IPHost.TryParse(host, out IPHost res)) + { + if (family == AddressFamily.InterNetwork) + { + res.Remove(AddressFamily.InterNetworkV6); + } + else + { + res.Remove(AddressFamily.InterNetwork); + } + + return res; + } + + throw new InvalidCastException("Host does not contain a valid value. {host}"); + } + + /// + /// Returns the Addresses that this item resolved to. + /// + /// IPAddress Array. + public IPAddress[] GetAddresses() + { + ResolveHost(); + return _addresses; + } + + /// + public override bool Contains(IPAddress address) + { + if (address != null && !Address.Equals(IPAddress.None)) + { + if (address.IsIPv4MappedToIPv6) + { + address = address.MapToIPv4(); + } + + foreach (var addr in GetAddresses()) + { + if (address.Equals(addr)) + { + return true; + } + } + } + + return false; + } + + /// + public override bool Equals(IPObject? other) + { + if (other is IPHost otherObj) + { + // Do we have the name Hostname? + if (string.Equals(otherObj.HostName, HostName, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + if (!ResolveHost() || !otherObj.ResolveHost()) + { + return false; + } + + // Do any of our IP addresses match? + foreach (IPAddress addr in _addresses) + { + foreach (IPAddress otherAddress in otherObj._addresses) + { + if (addr.Equals(otherAddress)) + { + return true; + } + } + } + } + + return false; + } + + /// + public override bool IsIP6() + { + // Returns true if interfaces are only IP6. + if (ResolveHost()) + { + foreach (IPAddress i in _addresses) + { + if (i.AddressFamily != AddressFamily.InterNetworkV6) + { + return false; + } + } + + return true; + } + + return false; + } + + /// + public override string ToString() + { + // StringBuilder not optimum here. + string output = string.Empty; + if (_addresses.Length > 0) + { + bool moreThanOne = _addresses.Length > 1; + if (moreThanOne) + { + output = "["; + } + + foreach (var i in _addresses) + { + if (Address.Equals(IPAddress.None) && Address.AddressFamily == AddressFamily.Unspecified) + { + output += HostName + ","; + } + else if (i.Equals(IPAddress.Any)) + { + output += "Any IP4 Address,"; + } + else if (Address.Equals(IPAddress.IPv6Any)) + { + output += "Any IP6 Address,"; + } + else if (i.Equals(IPAddress.Broadcast)) + { + output += "Any Address,"; + } + else + { + output += $"{i}/32,"; + } + } + + output = output[0..^1]; + + if (moreThanOne) + { + output += "]"; + } + } + else + { + output = HostName; + } + + return output; + } + + /// + public override void Remove(AddressFamily family) + { + if (ResolveHost()) + { + _addresses = _addresses.Where(p => p.AddressFamily != family).ToArray(); + } + } + + /// + public override bool Contains(IPObject address) + { + // An IPHost cannot contain another IPObject, it can only be equal. + return Equals(address); + } + + /// + protected override IPObject CalculateNetworkAddress() + { + var netAddr = NetworkAddressOf(this[0], PrefixLength); + return new IPNetAddress(netAddr.Address, netAddr.PrefixLength); + } + + /// + /// Attempt to resolve the ip address of a host. + /// + /// The result of the comparison function. + private bool ResolveHost() + { + // When was the last time we resolved? + if (_lastResolved == 0) + { + _lastResolved = DateTime.Now.Ticks; + } + + // If we haven't resolved before, or out timer has run out... + if ((_addresses.Length == 0 && !Resolved) || (TimeSpan.FromTicks(DateTime.Now.Ticks - _lastResolved).TotalMinutes > Timeout)) + { + _lastResolved = DateTime.Now.Ticks; + ResolveHostInternal().GetAwaiter().GetResult(); + Resolved = true; + } + + return _addresses.Length > 0; + } + + /// + /// Task that looks up a Host name and returns its IP addresses. + /// + /// Array of IPAddress objects. + private async Task ResolveHostInternal() + { + if (!string.IsNullOrEmpty(HostName)) + { + // Resolves the host name - so save a DNS lookup. + if (string.Equals(HostName, "localhost", StringComparison.OrdinalIgnoreCase)) + { + _addresses = new IPAddress[] { new IPAddress(Ipv4Loopback), new IPAddress(Ipv6Loopback) }; + return; + } + + if (Uri.CheckHostName(HostName).Equals(UriHostNameType.Dns)) + { + try + { + IPHostEntry ip = await Dns.GetHostEntryAsync(HostName).ConfigureAwait(false); + _addresses = ip.AddressList; + } + catch (SocketException) + { + // Ignore socket errors, as the result value will just be an empty array. + } + } + } + } + } +} diff --git a/MediaBrowser.Common/Net/IPNetAddress.cs b/MediaBrowser.Common/Net/IPNetAddress.cs new file mode 100644 index 0000000000..bcd049f3d5 --- /dev/null +++ b/MediaBrowser.Common/Net/IPNetAddress.cs @@ -0,0 +1,277 @@ +#nullable enable +using System; +using System.Net; +using System.Net.Sockets; + +namespace MediaBrowser.Common.Net +{ + /// + /// An object that holds and IP address and subnet mask. + /// + public class IPNetAddress : IPObject + { + /// + /// Represents an IPNetAddress that has no value. + /// + public static readonly IPNetAddress None = new IPNetAddress(IPAddress.None); + + /// + /// IPv4 multicast address. + /// + public static readonly IPAddress MulticastIPv4 = IPAddress.Parse("239.255.255.250"); + + /// + /// IPv6 local link multicast address. + /// + public static readonly IPAddress MulticastIPv6LinkLocal = IPAddress.Parse("ff02::C"); + + /// + /// IPv6 site local multicast address. + /// + public static readonly IPAddress MulticastIPv6SiteLocal = IPAddress.Parse("ff05::C"); + + /// + /// IP4Loopback address host. + /// + public static readonly IPNetAddress IP4Loopback = IPNetAddress.Parse("127.0.0.1/32"); + + /// + /// IP6Loopback address host. + /// + public static readonly IPNetAddress IP6Loopback = IPNetAddress.Parse("::1"); + + /// + /// Object's IP address. + /// + private IPAddress _address; + + /// + /// Initializes a new instance of the class. + /// + /// Address to assign. + public IPNetAddress(IPAddress address) + { + _address = address ?? throw new ArgumentNullException(nameof(address)); + PrefixLength = (byte)(address.AddressFamily == AddressFamily.InterNetwork ? 32 : 128); + } + + /// + /// Initializes a new instance of the class. + /// + /// IP Address. + /// Mask as a CIDR. + public IPNetAddress(IPAddress address, byte prefixLength) + { + if (address?.IsIPv4MappedToIPv6 ?? throw new ArgumentNullException(nameof(address))) + { + _address = address.MapToIPv4(); + } + else + { + _address = address; + } + + PrefixLength = prefixLength; + } + + /// + /// Gets or sets the object's IP address. + /// + public override IPAddress Address + { + get + { + return _address; + } + + set + { + _address = value ?? IPAddress.None; + } + } + + /// + public override byte PrefixLength { get; set; } + + /// + /// Try to parse the address and subnet strings into an IPNetAddress object. + /// + /// IP address to parse. Can be CIDR or X.X.X.X notation. + /// Resultant object. + /// True if the values parsed successfully. False if not, resulting in the IP being null. + public static bool TryParse(string addr, out IPNetAddress ip) + { + if (!string.IsNullOrEmpty(addr)) + { + addr = addr.Trim(); + + // Try to parse it as is. + if (IPAddress.TryParse(addr, out IPAddress res)) + { + ip = new IPNetAddress(res); + return true; + } + + // Is it a network? + string[] tokens = addr.Split("/"); + + if (tokens.Length == 2) + { + tokens[0] = tokens[0].TrimEnd(); + tokens[1] = tokens[1].TrimStart(); + + if (IPAddress.TryParse(tokens[0], out res)) + { + // Is the subnet part a cidr? + if (byte.TryParse(tokens[1], out byte cidr)) + { + ip = new IPNetAddress(res, cidr); + return true; + } + + // Is the subnet in x.y.a.b form? + if (IPAddress.TryParse(tokens[1], out IPAddress mask)) + { + ip = new IPNetAddress(res, MaskToCidr(mask)); + return true; + } + } + } + } + + ip = None; + return false; + } + + /// + /// Parses the string provided, throwing an exception if it is badly formed. + /// + /// String to parse. + /// IPNetAddress object. + public static IPNetAddress Parse(string addr) + { + if (TryParse(addr, out IPNetAddress o)) + { + return o; + } + + throw new ArgumentException("Unable to recognise object :" + addr); + } + + /// + public override bool Contains(IPAddress address) + { + if (address == null) + { + throw new ArgumentNullException(nameof(address)); + } + + if (address.IsIPv4MappedToIPv6) + { + address = address.MapToIPv4(); + } + + var altAddress = NetworkAddressOf(address, PrefixLength); + return NetworkAddress.Address.Equals(altAddress.Address) && NetworkAddress.PrefixLength >= altAddress.PrefixLength; + } + + /// + public override bool Contains(IPObject address) + { + if (address is IPHost addressObj && addressObj.HasAddress) + { + foreach (IPAddress addr in addressObj.GetAddresses()) + { + if (Contains(addr)) + { + return true; + } + } + } + else if (address is IPNetAddress netaddrObj) + { + // Have the same network address, but different subnets? + if (NetworkAddress.Address.Equals(netaddrObj.NetworkAddress.Address)) + { + return NetworkAddress.PrefixLength <= netaddrObj.PrefixLength; + } + + var altAddress = NetworkAddressOf(netaddrObj.Address, PrefixLength); + return NetworkAddress.Address.Equals(altAddress.Address); + } + + return false; + } + + /// + public override bool Equals(IPObject? other) + { + if (other is IPNetAddress otherObj && !Address.Equals(IPAddress.None) && !otherObj.Address.Equals(IPAddress.None)) + { + return Address.Equals(otherObj.Address) && + PrefixLength == otherObj.PrefixLength; + } + + return false; + } + + /// + public override bool Equals(IPAddress address) + { + if (address != null && !address.Equals(IPAddress.None) && !Address.Equals(IPAddress.None)) + { + return address.Equals(Address); + } + + return false; + } + + /// + public override string ToString() + { + return ToString(false); + } + + /// + /// Returns a textual representation of this object. + /// + /// Set to true, if the subnet is to be included as part of the address. + /// String representation of this object. + public string ToString(bool shortVersion) + { + if (!Address.Equals(IPAddress.None)) + { + if (Address.Equals(IPAddress.Any)) + { + return "Any IP4 Address"; + } + + if (Address.Equals(IPAddress.IPv6Any)) + { + return "Any IP6 Address"; + } + + if (Address.Equals(IPAddress.Broadcast)) + { + return "Any Address"; + } + + if (shortVersion) + { + return Address.ToString(); + } + + return $"{Address}/{PrefixLength}"; + } + + return string.Empty; + } + + /// + protected override IPObject CalculateNetworkAddress() + { + var value = NetworkAddressOf(_address, PrefixLength); + return new IPNetAddress(value.Address, value.PrefixLength); + } + } +} diff --git a/MediaBrowser.Common/Net/IPObject.cs b/MediaBrowser.Common/Net/IPObject.cs new file mode 100644 index 0000000000..a08694c266 --- /dev/null +++ b/MediaBrowser.Common/Net/IPObject.cs @@ -0,0 +1,395 @@ +#nullable enable +using System; +using System.Net; +using System.Net.Sockets; + +namespace MediaBrowser.Common.Net +{ + /// + /// Base network object class. + /// + public abstract class IPObject : IEquatable + { + /// + /// IPv6 Loopback address. + /// + protected static readonly byte[] Ipv6Loopback = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + + /// + /// IPv4 Loopback address. + /// + protected static readonly byte[] Ipv4Loopback = { 127, 0, 0, 1 }; + + /// + /// The network address of this object. + /// + private IPObject? _networkAddress; + + /// + /// Gets or sets the user defined functions that need storage in this object. + /// + public int Tag { get; set; } + + /// + /// Gets or sets the object's IP address. + /// + public abstract IPAddress Address { get; set; } + + /// + /// Gets the object's network address. + /// + public IPObject NetworkAddress + { + get + { + if (_networkAddress == null) + { + _networkAddress = CalculateNetworkAddress(); + } + + return _networkAddress; + } + } + + /// + /// Gets or sets the object's IP address. + /// + public abstract byte PrefixLength { get; set; } + + /// + /// Gets the AddressFamily of this object. + /// + public AddressFamily AddressFamily + { + get + { + // Keep terms separate as Address performs other functions in inherited objects. + IPAddress address = Address; + return address.Equals(IPAddress.None) ? AddressFamily.Unspecified : address.AddressFamily; + } + } + + /// + /// Returns the network address of an object. + /// + /// IP Address to convert. + /// Subnet prefix. + /// IPAddress. + public static (IPAddress Address, byte PrefixLength) NetworkAddressOf(IPAddress address, byte prefixLength) + { + if (address == null) + { + throw new ArgumentNullException(nameof(address)); + } + + if (address.IsIPv4MappedToIPv6) + { + address = address.MapToIPv4(); + } + + if (IsLoopback(address)) + { + return (Address: address, PrefixLength: prefixLength); + } + + byte[] addressBytes = address.GetAddressBytes(); + + int div = prefixLength / 8; + int mod = prefixLength % 8; + if (mod != 0) + { + mod = 8 - mod; + addressBytes[div] = (byte)((int)addressBytes[div] >> mod << mod); + div++; + } + + for (int octet = div; octet < addressBytes.Length; octet++) + { + addressBytes[octet] = 0; + } + + return (Address: new IPAddress(addressBytes), PrefixLength: prefixLength); + } + + /// + /// Tests to see if the ip address is a Loopback address. + /// + /// Value to test. + /// True if it is. + public static bool IsLoopback(IPAddress address) + { + if (address == null) + { + throw new ArgumentNullException(nameof(address)); + } + + if (!address.Equals(IPAddress.None)) + { + if (address.IsIPv4MappedToIPv6) + { + address = address.MapToIPv4(); + } + + return address.Equals(IPAddress.Loopback) || address.Equals(IPAddress.IPv6Loopback); + } + + return false; + } + + /// + /// Tests to see if the ip address is an IP6 address. + /// + /// Value to test. + /// True if it is. + public static bool IsIP6(IPAddress address) + { + if (address == null) + { + throw new ArgumentNullException(nameof(address)); + } + + if (address.IsIPv4MappedToIPv6) + { + address = address.MapToIPv4(); + } + + return !address.Equals(IPAddress.None) && (address.AddressFamily == AddressFamily.InterNetworkV6); + } + + /// + /// Tests to see if the address in the private address range. + /// + /// Object to test. + /// True if it contains a private address. + public static bool IsPrivateAddressRange(IPAddress address) + { + if (address == null) + { + throw new ArgumentNullException(nameof(address)); + } + + if (!address.Equals(IPAddress.None)) + { + if (address.AddressFamily == AddressFamily.InterNetwork) + { + if (address.IsIPv4MappedToIPv6) + { + address = address.MapToIPv4(); + } + + byte[] octet = address.GetAddressBytes(); + + return (octet[0] == 10) || + (octet[0] == 172 && octet[1] >= 16 && octet[1] <= 31) || // RFC1918 + (octet[0] == 192 && octet[1] == 168) || // RFC1918 + (octet[0] == 127); // RFC1122 + } + else + { + byte[] octet = address.GetAddressBytes(); + uint word = (uint)(octet[0] << 8) + octet[1]; + + return (word >= 0xfe80 && word <= 0xfebf) || // fe80::/10 :Local link. + (word >= 0xfc00 && word <= 0xfdff); // fc00::/7 :Unique local address. + } + } + + return false; + } + + /// + /// Returns true if the IPAddress contains an IP6 Local link address. + /// + /// IPAddress object to check. + /// True if it is a local link address. + /// See https://stackoverflow.com/questions/6459928/explain-the-instance-properties-of-system-net-ipaddress + /// it appears that the IPAddress.IsIPv6LinkLocal is out of date. + /// + public static bool IsIPv6LinkLocal(IPAddress address) + { + if (address == null) + { + throw new ArgumentNullException(nameof(address)); + } + + if (address.IsIPv4MappedToIPv6) + { + address = address.MapToIPv4(); + } + + if (address.AddressFamily != AddressFamily.InterNetworkV6) + { + return false; + } + + byte[] octet = address.GetAddressBytes(); + uint word = (uint)(octet[0] << 8) + octet[1]; + + return word >= 0xfe80 && word <= 0xfebf; // fe80::/10 :Local link. + } + + /// + /// Convert a subnet mask in CIDR notation to a dotted decimal string value. IPv4 only. + /// + /// Subnet mask in CIDR notation. + /// IPv4 or IPv6 family. + /// String value of the subnet mask in dotted decimal notation. + public static IPAddress CidrToMask(byte cidr, AddressFamily family) + { + uint addr = 0xFFFFFFFF << (family == AddressFamily.InterNetwork ? 32 : 128 - cidr); + addr = + ((addr & 0xff000000) >> 24) | + ((addr & 0x00ff0000) >> 8) | + ((addr & 0x0000ff00) << 8) | + ((addr & 0x000000ff) << 24); + return new IPAddress(addr); + } + + /// + /// Convert a mask to a CIDR. IPv4 only. + /// https://stackoverflow.com/questions/36954345/get-cidr-from-netmask. + /// + /// Subnet mask. + /// Byte CIDR representing the mask. + public static byte MaskToCidr(IPAddress mask) + { + if (mask == null) + { + throw new ArgumentNullException(nameof(mask)); + } + + byte cidrnet = 0; + if (!mask.Equals(IPAddress.Any)) + { + byte[] bytes = mask.GetAddressBytes(); + + var zeroed = false; + for (var i = 0; i < bytes.Length; i++) + { + for (int v = bytes[i]; (v & 0xFF) != 0; v <<= 1) + { + if (zeroed) + { + // Invalid netmask. + return (byte)~cidrnet; + } + + if ((v & 0x80) == 0) + { + zeroed = true; + } + else + { + cidrnet++; + } + } + } + } + + return cidrnet; + } + + /// + /// Tests to see if this object is a Loopback address. + /// + /// True if it is. + public virtual bool IsLoopback() + { + return IsLoopback(Address); + } + + /// + /// Removes all addresses of a specific type from this object. + /// + /// Type of address to remove. + public virtual void Remove(AddressFamily family) + { + // This method only peforms a function in the IPHost implementation of IPObject. + } + + /// + /// Tests to see if this object is an IPv6 address. + /// + /// True if it is. + public virtual bool IsIP6() + { + return IsIP6(Address); + } + + /// + /// Returns true if this IP address is in the RFC private address range. + /// + /// True this object has a private address. + public virtual bool IsPrivateAddressRange() + { + return IsPrivateAddressRange(Address); + } + + /// + /// Compares this to the object passed as a parameter. + /// + /// Object to compare to. + /// Equality result. + public virtual bool Equals(IPAddress ip) + { + if (ip != null) + { + if (ip.IsIPv4MappedToIPv6) + { + ip = ip.MapToIPv4(); + } + + return !Address.Equals(IPAddress.None) && Address.Equals(ip); + } + + return false; + } + + /// + /// Compares this to the object passed as a parameter. + /// + /// Object to compare to. + /// Equality result. + public virtual bool Equals(IPObject? other) + { + if (other != null && other is IPObject otherObj) + { + return !Address.Equals(IPAddress.None) && Address.Equals(otherObj.Address); + } + + return false; + } + + /// + /// Compares the address in this object and the address in the object passed as a parameter. + /// + /// Object's IP address to compare to. + /// Comparison result. + public abstract bool Contains(IPObject address); + + /// + /// Compares the address in this object and the address in the object passed as a parameter. + /// + /// Object's IP address to compare to. + /// Comparison result. + public abstract bool Contains(IPAddress address); + + /// + public override int GetHashCode() + { + return Address.GetHashCode(); + } + + /// + public override bool Equals(object obj) + { + return Equals(obj as IPObject); + } + + /// + /// Calculates the network address of this object. + /// + /// Returns the network address of this object. + protected abstract IPObject CalculateNetworkAddress(); + } +} diff --git a/MediaBrowser.Common/Net/NetworkExtensions.cs b/MediaBrowser.Common/Net/NetworkExtensions.cs new file mode 100644 index 0000000000..6e9cb46dc1 --- /dev/null +++ b/MediaBrowser.Common/Net/NetworkExtensions.cs @@ -0,0 +1,254 @@ +#pragma warning disable CA1062 // Validate arguments of public methods +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using System.Runtime.CompilerServices; +using NetCollection = System.Collections.ObjectModel.Collection; + +namespace MediaBrowser.Common.Net +{ + /// + /// Defines the . + /// + public static class NetworkExtensions + { + /// + /// Add an address to the collection. + /// + /// The . + /// Item to add. + public static void AddItem(this NetCollection source, IPAddress ip) + { + if (!source.ContainsAddress(ip)) + { + source.Add(new IPNetAddress(ip, 32)); + } + } + + /// + /// Add multiple items to the collection. + /// + /// The . + /// Item to add. + /// Return the collection. + public static NetCollection AddRange(this NetCollection destination, IEnumerable source) + { + foreach (var item in source) + { + destination.Add(item); + } + + return destination; + } + + /// + /// Adds a network to the collection. + /// + /// The . + /// Item to add. + public static void AddItem(this NetCollection source, IPObject item) + { + if (!source.ContainsAddress(item)) + { + source.Add(item); + } + } + + /// + /// Converts this object to a string. + /// + /// The . + /// Returns a string representation of this object. + public static string Readable(this NetCollection source) + { + string output = "["; + if (source.Count > 0) + { + foreach (var i in source) + { + output += $"{i},"; + } + + output = output[0..^1]; + } + + return $"{output}]"; + } + + /// + /// Returns true if the collection contains an item with the ip address, + /// or the ip address falls within any of the collection's network ranges. + /// + /// The . + /// The item to look for. + /// True if the collection contains the item. + public static bool ContainsAddress(this NetCollection source, IPAddress item) + { + if (source.Count == 0) + { + return false; + } + + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + if (item.IsIPv4MappedToIPv6) + { + item = item.MapToIPv4(); + } + + foreach (var i in source) + { + if (i.Contains(item)) + { + return true; + } + } + + return false; + } + + /// + /// Returns true if the collection contains an item with the ip address, + /// or the ip address falls within any of the collection's network ranges. + /// + /// The . + /// The item to look for. + /// True if the collection contains the item. + public static bool ContainsAddress(this NetCollection source, IPObject item) + { + if (source.Count == 0) + { + return false; + } + + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + foreach (var i in source) + { + if (i.Contains(item)) + { + return true; + } + } + + return false; + } + + /// + /// Returns a collection containing the subnets of this collection given. + /// + /// The . + /// NetCollection object containing the subnets. + public static NetCollection AsNetworks(this NetCollection source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + NetCollection res = new NetCollection(); + + foreach (IPObject i in source) + { + if (i is IPNetAddress nw) + { + // Add the subnet calculated from the interface address/mask. + var na = nw.NetworkAddress; + na.Tag = i.Tag; + res.Add(na); + } + else + { + // Flatten out IPHost and add all its ip addresses. + foreach (var addr in ((IPHost)i).GetAddresses()) + { + IPNetAddress host = new IPNetAddress(addr) + { + Tag = i.Tag + }; + + res.Add(host); + } + } + } + + return res; + } + + /// + /// Excludes all the items from this list that are found in excludeList. + /// + /// The . + /// Items to exclude. + /// A new collection, with the items excluded. + public static NetCollection Exclude(this NetCollection source, NetCollection excludeList) + { + if (source.Count == 0 || excludeList == null) + { + return new NetCollection(source); + } + + NetCollection results = new NetCollection(); + + bool found; + foreach (var outer in source) + { + found = false; + + foreach (var inner in excludeList) + { + if (outer.Equals(inner)) + { + found = true; + break; + } + } + + if (!found) + { + results.Add(outer); + } + } + + return results; + } + + /// + /// Returns all items that co-exist in this object and target. + /// + /// The . + /// Collection to compare with. + /// A collection containing all the matches. + public static NetCollection Union(this NetCollection source, NetCollection target) + { + if (source.Count == 0) + { + return new NetCollection(); + } + + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + + NetCollection nc = new NetCollection(); + + foreach (IPObject i in source) + { + if (target.ContainsAddress(i)) + { + nc.Add(i); + } + } + + return nc; + } + } +} diff --git a/MediaBrowser.sln b/MediaBrowser.sln index d460c0ab0c..cb204137bd 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -68,6 +68,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Implementat EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking", "Jellyfin.Networking\Jellyfin.Networking.csproj", "{0A3FCC4D-C714-4072-B90F-E374A15F9FF9}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\NetworkTesting\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -182,6 +184,10 @@ Global {0A3FCC4D-C714-4072-B90F-E374A15F9FF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {0A3FCC4D-C714-4072-B90F-E374A15F9FF9}.Release|Any CPU.ActiveCfg = Release|Any CPU {0A3FCC4D-C714-4072-B90F-E374A15F9FF9}.Release|Any CPU.Build.0 = Release|Any CPU + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -193,6 +199,7 @@ Global {A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {462584F7-5023-4019-9EAC-B98CA458C0A0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index ae175d8c9d..3e89d7f602 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -6,7 +6,6 @@ using System.Net; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; -using NetworkCollection; namespace Rssdp.Infrastructure { diff --git a/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj new file mode 100644 index 0000000000..fa18316dff --- /dev/null +++ b/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj @@ -0,0 +1,28 @@ + + + + + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} + + + + netcoreapp3.1 + false + true + enable + + + + + + + + + + + + + + + + diff --git a/tests/Jellyfin.Networking.Tests/NetworkTesting/UnitTesting.cs b/tests/Jellyfin.Networking.Tests/NetworkTesting/UnitTesting.cs new file mode 100644 index 0000000000..9e7e8d3ac3 --- /dev/null +++ b/tests/Jellyfin.Networking.Tests/NetworkTesting/UnitTesting.cs @@ -0,0 +1,425 @@ +using System; +using System.Net; +using Emby.Dlna.PlayTo; +using Jellyfin.Networking.Configuration; +using Jellyfin.Networking.Manager; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; +using Moq; +using Microsoft.Extensions.Logging.Abstractions; +using Xunit; +using NetCollection = System.Collections.ObjectModel.Collection; +using XMLProperties = System.Collections.Generic.Dictionary; + +namespace NetworkTesting +{ + public class NetTesting + { + /// + /// Trys to identify the string and return an object of that class. + /// + /// String to parse. + /// IPObject to return. + /// True if the value parsed successfully. + private static bool TryParse(string addr, out IPObject result) + { + if (!string.IsNullOrEmpty(addr)) + { + // Is it an IP address + if (IPNetAddress.TryParse(addr, out IPNetAddress nw)) + { + result = nw; + return true; + } + + if (IPHost.TryParse(addr, out IPHost h)) + { + result = h; + return true; + } + } + + result = IPNetAddress.None; + return false; + } + + + private IConfigurationManager GetMockConfig(NetworkConfiguration conf) + { + var configManager = new Mock + { + CallBase = true + }; + configManager.Setup(x => x.GetConfiguration(It.IsAny())).Returns(conf); + return (IConfigurationManager)configManager.Object; + } + + [Theory] + [InlineData("192.168.1.208/24,-16,eth16:200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.0/24", "[192.168.1.208/24,200.200.200.200/24]")] + [InlineData("192.168.1.208/24,-16,eth16:200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.208/24]")] + [InlineData("192.168.1.208/24,-16,vEthernet1:192.168.1.208/24,-16,vEthernet212;200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.208/24]")] + public void IgnoreVirtualInterfaces(string interfaces, string lan, string value) + { + var conf = new NetworkConfiguration() + { + EnableIPV6 = true, + EnableIPV4 = true, + LocalNetworkSubnets = lan.Split(';') + }; + + NetworkManager.MockNetworkSettings = interfaces; + var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + NetworkManager.MockNetworkSettings = string.Empty; + + Assert.True(string.Equals(nm.GetInternalBindAddresses().ToString(), value, StringComparison.Ordinal)); + } + + [Theory] + [InlineData("192.168.10.0/24, !192.168.10.60/32", "192.168.10.60")] + public void TextIsInNetwork(string network, string value) + { + var conf = new NetworkConfiguration() + { + EnableIPV6 = true, + EnableIPV4 = true, + LocalNetworkSubnets = network.Split(',') + }; + + var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + + Assert.True(!nm.IsInLocalNetwork(value)); + } + + [Theory] + [InlineData("127.0.0.1")] + [InlineData("127.0.0.1:123")] + [InlineData("localhost")] + [InlineData("localhost:1345")] + [InlineData("www.google.co.uk")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")] + [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]:124")] + [InlineData("fe80::7add:12ff:febb:c67b%16")] + [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")] + [InlineData("192.168.1.2/255.255.255.0")] + [InlineData("192.168.1.2/24")] + + public void TestCollectionCreation(string address) + { + Assert.True(TryParse(address, out _)); + } + + [Theory] + [InlineData("256.128.0.0.0.1")] + [InlineData("127.0.0.1#")] + [InlineData("localhost!")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")] + public void TestInvalidCollectionCreation(string address) + { + Assert.False(TryParse(address, out _)); + } + + [Theory] + // Src, IncIP6, incIP4, exIP6, ecIP4, net + [InlineData("127.0.0.1#", + "[]", + "[]", + "[]", + "[]", + "[]")] + [InlineData("[127.0.0.1]", + "[]", + "[]", + "[127.0.0.1/32]", + "[127.0.0.1/32]", + "[]")] + [InlineData("", + "[]", + "[]", + "[]", + "[]", + "[]")] + [InlineData("192.158.1.2/255.255.0.0,192.169.1.2/8", + "[192.158.1.2/16,192.169.1.2/8]", + "[192.158.1.2/16,192.169.1.2/8]", + "[]", + "[]", + "[192.158.0.0/16,192.0.0.0/8]")] + [InlineData("192.158.1.2/16, localhost, fd23:184f:2029:0:3139:7386:67d7:d517, [10.10.10.10]", + "[192.158.1.2/16,127.0.0.1/32,fd23:184f:2029:0:3139:7386:67d7:d517/128]", + "[192.158.1.2/16,127.0.0.1/32]", + "[10.10.10.10/32]", + "[10.10.10.10/32]", + "[192.158.0.0/16,127.0.0.1/32,fd23:184f:2029:0:3139:7386:67d7:d517/128]")] + public void TestCollections(string settings, string result1, string result2, string result3, string result4, string result5) + { + var conf = new NetworkConfiguration() + { + EnableIPV6 = true, + EnableIPV4 = true, + }; + + var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + + // Test included, IP6. + NetCollection nc = nm.CreateIPCollection(settings.Split(","), false); + Assert.True(string.Equals(nc.ToString(), result1, System.StringComparison.OrdinalIgnoreCase)); + + // Text excluded, non IP6. + nc = nm.CreateIPCollection(settings.Split(","), true); + Assert.True(string.Equals(nc?.ToString(), result3, System.StringComparison.OrdinalIgnoreCase)); + + conf.EnableIPV6 = false; + nm.UpdateSettings(conf); + + // Test included, non IP6. + nc = nm.CreateIPCollection(settings.Split(","), false); + Assert.True(string.Equals(nc.ToString(), result2, System.StringComparison.OrdinalIgnoreCase)); + + // Test excluded, including IPv6. + nc = nm.CreateIPCollection(settings.Split(","), true); + Assert.True(string.Equals(nc.ToString(), result4, System.StringComparison.OrdinalIgnoreCase)); + + conf.EnableIPV6 = true; + nm.UpdateSettings(conf); + + // Test network addresses of collection. + nc = nm.CreateIPCollection(settings.Split(","), false); + nc = nc.AsNetworks(); + Assert.True(string.Equals(nc.ToString(), result5, System.StringComparison.OrdinalIgnoreCase)); + } + + [Theory] + [InlineData("127.0.0.1", "fd23:184f:2029:0:3139:7386:67d7:d517/64,fd23:184f:2029:0:c0f0:8a8a:7605:fffa/128,fe80::3139:7386:67d7:d517%16/64,192.168.1.208/24,::1/128,127.0.0.1/8", "[127.0.0.1/32]")] + [InlineData("127.0.0.1", "127.0.0.1/8", "[127.0.0.1/32]")] + public void UnionCheck(string settings, string compare, string result) + { + var conf = new NetworkConfiguration() + { + EnableIPV6 = true, + EnableIPV4 = true, + }; + + var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + + NetCollection nc1 = nm.CreateIPCollection(settings.Split(","), false); + NetCollection nc2 = nm.CreateIPCollection(compare.Split(","), false); + + Assert.True(nc1.Union(nc2).ToString() == result); + } + + [Theory] + [InlineData("192.168.5.85/24", "192.168.5.1")] + [InlineData("192.168.5.85/24", "192.168.5.254")] + [InlineData("10.128.240.50/30", "10.128.240.48")] + [InlineData("10.128.240.50/30", "10.128.240.49")] + [InlineData("10.128.240.50/30", "10.128.240.50")] + [InlineData("10.128.240.50/30", "10.128.240.51")] + [InlineData("127.0.0.1/8", "127.0.0.1")] + public void IpV4SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress) + { + var ipAddressObj = IPNetAddress.Parse(netMask); + Assert.True(ipAddressObj.Contains(IPAddress.Parse(ipAddress))); + } + + [Theory] + [InlineData("192.168.5.85/24", "192.168.4.254")] + [InlineData("192.168.5.85/24", "191.168.5.254")] + [InlineData("10.128.240.50/30", "10.128.240.47")] + [InlineData("10.128.240.50/30", "10.128.240.52")] + [InlineData("10.128.240.50/30", "10.128.239.50")] + [InlineData("10.128.240.50/30", "10.127.240.51")] + public void IpV4SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress) + { + var ipAddressObj = IPNetAddress.Parse(netMask); + Assert.False(ipAddressObj.Contains(IPAddress.Parse(ipAddress))); + } + + [Theory] + [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")] + [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFFF")] + [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/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")] + public void IpV6SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress) + { + var ipAddressObj = IPNetAddress.Parse(netMask); + Assert.True(ipAddressObj.Contains(IPAddress.Parse(ipAddress))); + } + + [Theory] + [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0011:FFFF:FFFF:FFFF:FFFF")] + [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0013:0000: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/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0001")] + public void IpV6SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress) + { + var ipAddressObj = IPNetAddress.Parse(netMask); + Assert.False(ipAddressObj.Contains(IPAddress.Parse(ipAddress))); + } + + [Theory] + [InlineData("10.0.0.0/255.0.0.0", "10.10.10.1/32")] + [InlineData("10.0.0.0/8", "10.10.10.1/32")] + [InlineData("10.0.0.0/255.0.0.0", "10.10.10.1")] + + [InlineData("10.10.0.0/255.255.0.0", "10.10.10.1/32")] + [InlineData("10.10.0.0/16", "10.10.10.1/32")] + [InlineData("10.10.0.0/255.255.0.0", "10.10.10.1")] + + [InlineData("10.10.10.0/255.255.255.0", "10.10.10.1/32")] + [InlineData("10.10.10.0/24", "10.10.10.1/32")] + [InlineData("10.10.10.0/255.255.255.0", "10.10.10.1")] + + public void TestSubnets(string network, string ip) + { + Assert.True(TryParse(network, out IPObject? networkObj)); + Assert.True(TryParse(ip, out IPObject? ipObj)); + +#pragma warning disable CS8602 // Dereference of a possibly null reference. +#pragma warning disable CS8604 // Possible null reference argument. + Assert.True(networkObj.Contains(ipObj)); +#pragma warning restore CS8604 // Possible null reference argument. +#pragma warning restore CS8602 // Dereference of a possibly null reference. + } + + [Theory] + [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "172.168.1.2/24", "172.168.1.2/24")] + [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "172.168.1.2/24, 10.10.10.1", "172.168.1.2/24,10.10.10.1/24")] + [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "192.168.1.2/255.255.255.0, 10.10.10.1", "192.168.1.2/24,10.10.10.1/24")] + [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "192.168.1.2/24, 100.10.10.1", "192.168.1.2/24")] + [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "194.168.1.2/24, 100.10.10.1", "")] + + public void TestMatches(string source, string dest, string result) + { + var conf = new NetworkConfiguration() + { + EnableIPV6 = true, + EnableIPV4 = true + }; + + var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + + // Test included, IP6. + NetCollection ncSource = nm.CreateIPCollection(source.Split(",")); + NetCollection ncDest = nm.CreateIPCollection(dest.Split(",")); + NetCollection ncResult = ncSource.Union(ncDest); + NetCollection resultCollection = nm.CreateIPCollection(result.Split(",")); + Assert.True(ncResult.Equals(resultCollection)); + } + + + [Theory] + [InlineData("10.1.1.1/32", "10.1.1.1")] + [InlineData("192.168.1.254/32", "192.168.1.254/255.255.255.255")] + + public void TestEquals(string source, string dest) + { + Assert.True(IPNetAddress.Parse(source).Equals(IPNetAddress.Parse(dest))); + Assert.True(IPNetAddress.Parse(dest).Equals(IPNetAddress.Parse(source))); + } + + [Theory] + + // Testing bind interfaces. These are set for my system so won't work elsewhere. + // On my system eth16 is internal, eth11 external (Windows defines the indexes). + // + // This test is to replicate how DNLA requests work throughout the system. + + // User on internal network, we're bound internal and external - so result is internal. + [InlineData("192.168.1.1", "eth16,eth11", false, "eth16")] + // User on external network, we're bound internal and external - so result is external. + [InlineData("8.8.8.8", "eth16,eth11", false, "eth11")] + // User on internal network, we're bound internal only - so result is internal. + [InlineData("10.10.10.10", "eth16", false, "eth16")] + // User on internal network, no binding specified - so result is the 1st internal. + [InlineData("192.168.1.1", "", false, "eth16")] + // User on external network, internal binding only - so result is the 1st internal. + [InlineData("jellyfin.org", "eth16", false, "eth16")] + // User on external network, no binding - so result is the 1st external. + [InlineData("jellyfin.org", "", false, "eth11")] + // User assumed to be internal, no binding - so result is the 1st internal. + [InlineData("", "", false, "eth16")] + public void TestBindInterfaces(string source, string bindAddresses, bool ipv6enabled, string result) + { + var conf = new NetworkConfiguration() + { + LocalNetworkAddresses = bindAddresses.Split(','), + EnableIPV6 = ipv6enabled, + EnableIPV4 = true + }; + + NetworkManager.MockNetworkSettings = "192.168.1.208/24,-16,eth16:200.200.200.200/24,11,eth11"; + var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + NetworkManager.MockNetworkSettings = string.Empty; + + _ = nm.TryParseInterface(result, out NetCollection? resultObj); + + if (resultObj != null) + { + result = ((IPNetAddress)resultObj[0]).ToString(true); + var intf = nm.GetBindInterface(source, out int? _); + + Assert.True(string.Equals(intf, result, System.StringComparison.OrdinalIgnoreCase)); + } + } + + [Theory] + + // Testing bind interfaces. These are set for my system so won't work elsewhere. + // On my system eth16 is internal, eth11 external (Windows defines the indexes). + // + // This test is to replicate how subnet bound ServerPublisherUri work throughout the system. + + // User on internal network, we're bound internal and external - so result is internal override. + [InlineData("192.168.1.1", "192.168.1.0/24", "eth16,eth11", false, "192.168.1.0/24=internal.jellyfin", "internal.jellyfin")] + + // User on external network, we're bound internal and external - so result is override. + [InlineData("8.8.8.8", "192.168.1.0/24", "eth16,eth11", false, "0.0.0.0=http://helloworld.com", "http://helloworld.com")] + + // User on internal network, we're bound internal only, but the address isn't in the LAN - so return the override. + [InlineData("10.10.10.10", "192.168.1.0/24", "eth16", false, "0.0.0.0=http://internalButNotDefinedAsLan.com", "http://internalButNotDefinedAsLan.com")] + + // User on internal network, no binding specified - so result is the 1st internal. + [InlineData("192.168.1.1", "192.168.1.0/24", "", false, "0.0.0.0=http://helloworld.com", "eth16")] + + // User on external network, internal binding only - so asumption is a proxy forward, return external override. + [InlineData("jellyfin.org", "192.168.1.0/24", "eth16", false, "0.0.0.0=http://helloworld.com", "http://helloworld.com")] + + // User on external network, no binding - so result is the 1st external which is overriden. + [InlineData("jellyfin.org", "192.168.1.0/24", "", false, "0.0.0.0 = http://helloworld.com", "http://helloworld.com")] + + // User assumed to be internal, no binding - so result is the 1st internal. + [InlineData("", "192.168.1.0/24", "", false, "0.0.0.0=http://helloworld.com", "eth16")] + + // User is internal, no binding - so result is the 1st internal, which is then overridden. + [InlineData("192.168.1.1", "192.168.1.0/24", "", false, "eth16=http://helloworld.com", "http://helloworld.com")] + + public void TestBindInterfaceOverrides(string source, string lan, string bindAddresses, bool ipv6enabled, string publishedServers, string result) + { + var conf = new NetworkConfiguration() + { + LocalNetworkSubnets = lan.Split(','), + LocalNetworkAddresses = bindAddresses.Split(','), + EnableIPV6 = ipv6enabled, + EnableIPV4 = true, + PublishedServerUriBySubnet = new string[] { publishedServers } + }; + + NetworkManager.MockNetworkSettings = "192.168.1.208/24,-16,eth16:200.200.200.200/24,11,eth11"; + var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + NetworkManager.MockNetworkSettings = string.Empty; + + if (nm.TryParseInterface(result, out NetCollection? resultObj) && resultObj != null) + { + // Parse out IPAddresses so we can do a string comparison. (Ignore subnet masks). + result = ((IPNetAddress)resultObj[0]).ToString(true); + } + + var intf = nm.GetBindInterface(source, out int? _); + + Assert.True(string.Equals(intf, result, System.StringComparison.OrdinalIgnoreCase)); + } + } +} -- cgit v1.2.3 From 83629ab6f24ee1a8991dae2b8a55d24c93832ad5 Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 10 Nov 2020 09:52:34 -0700 Subject: Update packages to net5 --- DvdLib/DvdLib.csproj | 2 +- Emby.Dlna/Emby.Dlna.csproj | 2 +- Emby.Drawing/Emby.Drawing.csproj | 2 +- Emby.Naming/Emby.Naming.csproj | 2 +- Emby.Notifications/Emby.Notifications.csproj | 2 +- Emby.Photos/Emby.Photos.csproj | 2 +- .../Emby.Server.Implementations.csproj | 12 ++++++------ Jellyfin.Api/Jellyfin.Api.csproj | 6 +++--- Jellyfin.Data/Jellyfin.Data.csproj | 6 +++--- Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 2 +- .../Jellyfin.Server.Implementations.csproj | 8 ++++---- Jellyfin.Server/Jellyfin.Server.csproj | 10 +++++----- MediaBrowser.Common/MediaBrowser.Common.csproj | 6 +++--- MediaBrowser.Controller/MediaBrowser.Controller.csproj | 6 +++--- MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj | 2 +- MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj | 4 ++-- MediaBrowser.Model/Extensions/StringHelper.cs | 6 ------ MediaBrowser.Model/MediaBrowser.Model.csproj | 8 ++++---- MediaBrowser.Providers/MediaBrowser.Providers.csproj | 8 ++++---- MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj | 2 +- RSSDP/RSSDP.csproj | 2 +- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 6 +++--- tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj | 2 +- .../Jellyfin.Controller.Tests.csproj | 2 +- .../Jellyfin.MediaEncoding.Tests.csproj | 2 +- tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj | 2 +- .../Jellyfin.Server.Implementations.Tests.csproj | 2 +- 27 files changed, 55 insertions(+), 61 deletions(-) (limited to 'RSSDP') diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj index 64d041cb05..7bbd9acf82 100644 --- a/DvdLib/DvdLib.csproj +++ b/DvdLib/DvdLib.csproj @@ -10,7 +10,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 6ed49944c0..bd30cc1e11 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -17,7 +17,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index 092f8580a6..7d479a5c65 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -6,7 +6,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 6857f9952c..80800840ee 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -6,7 +6,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj index 1d430a5e58..16ee918c46 100644 --- a/Emby.Notifications/Emby.Notifications.csproj +++ b/Emby.Notifications/Emby.Notifications.csproj @@ -6,7 +6,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index dbe01257f4..62e33e6c44 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -19,7 +19,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index c762aa0b84..bcddea281c 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -32,13 +32,13 @@ - - - - + + + + - + @@ -49,7 +49,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index da0852cebc..2836f7b0ae 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -6,7 +6,7 @@ - netstandard2.1 + net5.0 true true enable @@ -14,9 +14,9 @@ - + - + diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 5038988f96..9ae129d072 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netstandard2.1 + net5.0 false true true @@ -41,8 +41,8 @@ - - + + diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index c11ac5fb37..466a12e676 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -6,7 +6,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index c52be3b8a9..e663798da0 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net5.0 false true true @@ -24,12 +24,12 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 3558f144cf..03d06fdff3 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -8,7 +8,7 @@ jellyfin Exe - netcoreapp3.1 + net5.0 false true true @@ -38,10 +38,10 @@ - - - - + + + + diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index e716a6610f..b67a549835 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -18,8 +18,8 @@ - - + + @@ -29,7 +29,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 4374317d67..9acc98dcec 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -14,8 +14,8 @@ - - + + @@ -29,7 +29,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index 529e7065cd..3ce9ff4cc4 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -11,7 +11,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 6ead93e09b..7bb2a7d03f 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -6,7 +6,7 @@ - netstandard2.1 + net5.0 false true true @@ -25,7 +25,7 @@ - + diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs index 8ffa3c4ba6..2d9a6c4dbc 100644 --- a/MediaBrowser.Model/Extensions/StringHelper.cs +++ b/MediaBrowser.Model/Extensions/StringHelper.cs @@ -22,11 +22,6 @@ namespace MediaBrowser.Model.Extensions return str; } -#if NETSTANDARD2_0 - char[] a = str.ToCharArray(); - a[0] = char.ToUpperInvariant(a[0]); - return new string(a); -#else return string.Create( str.Length, str, @@ -38,7 +33,6 @@ namespace MediaBrowser.Model.Extensions chars[i] = buf[i]; } }); -#endif } } } diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 253ee7e795..b86187f9be 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -14,7 +14,7 @@ - netstandard2.0;netstandard2.1 + net5.0 false true true @@ -32,11 +32,11 @@ - + - + - + diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 9465fe42c2..fd3f9f4c7e 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -16,9 +16,9 @@ - - - + + + @@ -26,7 +26,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index 45fd9add92..87d1e9464c 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -15,7 +15,7 @@ - netstandard2.1 + net5.0 false true true diff --git a/RSSDP/RSSDP.csproj b/RSSDP/RSSDP.csproj index 664663bd76..d0962e82c8 100644 --- a/RSSDP/RSSDP.csproj +++ b/RSSDP/RSSDP.csproj @@ -10,7 +10,7 @@ - netstandard2.1 + net5.0 false true diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index ce61f5684e..5bf322f071 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -6,7 +6,7 @@ - netcoreapp3.1 + net5.0 false true enable @@ -16,8 +16,8 @@ - - + + diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index 67dc8286a3..e8eca67600 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -6,7 +6,7 @@ - netcoreapp3.1 + net5.0 false true enable diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj index 30e84842a0..6e3fac43d4 100644 --- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -6,7 +6,7 @@ - netcoreapp3.1 + net5.0 false true enable diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index 4fd0d53421..e88de38112 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -6,7 +6,7 @@ - netcoreapp3.1 + net5.0 false true enable diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index 0d240fd65a..567cf34ef3 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -6,7 +6,7 @@ - netcoreapp3.1 + net5.0 false enable true diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index db1f2956ea..b960fda723 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -6,7 +6,7 @@ - netcoreapp3.1 + net5.0 false true enable -- cgit v1.2.3 From ff49a3bb6156a6b50a43d398796a8b4f3b4c2bda Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 14 Nov 2020 16:28:49 +0100 Subject: Missed some stuff --- DvdLib/Ifo/Dvd.cs | 2 +- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- .../Library/Resolvers/Audio/AudioResolver.cs | 2 +- .../ScheduledTasks/Tasks/ChapterImagesTask.cs | 2 +- Jellyfin.Api/Controllers/FilterController.cs | 6 +++--- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 4 ++-- MediaBrowser.Model/Dlna/ContainerProfile.cs | 2 +- MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs | 2 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 2 +- MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs | 2 +- MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs | 2 +- RSSDP/HttpParserBase.cs | 8 ++++---- 12 files changed, 18 insertions(+), 18 deletions(-) (limited to 'RSSDP') diff --git a/DvdLib/Ifo/Dvd.cs b/DvdLib/Ifo/Dvd.cs index 361319625c..b4a11ed5d6 100644 --- a/DvdLib/Ifo/Dvd.cs +++ b/DvdLib/Ifo/Dvd.cs @@ -31,7 +31,7 @@ namespace DvdLib.Ifo continue; } - var nums = ifo.Name.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries); + var nums = ifo.Name.Split('_', StringSplitOptions.RemoveEmptyEntries); if (nums.Length >= 2 && ushort.TryParse(nums[1], out var ifoNumber)) { ReadVTS(ifoNumber, ifo.FullName); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index f16eda1ec2..7074382b68 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2705,7 +2705,7 @@ namespace Emby.Server.Implementations.Library var videos = videoListResolver.Resolve(fileSystemChildren); - var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase)); + var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files[0].Path, StringComparison.OrdinalIgnoreCase)); if (currentVideo != null) { diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 70be524116..2c4497c693 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -201,7 +201,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio continue; } - var firstMedia = resolvedItem.Files.First(); + var firstMedia = resolvedItem.Files[0]; var libraryItem = new T { diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs index 8439f8a99d..171e442581 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs @@ -106,7 +106,7 @@ namespace Emby.Server.Implementations.ScheduledTasks try { previouslyFailedImages = File.ReadAllText(failHistoryPath) - .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) + .Split('|', StringSplitOptions.RemoveEmptyEntries) .ToList(); } catch (IOException) diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs index 2a567c8461..008bb58d16 100644 --- a/Jellyfin.Api/Controllers/FilterController.cs +++ b/Jellyfin.Api/Controllers/FilterController.cs @@ -78,8 +78,8 @@ namespace Jellyfin.Api.Controllers var query = new InternalItemsQuery { User = user, - MediaTypes = (mediaTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries), - IncludeItemTypes = (includeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries), + MediaTypes = (mediaTypes ?? string.Empty).Split(',', StringSplitOptions.RemoveEmptyEntries), + IncludeItemTypes = (includeItemTypes ?? string.Empty).Split(',', StringSplitOptions.RemoveEmptyEntries), Recursive = true, EnableTotalRecordCount = false, DtoOptions = new DtoOptions @@ -168,7 +168,7 @@ namespace Jellyfin.Api.Controllers var genreQuery = new InternalItemsQuery(user) { IncludeItemTypes = - (includeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries), + (includeItemTypes ?? string.Empty).Split(',', StringSplitOptions.RemoveEmptyEntries), DtoOptions = new DtoOptions { Fields = Array.Empty(), diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index cdeefbbbdf..15a70e2e7d 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -149,7 +149,7 @@ namespace MediaBrowser.MediaEncoding.Probing var iTunEXTC = FFProbeHelpers.GetDictionaryValue(tags, "iTunEXTC"); if (!string.IsNullOrWhiteSpace(iTunEXTC)) { - var parts = iTunEXTC.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + var parts = iTunEXTC.Split('|', StringSplitOptions.RemoveEmptyEntries); // Example // mpaa|G|100|For crude humor if (parts.Length > 1) @@ -1139,7 +1139,7 @@ namespace MediaBrowser.MediaEncoding.Probing return null; } - return value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries) + return value.Split('/', StringSplitOptions.RemoveEmptyEntries) .Select(i => i.Trim()) .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)); } diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs index f77d9b2675..09afa64bbd 100644 --- a/MediaBrowser.Model/Dlna/ContainerProfile.cs +++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs @@ -34,7 +34,7 @@ namespace MediaBrowser.Model.Dlna return Array.Empty(); } - return value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + return value.Split(',', StringSplitOptions.RemoveEmptyEntries); } public bool ContainsContainer(string container) diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index 8b73ecbd4d..50e3374f77 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -186,7 +186,7 @@ namespace MediaBrowser.Model.Dlna if (mediaProfile != null && !string.IsNullOrEmpty(mediaProfile.OrgPn)) { - orgPnValues.AddRange(mediaProfile.OrgPn.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)); + orgPnValues.AddRange(mediaProfile.OrgPn.Split(',', StringSplitOptions.RemoveEmptyEntries)); } else { diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 4959a9b922..13234c3813 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -1647,7 +1647,7 @@ namespace MediaBrowser.Model.Dlna // strip spaces to avoid having to encode var values = value - .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + .Split('|', StringSplitOptions.RemoveEmptyEntries); if (condition.Condition == ProfileConditionType.Equals || condition.Condition == ProfileConditionType.EqualsAny) { diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 32dab60a6b..9eed6172dd 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -391,7 +391,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb item.Genres = Array.Empty(); foreach (var genre in result.Genre - .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(i => i.Trim()) .Where(i => !string.IsNullOrWhiteSpace(i))) { diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs index b34e52235a..e5a3e9a6a3 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs @@ -170,7 +170,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb _logger.LogError(e, "Failed to retrieve series with remote id {RemoteId}", id); } - return result?.Data.First().Id.ToString(); + return result?.Data[0].Id.ToString(CultureInfo.InvariantCulture); } /// diff --git a/RSSDP/HttpParserBase.cs b/RSSDP/HttpParserBase.cs index a40612bc29..11202940e1 100644 --- a/RSSDP/HttpParserBase.cs +++ b/RSSDP/HttpParserBase.cs @@ -119,7 +119,7 @@ namespace Rssdp.Infrastructure } else { - headersToAddTo.TryAddWithoutValidation(headerName, values.First()); + headersToAddTo.TryAddWithoutValidation(headerName, values[0]); } } @@ -151,7 +151,7 @@ namespace Rssdp.Infrastructure return lineIndex; } - private IList ParseValues(string headerValue) + private List ParseValues(string headerValue) { // This really should be better and match the HTTP 1.1 spec, // but this should actually be good enough for SSDP implementations @@ -160,7 +160,7 @@ namespace Rssdp.Infrastructure if (headerValue == "\"\"") { - values.Add(String.Empty); + values.Add(string.Empty); return values; } @@ -172,7 +172,7 @@ namespace Rssdp.Infrastructure else { var segments = headerValue.Split(SeparatorCharacters); - if (headerValue.Contains("\"")) + if (headerValue.Contains('"')) { for (int segmentIndex = 0; segmentIndex < segments.Length; segmentIndex++) { -- cgit v1.2.3 From 9a323f6df08927b5db06ba634f430523a236265f Mon Sep 17 00:00:00 2001 From: Greenback Date: Wed, 18 Nov 2020 13:46:14 +0000 Subject: More spelling corrections. --- Jellyfin.Data/Entities/Libraries/CollectionItem.cs | 4 ++-- Jellyfin.Data/Entities/Libraries/ItemMetadata.cs | 2 +- .../Users/DefaultAuthenticationProvider.cs | 2 +- MediaBrowser.Common/Net/INetworkManager.cs | 4 ++-- MediaBrowser.Controller/Entities/BaseItem.cs | 2 +- MediaBrowser.Controller/Entities/Folder.cs | 2 +- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 6 +++--- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 +- MediaBrowser.Model/Dlna/ResolutionNormalizer.cs | 2 +- MediaBrowser.Providers/Manager/ProviderUtils.cs | 4 ++-- MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs | 10 +++++----- MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs | 2 +- MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs | 10 +++++----- RSSDP/DisposableManagedObjectBase.cs | 6 +++--- RSSDP/HttpParserBase.cs | 2 +- RSSDP/ISsdpDeviceLocator.cs | 4 ++-- RSSDP/SsdpDevicePublisher.cs | 4 ++-- RSSDP/SsdpRootDevice.cs | 4 ++-- 18 files changed, 36 insertions(+), 36 deletions(-) (limited to 'RSSDP') diff --git a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs index 4467c9bbdc..f9539964d0 100644 --- a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs +++ b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs @@ -73,7 +73,7 @@ namespace Jellyfin.Data.Entities.Libraries /// Gets or sets the next item in the collection. /// /// - /// TODO check if this properly updated dependant and has the proper principal relationship. + /// TODO check if this properly updated Dependant and has the proper principal relationship. /// public virtual CollectionItem Next { get; set; } @@ -81,7 +81,7 @@ namespace Jellyfin.Data.Entities.Libraries /// Gets or sets the previous item in the collection. /// /// - /// TODO check if this properly updated dependant and has the proper principal relationship. + /// TODO check if this properly updated Dependant and has the proper principal relationship. /// public virtual CollectionItem Previous { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs index 1d2dc0f669..d74330c051 100644 --- a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs @@ -141,7 +141,7 @@ namespace Jellyfin.Data.Entities.Libraries public virtual ICollection PersonRoles { get; protected set; } /// - /// Gets or sets a collection containing the generes for this item. + /// Gets or sets a collection containing the genres for this item. /// public virtual ICollection Genres { get; protected set; } diff --git a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs index f79e433a67..662b4bf651 100644 --- a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs +++ b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs @@ -53,7 +53,7 @@ namespace Jellyfin.Server.Implementations.Users bool success = false; - // As long as jellyfin supports passwordless users, we need this little block here to accommodate + // As long as jellyfin supports password-less users, we need this little block here to accommodate if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password)) { return Task.FromResult(new ProviderAuthenticationResult diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index a0330afeff..12966a474f 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -58,7 +58,7 @@ namespace MediaBrowser.Common.Net /// /// Investigates an caches a list of interface addresses, excluding local link and LAN excluded addresses. /// - /// The list of ipaddresses. + /// The list of ip addresses. IPAddress[] GetLocalIpAddresses(); /// @@ -73,7 +73,7 @@ namespace MediaBrowser.Common.Net /// Returns true if address is in the LAN list in the config file. /// /// The address to check. - /// If true, check against addresses in the LAN settings which have [] arroud and return true if it matches the address give in address. + /// If true, check against addresses in the LAN settings which have [] around and return true if it matches the address give in address. /// If true, returns false if address is in the 127.x.x.x or 169.128.x.x range. /// falseif the address isn't in the LAN list, true if the address has been defined as a LAN address. bool IsAddressInSubnets(IPAddress address, bool excludeInterfaces, bool excludeRFC); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 1d44a55114..8d77730041 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2611,7 +2611,7 @@ namespace MediaBrowser.Controller.Entities { if (!AllowsMultipleImages(type)) { - throw new ArgumentException("The change index operation is only applicable to backdrops and screenshots"); + throw new ArgumentException("The change index operation is only applicable to backdrops and screen shots"); } var info1 = GetImageInfo(type, index1); diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index a76c8a376b..1e3825c6e3 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -212,7 +212,7 @@ namespace MediaBrowser.Controller.Entities /// /// Loads our children. Validation will occur externally. - /// We want this sychronous. + /// We want this synchronous. /// protected virtual List LoadChildren() { diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5846a603a2..a4a5cecdc5 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -63,7 +63,7 @@ namespace MediaBrowser.Controller.MediaEncoding { // Only use alternative encoders for video files. // When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully - // Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this. + // Since transcoding of folder rips is experimental anyway, it's not worth adding additional variables such as this. if (state.VideoType == VideoType.VideoFile) { var hwType = encodingOptions.HardwareAccelerationType; @@ -247,7 +247,7 @@ namespace MediaBrowser.Controller.MediaEncoding return null; } - // Seeing reported failures here, not sure yet if this is related to specfying input format + // Seeing reported failures here, not sure yet if this is related to specifying input format if (string.Equals(container, "m4v", StringComparison.OrdinalIgnoreCase)) { return null; @@ -2752,7 +2752,7 @@ namespace MediaBrowser.Controller.MediaEncoding var videoType = state.MediaSource.VideoType ?? VideoType.VideoFile; // Only use alternative encoders for video files. // When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully - // Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this. + // Since transcoding of folder rips is experimental anyway, it's not worth adding additional variables such as this. if (videoType != VideoType.VideoFile) { return null; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 5a3a9185d7..e060253ee7 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -542,7 +542,7 @@ namespace MediaBrowser.MediaEncoding.Encoder break; case Video3DFormat.FullTopAndBottom: vf = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2"; - // ftab crop heigt in half, set the display aspect,crop out any black bars we may have made the scale width to 600 + // ftab crop height in half, set the display aspect,crop out any black bars we may have made the scale width to 600 break; default: break; diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs index a4305c8104..2f8614386a 100644 --- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs +++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.Model.Dlna int? maxWidth, int? maxHeight) { - // If the bitrate isn't changing, then don't downlscale the resolution + // If the bitrate isn't changing, then don't downscale the resolution if (inputBitrate.HasValue && outputBitrate >= inputBitrate.Value) { if (maxWidth.HasValue || maxHeight.HasValue) diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs index 70a5a6ac13..5621d2b86c 100644 --- a/MediaBrowser.Providers/Manager/ProviderUtils.cs +++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.Providers.Manager { if (replaceData || string.IsNullOrEmpty(target.Name)) { - // Safeguard against incoming data having an emtpy name + // Safeguard against incoming data having an empty name if (!string.IsNullOrWhiteSpace(source.Name)) { target.Name = source.Name; @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Manager if (replaceData || string.IsNullOrEmpty(target.OriginalTitle)) { - // Safeguard against incoming data having an emtpy name + // Safeguard against incoming data having an empty name if (!string.IsNullOrWhiteSpace(source.OriginalTitle)) { target.OriginalTitle = source.OriginalTitle; diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 6af52b591c..332479ff8a 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var result = await GetRootObject(imdbId, cancellationToken).ConfigureAwait(false); - // Only take the name and rating if the user's language is set to english, since Omdb has no localization + // Only take the name and rating if the user's language is set to English, since Omdb has no localization if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase) || _configurationManager.Configuration.EnableNewOmdbSupport) { item.Name = result.Title; @@ -151,7 +151,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb return false; } - // Only take the name and rating if the user's language is set to english, since Omdb has no localization + // Only take the name and rating if the user's language is set to English, since Omdb has no localization if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase) || _configurationManager.Configuration.EnableNewOmdbSupport) { item.Name = result.Title; @@ -385,7 +385,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var isConfiguredForEnglish = IsConfiguredForEnglish(item) || _configurationManager.Configuration.EnableNewOmdbSupport; // Grab series genres because IMDb data is better than TVDB. Leave movies alone - // But only do it if english is the preferred language because this data will not be localized + // But only do it if English is the preferred language because this data will not be localized if (isConfiguredForEnglish && !string.IsNullOrWhiteSpace(result.Genre)) { item.Genres = Array.Empty(); @@ -401,7 +401,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb if (isConfiguredForEnglish) { - // Omdb is currently english only, so for other languages skip this and let secondary providers fill it in + // Omdb is currently English only, so for other languages skip this and let secondary providers fill it in item.Overview = result.Plot; } @@ -455,7 +455,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb { var lang = item.GetPreferredMetadataLanguage(); - // The data isn't localized and so can only be used for english users + // The data isn't localized and so can only be used for English users return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs index b754a07953..0e8a5baab6 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs @@ -98,7 +98,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb if (preferredLanguage.Length == 5) // like en-US { - // Currenty, TMDB supports 2-letter language codes only + // Currently, TMDB supports 2-letter language codes only // They are planning to change this in the future, thus we're // supplying both codes if we're having a 5-letter code. languages.Add(preferredLanguage.Substring(0, 2)); diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs index 9cc0344c1c..bce4cf0093 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -134,7 +134,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - // int.TryParse is local aware, so it can be probamatic, force us culture + // int.TryParse is local aware, so it can be problematic, force us culture if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var rval)) { item.AirsBeforeEpisodeNumber = rval; @@ -150,7 +150,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - // int.TryParse is local aware, so it can be probamatic, force us culture + // int.TryParse is local aware, so it can be problematic, force us culture if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var rval)) { item.AirsAfterSeasonNumber = rval; @@ -166,7 +166,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - // int.TryParse is local aware, so it can be probamatic, force us culture + // int.TryParse is local aware, so it can be problematic, force us culture if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var rval)) { item.AirsBeforeSeasonNumber = rval; @@ -182,7 +182,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - // int.TryParse is local aware, so it can be probamatic, force us culture + // int.TryParse is local aware, so it can be problematic, force us culture if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var rval)) { item.AirsBeforeSeasonNumber = rval; @@ -198,7 +198,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - // int.TryParse is local aware, so it can be probamatic, force us culture + // int.TryParse is local aware, so it can be problematic, force us culture if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var rval)) { item.AirsBeforeEpisodeNumber = rval; diff --git a/RSSDP/DisposableManagedObjectBase.cs b/RSSDP/DisposableManagedObjectBase.cs index 745ec359c9..7d6a471f95 100644 --- a/RSSDP/DisposableManagedObjectBase.cs +++ b/RSSDP/DisposableManagedObjectBase.cs @@ -5,7 +5,7 @@ using System.Text; namespace Rssdp.Infrastructure { /// - /// Correclty implements the interface and pattern for an object containing only managed resources, and adds a few common niceities not on the interface such as an property. + /// Correctly implements the interface and pattern for an object containing only managed resources, and adds a few common niceties not on the interface such as an property. /// public abstract class DisposableManagedObjectBase : IDisposable { @@ -61,10 +61,10 @@ namespace Rssdp.Infrastructure /// Disposes this object instance and all internally managed resources. /// /// - /// Sets the property to true. Does not explicitly throw an exception if called multiple times, but makes no promises about behaviour of derived classes. + /// Sets the property to true. Does not explicitly throw an exception if called multiple times, but makes no promises about behavior of derived classes. /// /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "We do exactly as asked, but CA doesn't seem to like us also setting the IsDisposed property. Too bad, it's a good idea and shouldn't cause an exception or anything likely to interfer with the dispose process.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "We do exactly as asked, but CA doesn't seem to like us also setting the IsDisposed property. Too bad, it's a good idea and shouldn't cause an exception or anything likely to interfere with the dispose process.")] public void Dispose() { IsDisposed = true; diff --git a/RSSDP/HttpParserBase.cs b/RSSDP/HttpParserBase.cs index 11202940e1..c56249523d 100644 --- a/RSSDP/HttpParserBase.cs +++ b/RSSDP/HttpParserBase.cs @@ -105,7 +105,7 @@ namespace Rssdp.Infrastructure var headerName = line.Substring(0, headerKeySeparatorIndex).Trim(); var headerValue = line.Substring(headerKeySeparatorIndex + 1).Trim(); - // Not sure how to determine where request headers and and content headers begin, + // Not sure how to determine where request headers and content headers begin, // at least not without a known set of headers (general headers first the content headers) // which seems like a bad way of doing it. So we'll assume if it's a known content header put it there // else use request headers. diff --git a/RSSDP/ISsdpDeviceLocator.cs b/RSSDP/ISsdpDeviceLocator.cs index 4130556434..4df166cd26 100644 --- a/RSSDP/ISsdpDeviceLocator.cs +++ b/RSSDP/ISsdpDeviceLocator.cs @@ -52,7 +52,7 @@ namespace Rssdp.Infrastructure } /// - /// Aynchronously performs a search for all devices using the default search timeout, and returns an awaitable task that can be used to retrieve the results. + /// Asynchronously performs a search for all devices using the default search timeout, and returns an awaitable task that can be used to retrieve the results. /// /// A task whose result is an of instances, representing all found devices. System.Threading.Tasks.Task> SearchAsync(); @@ -83,7 +83,7 @@ namespace Rssdp.Infrastructure /// /// The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 is recommended by the UPnP 1.1 specification. Specify TimeSpan.Zero to return only devices already in the cache. /// - /// By design RSSDP does not support 'publishing services' as it is intended for use with non-standard UPnP devices that don't publish UPnP style services. However, it is still possible to use RSSDP to search for devices implemetning these services if you know the service type. + /// By design RSSDP does not support 'publishing services' as it is intended for use with non-standard UPnP devices that don't publish UPnP style services. However, it is still possible to use RSSDP to search for devices implementing these services if you know the service type. /// /// A task whose result is an of instances, representing all found devices. System.Threading.Tasks.Task> SearchAsync(string searchTarget, TimeSpan searchWaitTime); diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index 1a8577d8da..90925b9e0a 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -102,7 +102,7 @@ namespace Rssdp.Infrastructure /// The instance to add. /// Thrown if the argument is null. /// Thrown if the contains property values that are not acceptable to the UPnP 1.0 specification. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "t", Justification = "Capture task to local variable supresses compiler warning, but task is not really needed.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "t", Justification = "Capture task to local variable suppresses compiler warning, but task is not really needed.")] public void AddDevice(SsdpRootDevice device) { if (device == null) @@ -180,7 +180,7 @@ namespace Rssdp.Infrastructure /// /// /// Enabling this option will cause devices to show up in Microsoft Windows Explorer's network screens (if discovery is enabled etc.). Windows Explorer appears to search only for pnp:rootdeivce and not upnp:rootdevice. - /// If false, the system will only use upnp:rootdevice for notifiation broadcasts and and search responses, which is correct according to the UPnP/SSDP spec. + /// If false, the system will only use upnp:rootdevice for notification broadcasts and and search responses, which is correct according to the UPnP/SSDP spec. /// public bool SupportPnpRootDevice { diff --git a/RSSDP/SsdpRootDevice.cs b/RSSDP/SsdpRootDevice.cs index 8937ec331f..4084b31ca6 100644 --- a/RSSDP/SsdpRootDevice.cs +++ b/RSSDP/SsdpRootDevice.cs @@ -25,7 +25,7 @@ namespace Rssdp /// Specifies how long clients can cache this device's details for. Optional but defaults to which means no-caching. Recommended value is half an hour. /// /// - /// Specifiy to indicate no caching allowed. + /// Specify to indicate no caching allowed. /// Also used to specify how often to rebroadcast alive notifications. /// The UPnP/SSDP specifications indicate this should not be less than 1800 seconds (half an hour), but this is not enforced by this library. /// @@ -50,7 +50,7 @@ namespace Rssdp public IPAddress SubnetMask { get; set; } /// - /// The base URL to use for all relative url's provided in other propertise (and those of child devices). Optional. + /// The base URL to use for all relative url's provided in other properties (and those of child devices). Optional. /// /// /// Defines the base URL. Used to construct fully-qualified URLs. All relative URLs that appear elsewhere in the description are combined with this base URL. If URLBase is empty or not given, the base URL is the URL from which the device description was retrieved (which is the preferred implementation; use of URLBase is no longer recommended). Specified by UPnP vendor. Single URL. -- cgit v1.2.3