Whilst fixing issues with SSDP on devices with multiple interfaces, i came across a design issue in the current code - namely interfaces without a gateway were ignored.

Fixing this required the removal of the code that attempted to detect virtual interfaces. Not wanting to remove functionality, but not able to keep the code in place, I implemented a work around solution (see 4 below).

Whilst in the area, I also fixed a few minor bugs i encountered (1, 5, 6 below) and stopped SSDP messages from going out on non-LAN interfaces (3)

All these changes are related.

Changes

1 IsInPrivateAddressSpace - improved subnet code checking
2 interfaces with no gateway were being excluded from SSDP blasts
3 filtered SSDP blasts from not LAN addresses as defined on the network page.
4 removed #986 mod - as this was part of the issue of #2986. Interfaces can be excluded from the LAN by putting the LAN address in brackets. eg. [10.1.1.1] will exclude an interface with ip address 10.1.1.1 from SSDP
5 fixed a problem where an invalid LAN address causing the SSDP to crash
6 corrected local link filter (FilterIPAddress) to filter on 169.254. addresses
This commit is contained in:
BaronGreenback 2020-04-28 21:45:46 +01:00
parent aa6d52277d
commit 2aaecb8e14
6 changed files with 98 additions and 71 deletions

View File

@ -266,6 +266,12 @@ namespace Emby.Dlna.Main
continue; continue;
} }
// Limit to LAN addresses only
if (!_networkManager.IsAddressInSubnets(address, true, true))
{
continue;
}
var fullService = "urn:schemas-upnp-org:device:MediaServer:1"; var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
_logger.LogInformation("Registering publisher for {0} on {1}", fullService, address); _logger.LogInformation("Registering publisher for {0} on {1}", fullService, address);

View File

@ -1274,7 +1274,7 @@ namespace Emby.Server.Implementations
if (addresses.Count == 0) if (addresses.Count == 0)
{ {
addresses.AddRange(_networkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces)); addresses.AddRange(_networkManager.GetLocalIpAddresses());
} }
var resultList = new List<IPAddress>(); var resultList = new List<IPAddress>();

View File

