aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs30
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs280
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj1
-rw-r--r--Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs11
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs3
-rw-r--r--Emby.Server.Implementations/Networking/NetworkManager.cs556
-rw-r--r--Emby.Server.Implementations/Udp/UdpServer.cs2
8 files changed, 139 insertions, 747 deletions
diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
index 4ab0a2a3f..8503a358e 100644
--- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
+++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
@@ -10,6 +10,7 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
+using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.AppBase
@@ -134,6 +135,35 @@ namespace Emby.Server.Implementations.AppBase
}
/// <summary>
+ /// Manually pre-loads a factory so that it is available pre system initialisation.
+ /// </summary>
+ /// <typeparam name="T">Class to register.</typeparam>
+ public virtual void RegisterConfiguration<T>()
+ {
+ if (!typeof(IConfigurationFactory).IsAssignableFrom(typeof(T)))
+ {
+ throw new ArgumentException("Parameter does not implement IConfigurationFactory");
+ }
+
+ IConfigurationFactory factory = (IConfigurationFactory)Activator.CreateInstance(typeof(T));
+
+ if (_configurationFactories == null)
+ {
+ _configurationFactories = new IConfigurationFactory[] { factory };
+ }
+ else
+ {
+ var list = _configurationFactories.ToList<IConfigurationFactory>();
+ list.Add(factory);
+ _configurationFactories = list.ToArray();
+ }
+
+ _configurationStores = _configurationFactories
+ .SelectMany(i => i.GetConfigurations())
+ .ToArray();
+ }
+
+ /// <summary>
/// Adds parts.
/// </summary>
/// <param name="factories">The configuration factories.</param>
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 31ca73829..99d91881c 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -16,6 +16,7 @@ using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using System.Xml.Serialization;
using Emby.Dlna;
using Emby.Dlna.Main;
using Emby.Dlna.Ssdp;
@@ -48,6 +49,9 @@ using Emby.Server.Implementations.SyncPlay;
using Emby.Server.Implementations.TV;
using Emby.Server.Implementations.Updates;
using Jellyfin.Api.Helpers;
+using Jellyfin.Api.Migrations;
+using Jellyfin.Networking.Configuration;
+using Jellyfin.Networking.Manager;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
@@ -100,6 +104,8 @@ using MediaBrowser.Providers.Plugins.TheTvdb;
using MediaBrowser.Providers.Plugins.Tmdb;
using MediaBrowser.Providers.Subtitles;
using MediaBrowser.XbmcMetadata.Providers;
+using Microsoft.AspNetCore.DataProtection.Repositories;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -120,7 +126,6 @@ namespace Emby.Server.Implementations
private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" };
private readonly IFileSystem _fileSystemManager;
- private readonly INetworkManager _networkManager;
private readonly IXmlSerializer _xmlSerializer;
private readonly IJsonSerializer _jsonSerializer;
private readonly IStartupOptions _startupOptions;
@@ -163,6 +168,11 @@ namespace Emby.Server.Implementations
}
/// <summary>
+ /// Gets the <see cref="INetworkManager"/> singleton instance.
+ /// </summary>
+ public INetworkManager NetManager { get; internal set; }
+
+ /// <summary>
/// Occurs when [has pending restart changed].
/// </summary>
public event EventHandler HasPendingRestartChanged;
@@ -214,7 +224,7 @@ namespace Emby.Server.Implementations
private readonly List<IDisposable> _disposableParts = new List<IDisposable>();
/// <summary>
- /// Gets the configuration manager.
+ /// Gets or sets the configuration manager.
/// </summary>
/// <value>The configuration manager.</value>
protected IConfigurationManager ConfigurationManager { get; set; }
@@ -247,14 +257,12 @@ namespace Emby.Server.Implementations
/// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
/// <param name="options">Instance of the <see cref="IStartupOptions"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
- /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
/// <param name="serviceCollection">Instance of the <see cref="IServiceCollection"/> interface.</param>
public ApplicationHost(
IServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory,
IStartupOptions options,
IFileSystem fileSystem,
- INetworkManager networkManager,
IServiceCollection serviceCollection)
{
_xmlSerializer = new MyXmlSerializer();
@@ -262,14 +270,16 @@ namespace Emby.Server.Implementations
ServiceCollection = serviceCollection;
- _networkManager = networkManager;
- networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
-
ApplicationPaths = applicationPaths;
LoggerFactory = loggerFactory;
_fileSystemManager = fileSystem;
ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager);
+ MigrateNetworkConfiguration();
+
+ // Have to pre-register the NetworkConfigurationFactory.
+ ConfigurationManager.RegisterConfiguration<NetworkConfigurationFactory>();
+ NetManager = new NetworkManager((IServerConfigurationManager)ConfigurationManager, LoggerFactory.CreateLogger<NetworkManager>());
Logger = LoggerFactory.CreateLogger<ApplicationHost>();
@@ -283,8 +293,6 @@ namespace Emby.Server.Implementations
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
- _networkManager.NetworkChanged += OnNetworkChanged;
-
CertificateInfo = new CertificateInfo
{
Path = ServerConfigurationManager.Configuration.CertificatePath,
@@ -297,6 +305,18 @@ namespace Emby.Server.Implementations
ApplicationUserAgent = Name.Replace(' ', '-') + "/" + ApplicationVersionString;
}
+ private void MigrateNetworkConfiguration()
+ {
+ string path = Path.Combine(ConfigurationManager.CommonApplicationPaths.ConfigurationDirectoryPath, "network.xml");
+ if (!File.Exists(path))
+ {
+ var networkSettings = new NetworkConfiguration();
+ ClassMigrationHelper.CopyProperties(ServerConfigurationManager.Configuration, networkSettings);
+ _xmlSerializer.SerializeToFile(networkSettings, path);
+ Logger?.LogDebug("Successfully migrated network settings.");
+ }
+ }
+
public string ExpandVirtualPath(string path)
{
var appPaths = ApplicationPaths;
@@ -313,16 +333,6 @@ namespace Emby.Server.Implementations
.Replace(appPaths.InternalMetadataPath, appPaths.VirtualInternalMetadataPath, StringComparison.OrdinalIgnoreCase);
}
- private string[] GetConfiguredLocalSubnets()
- {
- return ServerConfigurationManager.Configuration.LocalNetworkSubnets;
- }
-
- private void OnNetworkChanged(object sender, EventArgs e)
- {
- _validAddressResults.Clear();
- }
-
/// <inheritdoc />
public Version ApplicationVersion { get; }
@@ -403,7 +413,7 @@ namespace Emby.Server.Implementations
/// <summary>
/// Resolves this instance.
/// </summary>
- /// <typeparam name="T">The type</typeparam>
+ /// <typeparam name="T">The type.</typeparam>
/// <returns>``0.</returns>
public T Resolve<T>() => ServiceProvider.GetService<T>();
@@ -489,14 +499,15 @@ namespace Emby.Server.Implementations
/// <inheritdoc/>
public void Init()
{
- HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
- HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
+ var networkConfiguration = ServerConfigurationManager.GetNetworkConfiguration();
+ HttpPort = networkConfiguration.HttpServerPortNumber;
+ HttpsPort = networkConfiguration.HttpsPortNumber;
// Safeguard against invalid configuration
if (HttpPort == HttpsPort)
{
- HttpPort = ServerConfiguration.DefaultHttpPort;
- HttpsPort = ServerConfiguration.DefaultHttpsPort;
+ HttpPort = NetworkConfiguration.DefaultHttpPort;
+ HttpsPort = NetworkConfiguration.DefaultHttpsPort;
}
if (Plugins != null)
@@ -539,7 +550,7 @@ namespace Emby.Server.Implementations
ServiceCollection.AddSingleton<TvdbClientManager>();
ServiceCollection.AddSingleton<TmdbClientManager>();
- ServiceCollection.AddSingleton(_networkManager);
+ ServiceCollection.AddSingleton(NetManager);
ServiceCollection.AddSingleton<IIsoManager, IsoManager>();
@@ -905,9 +916,10 @@ namespace Emby.Server.Implementations
// Don't do anything if these haven't been set yet
if (HttpPort != 0 && HttpsPort != 0)
{
+ var networkConfiguration = ServerConfigurationManager.GetNetworkConfiguration();
// Need to restart if ports have changed
- if (ServerConfigurationManager.Configuration.HttpServerPortNumber != HttpPort ||
- ServerConfigurationManager.Configuration.HttpsPortNumber != HttpsPort)
+ if (networkConfiguration.HttpServerPortNumber != HttpPort ||
+ networkConfiguration.HttpsPortNumber != HttpsPort)
{
if (ServerConfigurationManager.Configuration.IsPortAuthorized)
{
@@ -1165,6 +1177,9 @@ namespace Emby.Server.Implementations
// Xbmc
yield return typeof(ArtistNfoProvider).Assembly;
+ // Network
+ yield return typeof(NetworkManager).Assembly;
+
foreach (var i in GetAssembliesWithPartsInternal())
{
yield return i;
@@ -1176,13 +1191,10 @@ namespace Emby.Server.Implementations
/// <summary>
/// Gets the system status.
/// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
+ /// <param name="source">Where this request originated.</param>
/// <returns>SystemInfo.</returns>
- public async Task<SystemInfo> GetSystemInfo(CancellationToken cancellationToken)
+ public SystemInfo GetSystemInfo(IPAddress source)
{
- var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
- var transcodingTempPath = ConfigurationManager.GetTranscodePath();
-
return new SystemInfo
{
HasPendingRestart = HasPendingRestart,
@@ -1202,9 +1214,9 @@ namespace Emby.Server.Implementations
CanSelfRestart = CanSelfRestart,
CanLaunchWebBrowser = CanLaunchWebBrowser,
HasUpdateAvailable = HasUpdateAvailable,
- TranscodingTempPath = transcodingTempPath,
+ TranscodingTempPath = ConfigurationManager.GetTranscodePath(),
ServerName = FriendlyName,
- LocalAddress = localAddress,
+ LocalAddress = GetSmartApiUrl(source),
SupportsLibraryMonitor = true,
EncoderLocation = _mediaEncoder.EncoderLocation,
SystemArchitecture = RuntimeInformation.OSArchitecture,
@@ -1213,14 +1225,12 @@ namespace Emby.Server.Implementations
}
public IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo()
- => _networkManager.GetMacAddresses()
+ => NetManager.GetMacAddresses()
.Select(i => new WakeOnLanInfo(i))
.ToList();
- public async Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken)
+ public PublicSystemInfo GetPublicSystemInfo(IPAddress source)
{
- var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
-
return new PublicSystemInfo
{
Version = ApplicationVersionString,
@@ -1228,193 +1238,98 @@ namespace Emby.Server.Implementations
Id = SystemId,
OperatingSystem = OperatingSystem.Id.ToString(),
ServerName = FriendlyName,
- LocalAddress = localAddress,
+ LocalAddress = GetSmartApiUrl(source),
StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted
};
}
/// <inheritdoc/>
- public bool ListenWithHttps => Certificate != null && ServerConfigurationManager.Configuration.EnableHttps;
+ public bool ListenWithHttps => Certificate != null && ServerConfigurationManager.GetNetworkConfiguration().EnableHttps;
/// <inheritdoc/>
- public async Task<string> GetLocalApiUrl(CancellationToken cancellationToken)
+ public string GetSmartApiUrl(IPAddress ipAddress, int? port = null)
{
- try
+ // Published server ends with a /
+ if (_startupOptions.PublishedServerUrl != null)
{
- // Return the first matched address, if found, or the first known local address
- var addresses = await GetLocalIpAddressesInternal(false, 1, cancellationToken).ConfigureAwait(false);
- if (addresses.Count == 0)
- {
- return null;
- }
-
- return GetLocalApiUrl(addresses[0]);
+ // Published server ends with a '/', so we need to remove it.
+ return _startupOptions.PublishedServerUrl.ToString().Trim('/');
}
- catch (Exception ex)
+
+ 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))
{
- Logger.LogError(ex, "Error getting local Ip address information");
+ return smart.Trim('/');
}
- return null;
+ return GetLocalApiUrl(smart.Trim('/'), null, port);
}
- /// <summary>
- /// Removes the scope id from IPv6 addresses.
- /// </summary>
- /// <param name="address">The IPv6 address.</param>
- /// <returns>The IPv6 address without the scope id.</returns>
- private ReadOnlySpan<char> RemoveScopeId(ReadOnlySpan<char> address)
+ /// <inheritdoc/>
+ public string GetSmartApiUrl(HttpRequest request, int? port = null)
{
- var index = address.IndexOf('%');
- if (index == -1)
+ // Published server ends with a /
+ if (_startupOptions.PublishedServerUrl != null)
{
- return address;
+ // Published server ends with a '/', so we need to remove it.
+ return _startupOptions.PublishedServerUrl.ToString().Trim('/');
}
- return address.Slice(0, index);
- }
-
- /// <inheritdoc />
- public string GetLocalApiUrl(IPAddress ipAddress)
- {
- if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
+ 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))
{
- var str = RemoveScopeId(ipAddress.ToString());
- Span<char> span = new char[str.Length + 2];
- span[0] = '[';
- str.CopyTo(span.Slice(1));
- span[^1] = ']';
-
- return GetLocalApiUrl(span);
+ return smart.Trim('/');
}
- return GetLocalApiUrl(ipAddress.ToString());
- }
-
- /// <inheritdoc/>
- public string GetLoopbackHttpApiUrl()
- {
- return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort);
+ return GetLocalApiUrl(smart.Trim('/'), request.Scheme, port);
}
/// <inheritdoc/>
- public string GetLocalApiUrl(ReadOnlySpan<char> host, string scheme = null, int? port = null)
+ public string GetSmartApiUrl(string hostname, int? port = null)
{
- // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
- // not. For consistency, always trim the trailing slash.
- return new UriBuilder
+ // Published server ends with a /
+ if (_startupOptions.PublishedServerUrl != null)
{
- Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp),
- Host = host.ToString(),
- Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort),
- Path = ServerConfigurationManager.Configuration.BaseUrl
- }.ToString().TrimEnd('/');
- }
-
- public Task<List<IPAddress>> GetLocalIpAddresses(CancellationToken cancellationToken)
- {
- return GetLocalIpAddressesInternal(true, 0, cancellationToken);
- }
-
- private async Task<List<IPAddress>> GetLocalIpAddressesInternal(bool allowLoopback, int limit, CancellationToken cancellationToken)
- {
- var addresses = ServerConfigurationManager
- .Configuration
- .LocalNetworkAddresses
- .Select(x => NormalizeConfiguredLocalAddress(x))
- .Where(i => i != null)
- .ToList();
-
- if (addresses.Count == 0)
- {
- addresses.AddRange(_networkManager.GetLocalIpAddresses());
+ // Published server ends with a '/', so we need to remove it.
+ return _startupOptions.PublishedServerUrl.ToString().Trim('/');
}
- var resultList = new List<IPAddress>();
+ string smart = NetManager.GetBindInterface(hostname, out port);
- foreach (var address in addresses)
+ // If the smartAPI doesn't start with http then treat it as a host or ip.
+ if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
- if (!allowLoopback)
- {
- if (address.Equals(IPAddress.Loopback) || address.Equals(IPAddress.IPv6Loopback))
- {
- continue;
- }
- }
-
- if (await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false))
- {
- resultList.Add(address);
-
- if (limit > 0 && resultList.Count >= limit)
- {
- return resultList;
- }
- }
+ return smart.Trim('/');
}
- return resultList;
+ return GetLocalApiUrl(smart.Trim('/'), null, port);
}
- public IPAddress NormalizeConfiguredLocalAddress(ReadOnlySpan<char> address)
+ /// <inheritdoc/>
+ public string GetLoopbackHttpApiUrl()
{
- var index = address.Trim('/').IndexOf('/');
- if (index != -1)
+ if (NetManager.IsIP6Enabled)
{
- address = address.Slice(index + 1);
+ return GetLocalApiUrl("::1", Uri.UriSchemeHttp, HttpPort);
}
- if (IPAddress.TryParse(address.Trim('/'), out IPAddress result))
- {
- return result;
- }
-
- return null;
+ return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort);
}
- private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
-
- private async Task<bool> IsLocalIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken)
+ /// <inheritdoc/>
+ public string GetLocalApiUrl(string host, string scheme = null, int? port = null)
{
- if (address.Equals(IPAddress.Loopback)
- || address.Equals(IPAddress.IPv6Loopback))
- {
- return true;
- }
-
- var apiUrl = GetLocalApiUrl(address) + "/system/ping";
-
- if (_validAddressResults.TryGetValue(apiUrl, out var cachedResult))
- {
- return cachedResult;
- }
-
- try
- {
- using var request = new HttpRequestMessage(HttpMethod.Post, apiUrl);
- using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
- .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
-
- await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
- var result = await System.Text.Json.JsonSerializer.DeserializeAsync<string>(stream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false);
- var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase);
-
- _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid);
- Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, valid);
- return valid;
- }
- catch (OperationCanceledException)
- {
- Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, "Cancelled");
- throw;
- }
- catch (Exception ex)
+ // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
+ // not. For consistency, always trim the trailing slash.
+ return new UriBuilder
{
- Logger.LogDebug(ex, "Ping test result to {0}. Success: {1}", apiUrl, false);
-
- _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false);
- return false;
- }
+ Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp),
+ Host = host,
+ Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort),
+ Path = ServerConfigurationManager.GetNetworkConfiguration().BaseUrl
+ }.ToString().TrimEnd('/');
}
public string FriendlyName =>
@@ -1571,6 +1486,7 @@ namespace Emby.Server.Implementations
_disposed = true;
}
+
}
internal class CertificateInfo
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index c762aa0b8..075b07ca9 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -22,7 +22,6 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="IPNetwork2" Version="2.5.226" />
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index 2e8cc76d2..14201ead2 100644
--- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -8,6 +8,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Events;
+using Jellyfin.Networking.Configuration;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
@@ -56,7 +57,7 @@ namespace Emby.Server.Implementations.EntryPoints
private string GetConfigIdentifier()
{
const char Separator = '|';
- var config = _config.Configuration;
+ var config = _config.GetNetworkConfiguration();
return new StringBuilder(32)
.Append(config.EnableUPnP).Append(Separator)
@@ -93,7 +94,8 @@ namespace Emby.Server.Implementations.EntryPoints
private void Start()
{
- if (!_config.Configuration.EnableUPnP || !_config.Configuration.EnableRemoteAccess)
+ var config = _config.GetNetworkConfiguration();
+ if (!config.EnableUPnP || !config.EnableRemoteAccess)
{
return;
}
@@ -156,11 +158,12 @@ namespace Emby.Server.Implementations.EntryPoints
private IEnumerable<Task> CreatePortMaps(INatDevice device)
{
- yield return CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort);
+ var config = _config.GetNetworkConfiguration();
+ yield return CreatePortMap(device, _appHost.HttpPort, config.PublicPort);
if (_appHost.ListenWithHttps)
{
- yield return CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort);
+ yield return CreatePortMap(device, _appHost.HttpsPort, config.PublicHttpsPort);
}
}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index 8a0c0043a..3a738fd5d 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 = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
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 6730751d5..cfc5278ec 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 = _networkManager.GetRandomUnusedUdpPort();
+ var localPort = UdpHelper.GetRandomUnusedUdpPort();
Directory.CreateDirectory(Path.GetDirectoryName(TempFilePath));
diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs
deleted file mode 100644
index 089ec30e6..000000000
--- a/Emby.Server.Implementations/Networking/NetworkManager.cs
+++ /dev/null
@@ -1,556 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Net.NetworkInformation;
-using System.Net.Sockets;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.Networking
-{
- /// <summary>
- /// Class to take care of network interface management.
- /// </summary>
- public class NetworkManager : INetworkManager
- {
- private readonly ILogger<NetworkManager> _logger;
-
- private IPAddress[] _localIpAddresses;
- private readonly object _localIpAddressSyncLock = new object();
-
- private readonly object _subnetLookupLock = new object();
- private readonly Dictionary<string, List<string>> _subnetLookup = new Dictionary<string, List<string>>(StringComparer.Ordinal);
-
- private List<PhysicalAddress> _macAddresses;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="NetworkManager"/> class.
- /// </summary>
- /// <param name="logger">Logger to use for messages.</param>
- public NetworkManager(ILogger<NetworkManager> logger)
- {
- _logger = logger;
-
- NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;
- NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
- }
-
- /// <inheritdoc/>
- public event EventHandler NetworkChanged;
-
- /// <inheritdoc/>
- public Func<string[]> LocalSubnetsFn { get; set; }
-
- private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
- {
- _logger.LogDebug("NetworkAvailabilityChanged");
- OnNetworkChanged();
- }
-
- private void OnNetworkAddressChanged(object sender, EventArgs e)
- {
- _logger.LogDebug("NetworkAddressChanged");
- OnNetworkChanged();
- }
-
- private void OnNetworkChanged()
- {
- lock (_localIpAddressSyncLock)
- {
- _localIpAddresses = null;
- _macAddresses = null;
- }
-
- NetworkChanged?.Invoke(this, EventArgs.Empty);
- }
-
- /// <inheritdoc/>
- public IPAddress[] GetLocalIpAddresses()
- {
- lock (_localIpAddressSyncLock)
- {
- if (_localIpAddresses == null)
- {
- var addresses = GetLocalIpAddressesInternal().ToArray();
-
- _localIpAddresses = addresses;
- }
-
- return _localIpAddresses;
- }
- }
-
- private List<IPAddress> GetLocalIpAddressesInternal()
- {
- var list = GetIPsDefault().ToList();
-
- if (list.Count == 0)
- {
- list = GetLocalIpAddressesFallback().GetAwaiter().GetResult().ToList();
- }
-
- var listClone = new List<IPAddress>();
-
- var subnets = LocalSubnetsFn();
-
- foreach (var i in list)
- {
- if (i.IsIPv6LinkLocal || i.ToString().StartsWith("169.254.", StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
-
- if (Array.IndexOf(subnets, $"[{i}]") == -1)
- {
- listClone.Add(i);
- }
- }
-
- return listClone
- .OrderBy(i => i.AddressFamily == AddressFamily.InterNetwork ? 0 : 1)
- // .ThenBy(i => listClone.IndexOf(i))
- .GroupBy(i => i.ToString())
- .Select(x => x.First())
- .ToList();
- }
-
- /// <inheritdoc/>
- public bool IsInPrivateAddressSpace(string endpoint)
- {
- return IsInPrivateAddressSpace(endpoint, true);
- }
-
- // Checks if the address in endpoint is an RFC1918, RFC1122, or RFC3927 address
- private bool IsInPrivateAddressSpace(string endpoint, bool checkSubnets)
- {
- if (string.Equals(endpoint, "::1", StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
-
- // IPV6
- if (endpoint.Split('.').Length > 4)
- {
- // Handle ipv4 mapped to ipv6
- var originalEndpoint = endpoint;
- endpoint = endpoint.Replace("::ffff:", string.Empty, StringComparison.OrdinalIgnoreCase);
-
- if (string.Equals(endpoint, originalEndpoint, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- }
-
- // Private address space:
-
- if (string.Equals(endpoint, "localhost", StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
-
- if (!IPAddress.TryParse(endpoint, out var ipAddress))
- {
- return false;
- }
-
- byte[] octet = ipAddress.GetAddressBytes();
-
- if ((octet[0] == 10) ||
- (octet[0] == 172 && (octet[1] >= 16 && octet[1] <= 31)) || // RFC1918
- (octet[0] == 192 && octet[1] == 168) || // RFC1918
- (octet[0] == 127) || // RFC1122
- (octet[0] == 169 && octet[1] == 254)) // RFC3927
- {
- return true;
- }
-
- if (checkSubnets && IsInPrivateAddressSpaceAndLocalSubnet(endpoint))
- {
- return true;
- }
-
- return false;
- }
-
- /// <inheritdoc/>
- public bool IsInPrivateAddressSpaceAndLocalSubnet(string endpoint)
- {
- if (endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase))
- {
- var endpointFirstPart = endpoint.Split('.')[0];
-
- var subnets = GetSubnets(endpointFirstPart);
-
- foreach (var subnet_Match in subnets)
- {
- // logger.LogDebug("subnet_Match:" + subnet_Match);
-
- if (endpoint.StartsWith(subnet_Match + ".", StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- // Gives a list of possible subnets from the system whose interface ip starts with endpointFirstPart
- private List<string> GetSubnets(string endpointFirstPart)
- {
- lock (_subnetLookupLock)
- {
- if (_subnetLookup.TryGetValue(endpointFirstPart, out var subnets))
- {
- return subnets;
- }
-
- subnets = new List<string>();
-
- foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces())
- {
- foreach (var unicastIPAddressInformation in adapter.GetIPProperties().UnicastAddresses)
- {
- if (unicastIPAddressInformation.Address.AddressFamily == AddressFamily.InterNetwork && endpointFirstPart == unicastIPAddressInformation.Address.ToString().Split('.')[0])
- {
- int subnet_Test = 0;
- foreach (string part in unicastIPAddressInformation.IPv4Mask.ToString().Split('.'))
- {
- if (part.Equals("0", StringComparison.Ordinal))
- {
- break;
- }
-
- subnet_Test++;
- }
-
- var subnet_Match = string.Join(".", unicastIPAddressInformation.Address.ToString().Split('.').Take(subnet_Test).ToArray());
-
- // TODO: Is this check necessary?
- if (adapter.OperationalStatus == OperationalStatus.Up)
- {
- subnets.Add(subnet_Match);
- }
- }
- }
- }
-
- _subnetLookup[endpointFirstPart] = subnets;
-
- return subnets;
- }
- }
-
- /// <inheritdoc/>
- public bool IsInLocalNetwork(string endpoint)
- {
- return IsInLocalNetworkInternal(endpoint, true);
- }
-
- /// <inheritdoc/>
- public bool IsAddressInSubnets(string addressString, string[] subnets)
- {
- return IsAddressInSubnets(IPAddress.Parse(addressString), addressString, subnets);
- }
-
- /// <inheritdoc/>
- public bool IsAddressInSubnets(IPAddress address, bool excludeInterfaces, bool excludeRFC)
- {
- byte[] octet = address.GetAddressBytes();
-
- if ((octet[0] == 127) || // RFC1122
- (octet[0] == 169 && octet[1] == 254)) // RFC3927
- {
- // don't use on loopback or 169 interfaces
- return false;
- }
-
- string addressString = address.ToString();
- string excludeAddress = "[" + addressString + "]";
- var subnets = LocalSubnetsFn();
-
- // Include any address if LAN subnets aren't specified
- if (subnets.Length == 0)
- {
- return true;
- }
-
- // Exclude any addresses if they appear in the LAN list in [ ]
- if (Array.IndexOf(subnets, excludeAddress) != -1)
- {
- return false;
- }
-
- return IsAddressInSubnets(address, addressString, subnets);
- }
-
- /// <summary>
- /// Checks if the give address falls within the ranges given in [subnets]. The addresses in subnets can be hosts or subnets in the CIDR format.
- /// </summary>
- /// <param name="address">IPAddress version of the address.</param>
- /// <param name="addressString">The address to check.</param>
- /// <param name="subnets">If true, check against addresses in the LAN settings which have [] arroud and return true if it matches the address give in address.</param>
- /// <returns><c>false</c>if the address isn't in the subnets, <c>true</c> otherwise.</returns>
- private static bool IsAddressInSubnets(IPAddress address, string addressString, string[] subnets)
- {
- foreach (var subnet in subnets)
- {
- var normalizedSubnet = subnet.Trim();
- // Is the subnet a host address and does it match the address being passes?
- if (string.Equals(normalizedSubnet, addressString, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
-
- // Parse CIDR subnets and see if address falls within it.
- if (normalizedSubnet.Contains('/', StringComparison.Ordinal))
- {
- try
- {
- var ipNetwork = IPNetwork.Parse(normalizedSubnet);
- if (ipNetwork.Contains(address))
- {
- return true;
- }
- }
- catch
- {
- // Ignoring - invalid subnet passed encountered.
- }
- }
- }
-
- return false;
- }
-
- private bool IsInLocalNetworkInternal(string endpoint, bool resolveHost)
- {
- if (string.IsNullOrEmpty(endpoint))
- {
- throw new ArgumentNullException(nameof(endpoint));
- }
-
- if (IPAddress.TryParse(endpoint, out var address))
- {
- var addressString = address.ToString();
-
- var localSubnetsFn = LocalSubnetsFn;
- if (localSubnetsFn != null)
- {
- var localSubnets = localSubnetsFn();
- foreach (var subnet in localSubnets)
- {
- // Only validate if there's at least one valid entry.
- if (!string.IsNullOrWhiteSpace(subnet))
- {
- return IsAddressInSubnets(address, addressString, localSubnets) || IsInPrivateAddressSpace(addressString, false);
- }
- }
- }
-
- int lengthMatch = 100;
- if (address.AddressFamily == AddressFamily.InterNetwork)
- {
- lengthMatch = 4;
- if (IsInPrivateAddressSpace(addressString, true))
- {
- return true;
- }
- }
- else if (address.AddressFamily == AddressFamily.InterNetworkV6)
- {
- lengthMatch = 9;
- if (IsInPrivateAddressSpace(endpoint, true))
- {
- return true;
- }
- }
-
- // Should be even be doing this with ipv6?
- if (addressString.Length >= lengthMatch)
- {
- var prefix = addressString.Substring(0, lengthMatch);
-
- if (GetLocalIpAddresses().Any(i => i.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
- {
- return true;
- }
- }
- }
- else if (resolveHost)
- {
- if (Uri.TryCreate(endpoint, UriKind.RelativeOrAbsolute, out var uri))
- {
- try
- {
- var host = uri.DnsSafeHost;
- _logger.LogDebug("Resolving host {0}", host);
-
- address = GetIpAddresses(host).GetAwaiter().GetResult().FirstOrDefault();
-
- if (address != null)
- {
- _logger.LogDebug("{0} resolved to {1}", host, address);
-
- return IsInLocalNetworkInternal(address.ToString(), false);
- }
- }
- catch (InvalidOperationException)
- {
- // Can happen with reverse proxy or IIS url rewriting?
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error resolving hostname");
- }
- }
- }
-
- return false;
- }
-
- private static Task<IPAddress[]> GetIpAddresses(string hostName)
- {
- return Dns.GetHostAddressesAsync(hostName);
- }
-
- private IEnumerable<IPAddress> GetIPsDefault()
- {
- IEnumerable<NetworkInterface> interfaces;
-
- try
- {
- interfaces = NetworkInterface.GetAllNetworkInterfaces()
- .Where(x => x.OperationalStatus == OperationalStatus.Up
- || x.OperationalStatus == OperationalStatus.Unknown);
- }
- catch (NetworkInformationException ex)
- {
- _logger.LogError(ex, "Error in GetAllNetworkInterfaces");
- return Enumerable.Empty<IPAddress>();
- }
-
- return interfaces.SelectMany(network =>
- {
- var ipProperties = network.GetIPProperties();
-
- // Exclude any addresses if they appear in the LAN list in [ ]
-
- return ipProperties.UnicastAddresses
- .Select(i => i.Address)
- .Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6);
- }).GroupBy(i => i.ToString())
- .Select(x => x.First());
- }
-
- private static async Task<IEnumerable<IPAddress>> GetLocalIpAddressesFallback()
- {
- var host = await Dns.GetHostEntryAsync(Dns.GetHostName()).ConfigureAwait(false);
-
- // Reverse them because the last one is usually the correct one
- // It's not fool-proof so ultimately the consumer will have to examine them and decide
- return host.AddressList
- .Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6)
- .Reverse();
- }
-
- /// <summary>
- /// Gets a random port number that is currently available.
- /// </summary>
- /// <returns>System.Int32.</returns>
- public int GetRandomUnusedTcpPort()
- {
- var listener = new TcpListener(IPAddress.Any, 0);
- listener.Start();
- var port = ((IPEndPoint)listener.LocalEndpoint).Port;
- listener.Stop();
- return port;
- }
-
- /// <inheritdoc/>
- public int GetRandomUnusedUdpPort()
- {
- var localEndPoint = new IPEndPoint(IPAddress.Any, 0);
- using (var udpClient = new UdpClient(localEndPoint))
- {
- return ((IPEndPoint)udpClient.Client.LocalEndPoint).Port;
- }
- }
-
- /// <inheritdoc/>
- public List<PhysicalAddress> GetMacAddresses()
- {
- return _macAddresses ??= GetMacAddressesInternal().ToList();
- }
-
- private static IEnumerable<PhysicalAddress> GetMacAddressesInternal()
- => NetworkInterface.GetAllNetworkInterfaces()
- .Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback)
- .Select(x => x.GetPhysicalAddress())
- .Where(x => !x.Equals(PhysicalAddress.None));
-
- /// <inheritdoc/>
- public bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask)
- {
- IPAddress network1 = GetNetworkAddress(address1, subnetMask);
- IPAddress network2 = GetNetworkAddress(address2, subnetMask);
- return network1.Equals(network2);
- }
-
- private IPAddress GetNetworkAddress(IPAddress address, IPAddress subnetMask)
- {
- byte[] ipAdressBytes = address.GetAddressBytes();
- byte[] subnetMaskBytes = subnetMask.GetAddressBytes();
-
- if (ipAdressBytes.Length != subnetMaskBytes.Length)
- {
- throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
- }
-
- byte[] broadcastAddress = new byte[ipAdressBytes.Length];
- for (int i = 0; i < broadcastAddress.Length; i++)
- {
- broadcastAddress[i] = (byte)(ipAdressBytes[i] & subnetMaskBytes[i]);
- }
-
- return new IPAddress(broadcastAddress);
- }
-
- /// <inheritdoc/>
- public IPAddress GetLocalIpSubnetMask(IPAddress address)
- {
- NetworkInterface[] interfaces;
-
- try
- {
- var validStatuses = new[] { OperationalStatus.Up, OperationalStatus.Unknown };
-
- interfaces = NetworkInterface.GetAllNetworkInterfaces()
- .Where(i => validStatuses.Contains(i.OperationalStatus))
- .ToArray();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error in GetAllNetworkInterfaces");
- return null;
- }
-
- foreach (NetworkInterface ni in interfaces)
- {
- foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
- {
- if (ip.Address.Equals(address) && ip.IPv4Mask != null)
- {
- return ip.IPv4Mask;
- }
- }
- }
-
- return null;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs
index b7a59cee2..4fd7ac0c1 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]
- : await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
+ : _appHost.GetSmartApiUrl(((IPEndPoint)endpoint).Address);
if (!string.IsNullOrEmpty(localUrl))
{