diff options
| -rw-r--r-- | Emby.Dlna/Main/DlnaEntryPoint.cs | 136 | ||||
| -rw-r--r-- | Emby.Server.Implementations/ApplicationHost.cs | 2 | ||||
| -rw-r--r-- | Emby.Server.Implementations/Networking/NetworkManager.cs | 503 | ||||
| -rw-r--r-- | Jellyfin.Server/Program.cs | 31 | ||||
| -rw-r--r-- | MediaBrowser.Common/Net/INetworkManager.cs | 53 | ||||
| -rw-r--r-- | RSSDP/SsdpCommunicationsServer.cs | 12 | ||||
| -rw-r--r-- | RSSDP/SsdpDeviceLocator.cs | 2 |
7 files changed, 406 insertions, 333 deletions
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index c5d60b2a0..0b529c10d 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -35,8 +35,6 @@ namespace Emby.Dlna.Main private readonly IServerConfigurationManager _config; private readonly ILogger _logger; private readonly IServerApplicationHost _appHost; - - private PlayToManager _manager; private readonly ISessionManager _sessionManager; private readonly IHttpClient _httpClient; private readonly ILibraryManager _libraryManager; @@ -47,14 +45,13 @@ namespace Emby.Dlna.Main private readonly ILocalizationManager _localization; private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaEncoder _mediaEncoder; - private readonly IDeviceDiscovery _deviceDiscovery; - - private SsdpDevicePublisher _Publisher; - private readonly ISocketFactory _socketFactory; private readonly INetworkManager _networkManager; + private readonly object _syncLock = new object(); + private PlayToManager _manager; + private SsdpDevicePublisher _publisher; private ISsdpCommunicationsServer _communicationsServer; internal IContentDirectory ContentDirectory { get; private set; } @@ -65,7 +62,8 @@ namespace Emby.Dlna.Main public static DlnaEntryPoint Current; - public DlnaEntryPoint(IServerConfigurationManager config, + public DlnaEntryPoint( + IServerConfigurationManager config, ILoggerFactory loggerFactory, IServerApplicationHost appHost, ISessionManager sessionManager, @@ -138,6 +136,55 @@ namespace Emby.Dlna.Main _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated; } + public void Dispose() + { + DisposeDevicePublisher(); + DisposePlayToManager(); + DisposeDeviceDiscovery(); + + if (_communicationsServer != null) + { + _logger.LogInformation("Disposing SsdpCommunicationsServer"); + _communicationsServer.Dispose(); + _communicationsServer = null; + } + + ContentDirectory = null; + ConnectionManager = null; + MediaReceiverRegistrar = null; + Current = null; + } + + public async Task StartDevicePublisher(Configuration.DlnaOptions options) + { + if (!options.BlastAliveMessages) + { + return; + } + + if (_publisher != null) + { + return; + } + + try + { + _publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost) + { + LogFunction = LogMessage, + SupportPnpRootDevice = false + }; + + await RegisterServerEndpoints().ConfigureAwait(false); + + _publisher.StartBroadcastingAliveMessages(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds)); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error registering endpoint"); + } + } + void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) { if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase)) @@ -180,7 +227,7 @@ namespace Emby.Dlna.Main var enableMultiSocketBinding = OperatingSystem.Id == OperatingSystemId.Windows || OperatingSystem.Id == OperatingSystemId.Linux; - _communicationsServer = new SsdpCommunicationsServer(_config, _socketFactory, _networkManager, _logger, enableMultiSocketBinding) + _communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger, enableMultiSocketBinding) { IsShared = true }; @@ -224,34 +271,6 @@ namespace Emby.Dlna.Main } } - public async Task StartDevicePublisher(Configuration.DlnaOptions options) - { - if (!options.BlastAliveMessages) - { - return; - } - - if (_Publisher != null) - { - return; - } - - try - { - _Publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost); - _Publisher.LogFunction = LogMessage; - _Publisher.SupportPnpRootDevice = false; - - await RegisterServerEndpoints().ConfigureAwait(false); - - _Publisher.StartBroadcastingAliveMessages(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds)); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error registering endpoint"); - } - } - private async Task RegisterServerEndpoints() { var addresses = await _appHost.GetLocalIpAddresses(CancellationToken.None).ConfigureAwait(false); @@ -266,6 +285,12 @@ namespace Emby.Dlna.Main continue; } + // Limit to LAN addresses only + if (!_networkManager.IsAddressInSubnets(address, true, true)) + { + continue; + } + var fullService = "urn:schemas-upnp-org:device:MediaServer:1"; _logger.LogInformation("Registering publisher for {0} on {1}", fullService, address); @@ -275,7 +300,7 @@ namespace Emby.Dlna.Main var device = new SsdpRootDevice { - CacheLifetime = TimeSpan.FromSeconds(1800), //How long SSDP clients can cache this info. + 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, SubnetMask = _networkManager.GetLocalIpSubnetMask(address), @@ -287,13 +312,13 @@ namespace Emby.Dlna.Main }; SetProperies(device, fullService); - _Publisher.AddDevice(device); + _publisher.AddDevice(device); var embeddedDevices = new[] { "urn:schemas-upnp-org:service:ContentDirectory:1", "urn:schemas-upnp-org:service:ConnectionManager:1", - //"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1" + // "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1" }; foreach (var subDevice in embeddedDevices) @@ -319,12 +344,13 @@ namespace Emby.Dlna.Main { guid = text.GetMD5(); } + return guid.ToString("N", CultureInfo.InvariantCulture); } private void SetProperies(SsdpDevice device, string fullDeviceType) { - var service = fullDeviceType.Replace("urn:", string.Empty).Replace(":1", string.Empty); + var service = fullDeviceType.Replace("urn:", string.Empty, StringComparison.OrdinalIgnoreCase).Replace(":1", string.Empty, StringComparison.OrdinalIgnoreCase); var serviceParts = service.Split(':'); @@ -335,7 +361,6 @@ namespace Emby.Dlna.Main device.DeviceType = serviceParts[2]; } - private readonly object _syncLock = new object(); private void StartPlayToManager() { lock (_syncLock) @@ -347,7 +372,8 @@ namespace Emby.Dlna.Main try { - _manager = new PlayToManager(_logger, + _manager = new PlayToManager( + _logger, _sessionManager, _libraryManager, _userManager, @@ -386,37 +412,19 @@ namespace Emby.Dlna.Main { _logger.LogError(ex, "Error disposing PlayTo manager"); } + _manager = null; } } } - public void Dispose() - { - DisposeDevicePublisher(); - DisposePlayToManager(); - DisposeDeviceDiscovery(); - - if (_communicationsServer != null) - { - _logger.LogInformation("Disposing SsdpCommunicationsServer"); - _communicationsServer.Dispose(); - _communicationsServer = null; - } - - ContentDirectory = null; - ConnectionManager = null; - MediaReceiverRegistrar = null; - Current = null; - } - public void DisposeDevicePublisher() { - if (_Publisher != null) + if (_publisher != null) { _logger.LogInformation("Disposing SsdpDevicePublisher"); - _Publisher.Dispose(); - _Publisher = null; + _publisher.Dispose(); + _publisher = null; } } } diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 33aec1a06..97fc2c004 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1274,7 +1274,7 @@ namespace Emby.Server.Implementations if (addresses.Count == 0) { - addresses.AddRange(_networkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces)); + addresses.AddRange(_networkManager.GetLocalIpAddresses()); } var resultList = new List<IPAddress>(); diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index b3e88b667..b14b84541 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Net; using System.Net.NetworkInformation; @@ -11,16 +10,22 @@ 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 _logger; - - private IPAddress[] _localIpAddresses; private readonly object _localIpAddressSyncLock = new object(); - private readonly object _subnetLookupLock = new object(); - private Dictionary<string, List<string>> _subnetLookup = new Dictionary<string, List<string>>(StringComparer.Ordinal); + private readonly Dictionary<string, List<string>> _subnetLookup = new Dictionary<string, List<string>>(StringComparer.Ordinal); + private IPAddress[] _localIpAddresses; + 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; @@ -29,10 +34,232 @@ namespace Emby.Server.Implementations.Networking NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged; } + /// <inheritdoc/> public event EventHandler NetworkChanged; + /// <inheritdoc/> public Func<string[]> LocalSubnetsFn { get; set; } + /// <inheritdoc/> + public IPAddress[] GetLocalIpAddresses() + { + lock (_localIpAddressSyncLock) + { + if (_localIpAddresses == null) + { + var addresses = GetLocalIpAddressesInternal().ToArray(); + + _localIpAddresses = addresses; + } + + return _localIpAddresses; + } + } + + /// <inheritdoc/> + public bool IsInPrivateAddressSpace(string endpoint) + { + return IsInPrivateAddressSpace(endpoint, true); + } + + /// <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 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; + } + + /// <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); + var udpClient = new UdpClient(localEndPoint); + using (udpClient) + { + var port = ((IPEndPoint)udpClient.Client.LocalEndPoint).Port; + return port; + } + } + + /// <inheritdoc/> + public List<PhysicalAddress> GetMacAddresses() + { + if (_macAddresses == null) + { + _macAddresses = GetMacAddressesInternal().ToList(); + } + + return _macAddresses; + } + + /// <inheritdoc/> + public bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask) + { + IPAddress network1 = GetNetworkAddress(address1, subnetMask); + IPAddress network2 = GetNetworkAddress(address2, subnetMask); + return network1.Equals(network2); + } + + /// <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(); + + // Exclude any addresses if they appear in the LAN list in [ ] + if (Array.IndexOf(subnets, excludeAddress) != -1) + { + return false; + } + + return IsAddressInSubnets(address, addressString, subnets); + } + + /// <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; + } + + /// <summary> + /// Checks if the give address false within the ranges givin 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 static Task<IPAddress[]> GetIpAddresses(string hostName) + { + return Dns.GetHostAddressesAsync(hostName); + } + + 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(); + } + + private static IEnumerable<PhysicalAddress> GetMacAddressesInternal() + => NetworkInterface.GetAllNetworkInterfaces() + .Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback) + .Select(x => x.GetPhysicalAddress()) + .Where(x => x != null && x != PhysicalAddress.None); + private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) { _logger.LogDebug("NetworkAvailabilityChanged"); @@ -56,57 +283,41 @@ namespace Emby.Server.Implementations.Networking NetworkChanged?.Invoke(this, EventArgs.Empty); } - public IPAddress[] GetLocalIpAddresses(bool ignoreVirtualInterface = true) - { - lock (_localIpAddressSyncLock) - { - if (_localIpAddresses == null) - { - var addresses = GetLocalIpAddressesInternal(ignoreVirtualInterface).ToArray(); - - _localIpAddresses = addresses; - } - - return _localIpAddresses; - } - } - - private List<IPAddress> GetLocalIpAddressesInternal(bool ignoreVirtualInterface) + private List<IPAddress> GetLocalIpAddressesInternal() { - var list = GetIPsDefault(ignoreVirtualInterface).ToList(); + var list = GetIPsDefault().ToList(); if (list.Count == 0) { list = GetLocalIpAddressesFallback().GetAwaiter().GetResult().ToList(); } - var listClone = list.ToList(); + var listClone = new List<IPAddress>(); - return list - .OrderBy(i => i.AddressFamily == AddressFamily.InterNetwork ? 0 : 1) - .ThenBy(i => listClone.IndexOf(i)) - .Where(FilterIpAddress) - .GroupBy(i => i.ToString()) - .Select(x => x.First()) - .ToList(); - } + var subnets = LocalSubnetsFn(); - private static bool FilterIpAddress(IPAddress address) - { - if (address.IsIPv6LinkLocal - || address.ToString().StartsWith("169.", StringComparison.OrdinalIgnoreCase)) + foreach (var i in list) { - return false; - } + if (i.IsIPv6LinkLocal || i.ToString().StartsWith("169.254.", StringComparison.OrdinalIgnoreCase)) + { + continue; + } - return true; - } + if (Array.IndexOf(subnets, "[" + i.ToString() + "]") == -1) + { + listClone.Add(i); + } + } - public bool IsInPrivateAddressSpace(string endpoint) - { - return IsInPrivateAddressSpace(endpoint, true); + return listClone + .OrderBy(i => i.AddressFamily == AddressFamily.InterNetwork ? 0 : 1) + // .ThenBy(i => listClone.IndexOf(i)) + .GroupBy(i => i.ToString()) + .Select(x => x.First()) + .ToList(); } + // 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)) @@ -114,12 +325,12 @@ namespace Emby.Server.Implementations.Networking return true; } - // ipv6 + // IPV6 if (endpoint.Split('.').Length > 4) { // Handle ipv4 mapped to ipv6 var originalEndpoint = endpoint; - endpoint = endpoint.Replace("::ffff:", string.Empty); + endpoint = endpoint.Replace("::ffff:", string.Empty, StringComparison.OrdinalIgnoreCase); if (string.Equals(endpoint, originalEndpoint, StringComparison.OrdinalIgnoreCase)) { @@ -128,23 +339,21 @@ namespace Emby.Server.Implementations.Networking } // Private address space: - // http://en.wikipedia.org/wiki/Private_network - - if (endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase)) - { - return Is172AddressPrivate(endpoint); - } - if (endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) || - endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) || - endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(endpoint, "localhost", StringComparison.OrdinalIgnoreCase)) { return true; } - if (checkSubnets && endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase)) + byte[] octet = IPAddress.Parse(endpoint).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; + return false; } if (checkSubnets && IsInPrivateAddressSpaceAndLocalSubnet(endpoint)) @@ -155,28 +364,7 @@ namespace Emby.Server.Implementations.Networking return false; } - 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) @@ -222,53 +410,6 @@ namespace Emby.Server.Implementations.Networking } } - private static bool Is172AddressPrivate(string endpoint) - { - for (var i = 16; i <= 31; i++) - { - if (endpoint.StartsWith("172." + i.ToString(CultureInfo.InvariantCulture) + ".", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } - - public bool IsInLocalNetwork(string endpoint) - { - return IsInLocalNetworkInternal(endpoint, true); - } - - public bool IsAddressInSubnets(string addressString, string[] subnets) - { - return IsAddressInSubnets(IPAddress.Parse(addressString), addressString, subnets); - } - - private static bool IsAddressInSubnets(IPAddress address, string addressString, string[] subnets) - { - foreach (var subnet in subnets) - { - var normalizedSubnet = subnet.Trim(); - - if (string.Equals(normalizedSubnet, addressString, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - if (normalizedSubnet.Contains('/', StringComparison.Ordinal)) - { - var ipNetwork = IPNetwork.Parse(normalizedSubnet); - if (ipNetwork.Contains(address)) - { - return true; - } - } - } - - return false; - } - private bool IsInLocalNetworkInternal(string endpoint, bool resolveHost) { if (string.IsNullOrEmpty(endpoint)) @@ -286,7 +427,7 @@ namespace Emby.Server.Implementations.Networking var localSubnets = localSubnetsFn(); foreach (var subnet in localSubnets) { - // only validate if there's at least one valid entry + // Only validate if there's at least one valid entry. if (!string.IsNullOrWhiteSpace(subnet)) { return IsAddressInSubnets(address, addressString, localSubnets) || IsInPrivateAddressSpace(addressString, false); @@ -343,7 +484,7 @@ namespace Emby.Server.Implementations.Networking } catch (InvalidOperationException) { - // Can happen with reverse proxy or IIS url rewriting + // Can happen with reverse proxy or IIS url rewriting? } catch (Exception ex) { @@ -355,12 +496,7 @@ namespace Emby.Server.Implementations.Networking return false; } - private static Task<IPAddress[]> GetIpAddresses(string hostName) - { - return Dns.GetHostAddressesAsync(hostName); - } - - private IEnumerable<IPAddress> GetIPsDefault(bool ignoreVirtualInterface) + private IEnumerable<IPAddress> GetIPsDefault() { IEnumerable<NetworkInterface> interfaces; @@ -380,15 +516,7 @@ namespace Emby.Server.Implementations.Networking { var ipProperties = network.GetIPProperties(); - // Try to exclude virtual adapters - // http://stackoverflow.com/questions/8089685/c-sharp-finding-my-machines-local-ip-address-and-not-the-vms - var addr = ipProperties.GatewayAddresses.FirstOrDefault(); - if (addr == null - || (ignoreVirtualInterface - && (addr.Address.Equals(IPAddress.Any) || addr.Address.Equals(IPAddress.IPv6Any)))) - { - return Enumerable.Empty<IPAddress>(); - } + // Exclude any addresses if they appear in the LAN list in [ ] return ipProperties.UnicastAddresses .Select(i => i.Address) @@ -397,64 +525,6 @@ namespace Emby.Server.Implementations.Networking .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; - } - - public int GetRandomUnusedUdpPort() - { - var localEndPoint = new IPEndPoint(IPAddress.Any, 0); - using (var udpClient = new UdpClient(localEndPoint)) - { - var port = ((IPEndPoint)udpClient.Client.LocalEndPoint).Port; - return port; - } - } - - private List<PhysicalAddress> _macAddresses; - public List<PhysicalAddress> GetMacAddresses() - { - if (_macAddresses == null) - { - _macAddresses = GetMacAddressesInternal().ToList(); - } - - return _macAddresses; - } - - private static IEnumerable<PhysicalAddress> GetMacAddressesInternal() - => NetworkInterface.GetAllNetworkInterfaces() - .Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback) - .Select(x => x.GetPhysicalAddress()) - .Where(x => x != null && x != PhysicalAddress.None); - - 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(); @@ -473,40 +543,5 @@ namespace Emby.Server.Implementations.Networking return new IPAddress(broadcastAddress); } - - 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) - { - if (ni.GetIPProperties().GatewayAddresses.FirstOrDefault() != null) - { - foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses) - { - if (ip.Address.Equals(address) && ip.IPv4Mask != null) - { - return ip.IPv4Mask; - } - } - } - } - - return null; - } } } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 069a10b1a..193d30e3a 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -272,6 +272,7 @@ namespace Jellyfin.Server { _logger.LogInformation("Kestrel listening on {IpAddress}", address); options.Listen(address, appHost.HttpPort); + if (appHost.EnableHttps && appHost.Certificate != null) { options.Listen(address, appHost.HttpsPort, listenOptions => @@ -282,18 +283,11 @@ namespace Jellyfin.Server } else if (builderContext.HostingEnvironment.IsDevelopment()) { - try - { - options.Listen(address, appHost.HttpsPort, listenOptions => - { - listenOptions.UseHttps(); - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); - } - catch (Exception ex) + options.Listen(address, appHost.HttpsPort, listenOptions => { - _logger.LogError(ex, "Error whilst listing to https - Is a development certificate installed?"); - } + listenOptions.UseHttps(); + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); } } } @@ -312,18 +306,11 @@ namespace Jellyfin.Server } else if (builderContext.HostingEnvironment.IsDevelopment()) { - try - { - options.ListenAnyIP(appHost.HttpsPort, listenOptions => - { - listenOptions.UseHttps(); - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); - } - catch (Exception ex) + options.ListenAnyIP(appHost.HttpsPort, listenOptions => { - _logger.LogError(ex, "Error whilst listing to https - Is a development certificate installed?"); - } + listenOptions.UseHttps(); + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); } } }) diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index 3ba75abd8..19314ada8 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -11,14 +11,21 @@ namespace MediaBrowser.Common.Net { event EventHandler NetworkChanged; + /// <summary> + /// Contains a function to return the list of user defined LAN addresses + /// </summary> Func<string[]> LocalSubnetsFn { get; set; } /// <summary> - /// Gets a random port number that is currently available. + /// Gets a random port TCP number that is currently available. /// </summary> /// <returns>System.Int32.</returns> int GetRandomUnusedTcpPort(); + /// <summary> + /// Gets a random port UDP number that is currently available. + /// </summary> + /// <returns>System.Int32.</returns> int GetRandomUnusedUdpPort(); /// <summary> @@ -35,18 +42,56 @@ namespace MediaBrowser.Common.Net bool IsInPrivateAddressSpace(string endpoint); /// <summary> + /// Determines whether [is in private address space 10.x.x.x] [the specified endpoint] and exists in the subnets returned by GetSubnets(). + /// </summary> + /// <param name="endpoint">The endpoint.</param> + /// <returns><c>true</c> if [is in private address space 10.x.x.x] [the specified endpoint]; otherwise, <c>false</c>.</returns> + bool IsInPrivateAddressSpaceAndLocalSubnet(string endpoint); + + /// <summary> /// Determines whether [is in local network] [the specified endpoint]. /// </summary> /// <param name="endpoint">The endpoint.</param> /// <returns><c>true</c> if [is in local network] [the specified endpoint]; otherwise, <c>false</c>.</returns> bool IsInLocalNetwork(string endpoint); - - IPAddress[] GetLocalIpAddresses(bool ignoreVirtualInterface); - + + /// <summary> + /// Investigates an caches a list of interface addresses, excluding local link and LAN excluded addresses + /// </summary> + /// <returns>The list of ipaddresses</returns> + IPAddress[] GetLocalIpAddresses(); + + /// <summary> + /// Checks if the give address false within the ranges givin in [subnets]. The addresses in subnets can be hosts or subnets in the CIDR format. + /// </summary> + /// <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> bool IsAddressInSubnets(string addressString, string[] subnets); + /// <summary> + /// Returns true if address is in the LAN list in the config file + /// </summary> + /// <param name="address">The address to check</param> + /// <param name="excludeInterfaces">If true, check against addresses in the LAN settings which have [] arroud and return true if it matches the address give in address</param> + /// <param name="excludeRFC">If true, returns false if address is in the 127.x.x.x or 169.128.x.x range</param> + /// <returns><c>false</c>if the address isn't in the LAN list, <c>true</c> if the address has been defined as a LAN address</returns> + bool IsAddressInSubnets(IPAddress address, bool excludeInterfaces, bool excludeRFC); + + /// <summary> + /// Checks if address is in the LAN list in the config file + /// </summary> + /// <param name="address1">Source address to check</param> + /// <param name="address2">Destination address to check against</param> + /// <param name="subnetMask">Destination subnet to check against</param> + /// <returns><c>true/false</c>depending on whether address1 is in the same subnet as IPAddress2 with subnetMas</returns> bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask); + /// <summary> + /// Returns the subnet mask of an interface with the given address + /// </summary> + /// <param name="address">The address to check</param> + /// <returns>Returns the subnet mask of an interface with the given address, or null if an interface match cannot be found</returns> IPAddress GetLocalIpSubnetMask(IPAddress address); } } diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 18097ef24..227190575 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -8,7 +8,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; @@ -46,8 +45,7 @@ namespace Rssdp.Infrastructure private HttpResponseParser _ResponseParser; private readonly ILogger _logger; private ISocketFactory _SocketFactory; - private readonly INetworkManager _networkManager; - private readonly IServerConfigurationManager _config; + private readonly INetworkManager _networkManager; private int _LocalPort; private int _MulticastTtl; @@ -77,11 +75,11 @@ namespace Rssdp.Infrastructure /// Minimum constructor. /// </summary> /// <exception cref="ArgumentNullException">The <paramref name="socketFactory"/> argument is null.</exception> - public SsdpCommunicationsServer(IServerConfigurationManager config, ISocketFactory socketFactory, + public SsdpCommunicationsServer(ISocketFactory socketFactory, INetworkManager networkManager, ILogger logger, bool enableMultiSocketBinding) : this(socketFactory, 0, SsdpConstants.SsdpDefaultMulticastTimeToLive, networkManager, logger, enableMultiSocketBinding) { - _config = config; + } /// <summary> @@ -370,13 +368,13 @@ namespace Rssdp.Infrastructure if (_enableMultiSocketBinding) { - foreach (var address in _networkManager.GetLocalIpAddresses(_config.Configuration.IgnoreVirtualInterfaces)) + foreach (var address in _networkManager.GetLocalIpAddresses()) { if (address.AddressFamily == AddressFamily.InterNetworkV6) { // Not support IPv6 right now continue; - } + } try { diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs index 59a2710d5..b62c50e28 100644 --- a/RSSDP/SsdpDeviceLocator.cs +++ b/RSSDP/SsdpDeviceLocator.cs @@ -357,7 +357,7 @@ namespace Rssdp.Infrastructure private void ProcessSearchResponseMessage(HttpResponseMessage message, IPAddress localIpAddress) { if (!message.IsSuccessStatusCode) return; - + var location = GetFirstHeaderUriValue("Location", message); if (location != null) { |