@ -56,13 +56,13 @@ namespace Emby.Server.Implementations.Networking
NetworkChanged?.Invoke(this, EventArgs.Empty); NetworkChanged?.Invoke(this, EventArgs.Empty);
} }
public IPAddress[] GetLocalIpAddresses(bool ignoreVirtualInterface = true) public IPAddress[] GetLocalIpAddresses()
{ {
lock (_localIpAddressSyncLock) lock (_localIpAddressSyncLock)
{ {
if (_localIpAddresses == null) if (_localIpAddresses == null)
{ {
var addresses = GetLocalIpAddressesInternal(ignoreVirtualInterface).ToArray(); var addresses = GetLocalIpAddressesInternal().ToArray();
_localIpAddresses = addresses; _localIpAddresses = addresses;
} }
@ -71,42 +71,45 @@ 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) if (list.Count == 0)
{ {
list = GetLocalIpAddressesFallback().GetAwaiter().GetResult().ToList(); list = GetLocalIpAddressesFallback().GetAwaiter().GetResult().ToList();
} }
var listClone = list.ToList(); var listClone = new List<IPAddress>();
return list var subnets = LocalSubnetsFn();
foreach (var i in list)
{
if (i.IsIPv6LinkLocal || i.ToString().StartsWith("169.254.", StringComparison.OrdinalIgnoreCase))
{
continue;
}
if (Array.IndexOf(subnets, "[" + i.ToString() + "]") == -1)
{
listClone.Add(i);
}
}
return listClone
.OrderBy(i => i.AddressFamily == AddressFamily.InterNetwork ? 0 : 1) .OrderBy(i => i.AddressFamily == AddressFamily.InterNetwork ? 0 : 1)
.ThenBy(i => listClone.IndexOf(i)) // .ThenBy(i => listClone.IndexOf(i))
.Where(FilterIpAddress)
.GroupBy(i => i.ToString()) .GroupBy(i => i.ToString())
.Select(x => x.First()) .Select(x => x.First())
.ToList(); .ToList();
} }
private static bool FilterIpAddress(IPAddress address)
{
if (address.IsIPv6LinkLocal
|| address.ToString().StartsWith("169.", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return true;
}
public bool IsInPrivateAddressSpace(string endpoint) public bool IsInPrivateAddressSpace(string endpoint)
{ {
return IsInPrivateAddressSpace(endpoint, true); return IsInPrivateAddressSpace(endpoint, true);
} }
// checks if the address in endpoint is an RFC1918, RFC1122, or RFC3927 address
private bool IsInPrivateAddressSpace(string endpoint, bool checkSubnets) private bool IsInPrivateAddressSpace(string endpoint, bool checkSubnets)
{ {
if (string.Equals(endpoint, "::1", StringComparison.OrdinalIgnoreCase)) if (string.Equals(endpoint, "::1", StringComparison.OrdinalIgnoreCase))
@ -128,23 +131,28 @@ namespace Emby.Server.Implementations.Networking
} }
// Private address space: // Private address space:
// http://en.wikipedia.org/wiki/Private_network
if (endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase)) if (endpoint.ToLower() == "localhost")
{
return Is172AddressPrivate(endpoint);
}
if (endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
{ {
return true; return true;
} }
if (checkSubnets && endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase)) try
{ {
return true; 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 false;
}
}
catch
{
// return false;
} }
if (checkSubnets && IsInPrivateAddressSpaceAndLocalSubnet(endpoint)) if (checkSubnets && IsInPrivateAddressSpaceAndLocalSubnet(endpoint))
@ -177,6 +185,7 @@ namespace Emby.Server.Implementations.Networking
return false; return false;
} }
// Gives a list of possible subnets from the system whose interface ip starts with endpointFirstPart
private List<string> GetSubnets(string endpointFirstPart) private List<string> GetSubnets(string endpointFirstPart)
{ {
lock (_subnetLookupLock) lock (_subnetLookupLock)
@ -222,19 +231,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) public bool IsInLocalNetwork(string endpoint)
{ {
return IsInLocalNetworkInternal(endpoint, true); return IsInLocalNetworkInternal(endpoint, true);
@ -245,18 +241,47 @@ namespace Emby.Server.Implementations.Networking
return IsAddressInSubnets(IPAddress.Parse(addressString), addressString, subnets); return IsAddressInSubnets(IPAddress.Parse(addressString), addressString, subnets);
} }
// returns true if address is in the LAN list in the config file
// always returns false if address has been excluded from the LAN if excludeInterfaces is true
// and excludes RFC addresses if excludeRFC is true
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 to see if address/addressString (same but different type) falls within subnets[]
private static bool IsAddressInSubnets(IPAddress address, string addressString, string[] subnets) private static bool IsAddressInSubnets(IPAddress address, string addressString, string[] subnets)
{ {
foreach (var subnet in subnets) foreach (var subnet in subnets)
{ {
var normalizedSubnet = subnet.Trim(); 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)) if (string.Equals(normalizedSubnet, addressString, StringComparison.OrdinalIgnoreCase))
{ {
return true; return true;
} }
// parse CIDR subnets and see if address falls within it.
if (normalizedSubnet.Contains('/', StringComparison.Ordinal)) if (normalizedSubnet.Contains('/', StringComparison.Ordinal))
{
try
{ {
var ipNetwork = IPNetwork.Parse(normalizedSubnet); var ipNetwork = IPNetwork.Parse(normalizedSubnet);
if (ipNetwork.Contains(address)) if (ipNetwork.Contains(address))
@ -264,6 +289,11 @@ namespace Emby.Server.Implementations.Networking
return true; return true;
} }
} }
catch
{
// Ignoring - invalid subnet passed encountered.
}
}
} }
return false; return false;
@ -360,7 +390,7 @@ namespace Emby.Server.Implementations.Networking
return Dns.GetHostAddressesAsync(hostName); return Dns.GetHostAddressesAsync(hostName);
} }
private IEnumerable<IPAddress> GetIPsDefault(bool ignoreVirtualInterface) private IEnumerable<IPAddress> GetIPsDefault()
{ {
IEnumerable<NetworkInterface> interfaces; IEnumerable<NetworkInterface> interfaces;
@ -380,15 +410,7 @@ namespace Emby.Server.Implementations.Networking
{ {
var ipProperties = network.GetIPProperties(); var ipProperties = network.GetIPProperties();
// Try to exclude virtual adapters // Exclude any addresses if they appear in the LAN list in [ ]
// 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>();
}
return ipProperties.UnicastAddresses return ipProperties.UnicastAddresses
.Select(i => i.Address) .Select(i => i.Address)
@ -493,8 +515,6 @@ namespace Emby.Server.Implementations.Networking
} }
foreach (NetworkInterface ni in interfaces) 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)
{ {
@ -504,7 +524,6 @@ namespace Emby.Server.Implementations.Networking
} }
} }
} }
}
return null; return null;
} }

View File

@ -41,10 +41,12 @@ namespace MediaBrowser.Common.Net
/// <returns><c>true</c> if [is in local network] [the specified endpoint]; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if [is in local network] [the specified endpoint]; otherwise, <c>false</c>.</returns>
bool IsInLocalNetwork(string endpoint); bool IsInLocalNetwork(string endpoint);
IPAddress[] GetLocalIpAddresses(bool ignoreVirtualInterface); IPAddress[] GetLocalIpAddresses();
bool IsAddressInSubnets(string addressString, string[] subnets); bool IsAddressInSubnets(string addressString, string[] subnets);
bool IsAddressInSubnets(IPAddress address, bool excludeInterfaces, bool excludeRFC);
bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask); bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask);
IPAddress GetLocalIpSubnetMask(IPAddress address); IPAddress GetLocalIpSubnetMask(IPAddress address);

View File

@ -370,7 +370,7 @@ namespace Rssdp.Infrastructure
if (_enableMultiSocketBinding) if (_enableMultiSocketBinding)
{ {
foreach (var address in _networkManager.GetLocalIpAddresses(_config.Configuration.IgnoreVirtualInterfaces)) foreach (var address in _networkManager.GetLocalIpAddresses())
{ {
if (address.AddressFamily == AddressFamily.InterNetworkV6) if (address.AddressFamily == AddressFamily.InterNetworkV6)
{ {