diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index 82f5d39770..967263a200 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -16,10 +16,13 @@ namespace Emby.Server.Implementations.Networking public class NetworkManager : INetworkManager { private readonly ILogger _logger; + + private IPAddress[] _localIpAddresses; private readonly object _localIpAddressSyncLock = new object(); + private readonly object _subnetLookupLock = new object(); private readonly Dictionary> _subnetLookup = new Dictionary>(StringComparer.Ordinal); - private IPAddress[] _localIpAddresses; + private List _macAddresses; /// @@ -40,219 +43,6 @@ namespace Emby.Server.Implementations.Networking /// public Func LocalSubnetsFn { get; set; } - /// - public IPAddress[] GetLocalIpAddresses() - { - lock (_localIpAddressSyncLock) - { - if (_localIpAddresses == null) - { - var addresses = GetLocalIpAddressesInternal().ToArray(); - - _localIpAddresses = addresses; - } - - return _localIpAddresses; - } - } - - /// - public bool IsInPrivateAddressSpace(string endpoint) - { - return IsInPrivateAddressSpace(endpoint, true); - } - - /// - public bool IsInLocalNetwork(string endpoint) - { - return IsInLocalNetworkInternal(endpoint, true); - } - - /// - public bool IsAddressInSubnets(string addressString, string[] subnets) - { - return IsAddressInSubnets(IPAddress.Parse(addressString), addressString, subnets); - } - - /// - 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; - } - - /// - /// Gets a random port number that is currently available. - /// - /// System.Int32. - 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)) - { - return ((IPEndPoint)udpClient.Client.LocalEndPoint).Port; - } - } - - /// - public List GetMacAddresses() - { - return _macAddresses ??= GetMacAddressesInternal().ToList(); - } - - /// - public bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask) - { - IPAddress network1 = GetNetworkAddress(address1, subnetMask); - IPAddress network2 = GetNetworkAddress(address2, subnetMask); - return network1.Equals(network2); - } - - /// - 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); - } - - /// - 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; - } - - /// - /// 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. - /// - /// IPAddress version of the address. - /// 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. - /// falseif the address isn't in the subnets, true otherwise. - 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 GetIpAddresses(string hostName) - { - return Dns.GetHostAddressesAsync(hostName); - } - - private static async Task> 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 GetMacAddressesInternal() - => NetworkInterface.GetAllNetworkInterfaces() - .Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback) - .Select(x => x.GetPhysicalAddress()) - .Where(x => !x.Equals(PhysicalAddress.None)); - private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) { _logger.LogDebug("NetworkAvailabilityChanged"); @@ -276,6 +66,22 @@ namespace Emby.Server.Implementations.Networking NetworkChanged?.Invoke(this, EventArgs.Empty); } + /// + public IPAddress[] GetLocalIpAddresses() + { + lock (_localIpAddressSyncLock) + { + if (_localIpAddresses == null) + { + var addresses = GetLocalIpAddressesInternal().ToArray(); + + _localIpAddresses = addresses; + } + + return _localIpAddresses; + } + } + private List GetLocalIpAddressesInternal() { var list = GetIPsDefault().ToList(); @@ -310,6 +116,12 @@ namespace Emby.Server.Implementations.Networking .ToList(); } + /// + 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) { @@ -357,6 +169,29 @@ 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 GetSubnets(string endpointFirstPart) { @@ -403,6 +238,82 @@ namespace Emby.Server.Implementations.Networking } } + /// + public bool IsInLocalNetwork(string endpoint) + { + return IsInLocalNetworkInternal(endpoint, true); + } + + /// + public bool IsAddressInSubnets(string addressString, string[] subnets) + { + return IsAddressInSubnets(IPAddress.Parse(addressString), addressString, subnets); + } + + /// + 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); + } + + /// + /// 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. + /// + /// IPAddress version of the address. + /// 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. + /// falseif the address isn't in the subnets, true otherwise. + 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)) @@ -489,6 +400,11 @@ namespace Emby.Server.Implementations.Networking return false; } + private static Task GetIpAddresses(string hostName) + { + return Dns.GetHostAddressesAsync(hostName); + } + private IEnumerable GetIPsDefault() { IEnumerable interfaces; @@ -518,6 +434,60 @@ namespace Emby.Server.Implementations.Networking .Select(x => x.First()); } + private static async Task> 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(); + } + + /// + /// Gets a random port number that is currently available. + /// + /// System.Int32. + 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)) + { + return ((IPEndPoint)udpClient.Client.LocalEndPoint).Port; + } + } + + /// + public List GetMacAddresses() + { + return _macAddresses ??= GetMacAddressesInternal().ToList(); + } + + private static IEnumerable GetMacAddressesInternal() + => NetworkInterface.GetAllNetworkInterfaces() + .Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback) + .Select(x => x.GetPhysicalAddress()) + .Where(x => !x.Equals(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(); @@ -536,5 +506,38 @@ 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) + { + foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses) + { + if (ip.Address.Equals(address) && ip.IPv4Mask != null) + { + return ip.IPv4Mask; + } + } + } + + return null; + } } }