diff options
Diffstat (limited to 'Emby.Server.Implementations/Networking/NetworkManager.cs')
| -rw-r--r-- | Emby.Server.Implementations/Networking/NetworkManager.cs | 200 |
1 files changed, 121 insertions, 79 deletions
diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index 45864bb42..50cb44b28 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Net; using System.Net.NetworkInformation; @@ -13,6 +12,9 @@ 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; @@ -21,8 +23,14 @@ namespace Emby.Server.Implementations.Networking 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 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; @@ -31,8 +39,10 @@ namespace Emby.Server.Implementations.Networking NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged; } + /// <inheritdoc/> public event EventHandler NetworkChanged; + /// <inheritdoc/> public Func<string[]> LocalSubnetsFn { get; set; } private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) @@ -58,13 +68,14 @@ namespace Emby.Server.Implementations.Networking NetworkChanged?.Invoke(this, EventArgs.Empty); } - public IPAddress[] GetLocalIpAddresses(bool ignoreVirtualInterface = true) + /// <inheritdoc/> + public IPAddress[] GetLocalIpAddresses() { lock (_localIpAddressSyncLock) { if (_localIpAddresses == null) { - var addresses = GetLocalIpAddressesInternal(ignoreVirtualInterface).ToArray(); + var addresses = GetLocalIpAddressesInternal().ToArray(); _localIpAddresses = addresses; } @@ -73,42 +84,47 @@ namespace Emby.Server.Implementations.Networking } } - 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; + } + + if (Array.IndexOf(subnets, $"[{i}]") == -1) + { + listClone.Add(i); + } } - return 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(); } + /// <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)) @@ -116,12 +132,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)) { @@ -130,23 +146,26 @@ namespace Emby.Server.Implementations.Networking } // Private address space: - // http://en.wikipedia.org/wiki/Private_network - if (endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(endpoint, "localhost", StringComparison.OrdinalIgnoreCase)) { - return Is172AddressPrivate(endpoint); + return true; } - if (endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) || - endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) || - endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase)) + if (!IPAddress.TryParse(endpoint, out var ipAddress)) { - return true; + return false; } - if (checkSubnets && endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase)) + 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; + return false; } if (checkSubnets && IsInPrivateAddressSpaceAndLocalSubnet(endpoint)) @@ -157,6 +176,7 @@ namespace Emby.Server.Implementations.Networking return false; } + /// <inheritdoc/> public bool IsInPrivateAddressSpaceAndLocalSubnet(string endpoint) { if (endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase)) @@ -167,7 +187,7 @@ namespace Emby.Server.Implementations.Networking foreach (var subnet_Match in subnets) { - //logger.LogDebug("subnet_Match:" + subnet_Match); + // logger.LogDebug("subnet_Match:" + subnet_Match); if (endpoint.StartsWith(subnet_Match + ".", StringComparison.OrdinalIgnoreCase)) { @@ -179,6 +199,7 @@ namespace Emby.Server.Implementations.Networking 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) @@ -224,46 +245,81 @@ 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; - } - + /// <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)) { - var ipNetwork = IPNetwork.Parse(normalizedSubnet); - if (ipNetwork.Contains(address)) + try { - return true; + var ipNetwork = IPNetwork.Parse(normalizedSubnet); + if (ipNetwork.Contains(address)) + { + return true; + } + } + catch + { + // Ignoring - invalid subnet passed encountered. } } } @@ -288,7 +344,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); @@ -345,7 +401,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) { @@ -362,7 +418,7 @@ namespace Emby.Server.Implementations.Networking return Dns.GetHostAddressesAsync(hostName); } - private IEnumerable<IPAddress> GetIPsDefault(bool ignoreVirtualInterface) + private IEnumerable<IPAddress> GetIPsDefault() { IEnumerable<NetworkInterface> interfaces; @@ -382,15 +438,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) @@ -411,7 +459,7 @@ namespace Emby.Server.Implementations.Networking } /// <summary> - /// Gets a random port number that is currently available + /// Gets a random port number that is currently available. /// </summary> /// <returns>System.Int32.</returns> public int GetRandomUnusedTcpPort() @@ -423,33 +471,29 @@ namespace Emby.Server.Implementations.Networking return port; } + /// <inheritdoc/> 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; + return ((IPEndPoint)udpClient.Client.LocalEndPoint).Port; } } - private List<PhysicalAddress> _macAddresses; + /// <inheritdoc/> public List<PhysicalAddress> GetMacAddresses() { - if (_macAddresses == null) - { - _macAddresses = GetMacAddressesInternal().ToList(); - } - - return _macAddresses; + return _macAddresses ??= GetMacAddressesInternal().ToList(); } private static IEnumerable<PhysicalAddress> GetMacAddressesInternal() => NetworkInterface.GetAllNetworkInterfaces() .Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback) .Select(x => x.GetPhysicalAddress()) - .Where(x => x != null && x != PhysicalAddress.None); + .Where(x => !x.Equals(PhysicalAddress.None)); + /// <inheritdoc/> public bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask) { IPAddress network1 = GetNetworkAddress(address1, subnetMask); @@ -476,6 +520,7 @@ namespace Emby.Server.Implementations.Networking return new IPAddress(broadcastAddress); } + /// <inheritdoc/> public IPAddress GetLocalIpSubnetMask(IPAddress address) { NetworkInterface[] interfaces; @@ -496,14 +541,11 @@ namespace Emby.Server.Implementations.Networking foreach (NetworkInterface ni in interfaces) { - if (ni.GetIPProperties().GatewayAddresses.FirstOrDefault() != null) + foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses) { - foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses) + if (ip.Address.Equals(address) && ip.IPv4Mask != null) { - if (ip.Address.Equals(address) && ip.IPv4Mask != null) - { - return ip.IPv4Mask; - } + return ip.IPv4Mask; } } } |
