|
@@ -16,10 +16,13 @@ namespace Emby.Server.Implementations.Networking
|
|
|
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 IPAddress[] _localIpAddresses;
|
|
|
+
|
|
|
private List<PhysicalAddress> _macAddresses;
|
|
|
|
|
|
/// <summary>
|
|
@@ -40,219 +43,6 @@ namespace Emby.Server.Implementations.Networking
|
|
|
/// <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);
|
|
|
- using (var udpClient = new UdpClient(localEndPoint))
|
|
|
- {
|
|
|
- return ((IPEndPoint)udpClient.Client.LocalEndPoint).Port;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- public List<PhysicalAddress> GetMacAddresses()
|
|
|
- {
|
|
|
- return _macAddresses ??= GetMacAddressesInternal().ToList();
|
|
|
- }
|
|
|
-
|
|
|
- /// <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 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 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.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);
|
|
|
}
|
|
|
|
|
|
+ /// <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();
|
|
@@ -310,6 +116,12 @@ namespace Emby.Server.Implementations.Networking
|
|
|
.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)
|
|
|
{
|
|
@@ -357,6 +169,29 @@ namespace Emby.Server.Implementations.Networking
|
|
|
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)
|
|
|
{
|
|
@@ -403,6 +238,82 @@ namespace Emby.Server.Implementations.Networking
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <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();
|
|
|
+
|
|
|
+ // 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))
|
|
@@ -489,6 +400,11 @@ namespace Emby.Server.Implementations.Networking
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ private static Task<IPAddress[]> GetIpAddresses(string hostName)
|
|
|
+ {
|
|
|
+ return Dns.GetHostAddressesAsync(hostName);
|
|
|
+ }
|
|
|
+
|
|
|
private IEnumerable<IPAddress> GetIPsDefault()
|
|
|
{
|
|
|
IEnumerable<NetworkInterface> interfaces;
|
|
@@ -518,6 +434,60 @@ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <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();
|
|
@@ -536,5 +506,38 @@ namespace Emby.Server.Implementations.Networking
|
|
|
|
|
|
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;
|
|
|
+ }
|
|
|
}
|
|
|
}
|