From cf4e64f4309a40ff50607d418970bc2767a275bb Mon Sep 17 00:00:00 2001 From: Xu Fasheng Date: Thu, 21 Feb 2019 22:35:31 +0800 Subject: Add option to toggle if ignore virtual interfaces Some VPN like ZerotierOne owns IP address but no gateway, and there is no good idea in NetworkManager.GetIPsDefault() to filter such virtual interfaces, so just provide one option to let user decide it. --- RSSDP/RSSDP.csproj | 1 + RSSDP/SsdpCommunicationsServer.cs | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'RSSDP') diff --git a/RSSDP/RSSDP.csproj b/RSSDP/RSSDP.csproj index f06d4687b9..456a93aa80 100644 --- a/RSSDP/RSSDP.csproj +++ b/RSSDP/RSSDP.csproj @@ -3,6 +3,7 @@ + diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 04e76ef591..9da906b496 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using MediaBrowser.Common.Net; using Microsoft.Extensions.Logging; using MediaBrowser.Model.Net; +using MediaBrowser.Controller.Configuration; namespace Rssdp.Infrastructure { @@ -45,6 +46,7 @@ namespace Rssdp.Infrastructure private readonly ILogger _logger; private ISocketFactory _SocketFactory; private readonly INetworkManager _networkManager; + private readonly IServerConfigurationManager _config; private int _LocalPort; private int _MulticastTtl; @@ -74,9 +76,11 @@ namespace Rssdp.Infrastructure /// Minimum constructor. /// /// The argument is null. - public SsdpCommunicationsServer(ISocketFactory socketFactory, INetworkManager networkManager, ILogger logger, bool enableMultiSocketBinding) + public SsdpCommunicationsServer(IServerConfigurationManager config, ISocketFactory socketFactory, + INetworkManager networkManager, ILogger logger, bool enableMultiSocketBinding) : this(socketFactory, 0, SsdpConstants.SsdpDefaultMulticastTimeToLive, networkManager, logger, enableMultiSocketBinding) { + _config = config; } /// @@ -363,7 +367,7 @@ namespace Rssdp.Infrastructure if (_enableMultiSocketBinding) { - foreach (var address in _networkManager.GetLocalIpAddresses()) + foreach (var address in _networkManager.GetLocalIpAddresses(_config.Configuration.IgnoreVirtualInterfaces)) { if (address.AddressFamily == IpAddressFamily.InterNetworkV6) { -- cgit v1.2.3 From cbd0e71c077e6233bcbc751f9a2f1ee742000ba3 Mon Sep 17 00:00:00 2001 From: Xu Fasheng Date: Fri, 22 Feb 2019 12:06:49 +0800 Subject: Send DLNA devices message to only the matched interface This will be the right way for multiple interfaces, or the client will receive all devices message with different IP addresses and could not detect which one could access. And provide one option DlnaOptions.SendOnlyMatchedHost to fallback to old behaviour if this commit missed something. --- Emby.Dlna/Configuration/DlnaOptions.cs | 2 + Emby.Dlna/Main/DlnaEntryPoint.cs | 4 +- .../Networking/NetworkManager.cs | 60 ++++++++++++++++++++++ MediaBrowser.Common/Net/INetworkManager.cs | 3 ++ MediaBrowser.Model/Net/IpAddressInfo.cs | 1 + RSSDP/ISsdpCommunicationsServer.cs | 6 +-- RSSDP/SsdpCommunicationsServer.cs | 13 ++--- RSSDP/SsdpDeviceLocator.cs | 2 +- RSSDP/SsdpDevicePublisher.cs | 30 +++++++++-- RSSDP/SsdpRootDevice.cs | 10 ++++ 10 files changed, 116 insertions(+), 15 deletions(-) (limited to 'RSSDP') diff --git a/Emby.Dlna/Configuration/DlnaOptions.cs b/Emby.Dlna/Configuration/DlnaOptions.cs index 0ebb490a1f..c7cb364a83 100644 --- a/Emby.Dlna/Configuration/DlnaOptions.cs +++ b/Emby.Dlna/Configuration/DlnaOptions.cs @@ -7,6 +7,7 @@ namespace Emby.Dlna.Configuration public bool EnableServer { get; set; } public bool EnableDebugLog { get; set; } public bool BlastAliveMessages { get; set; } + public bool SendOnlyMatchedHost { get; set; } public int ClientDiscoveryIntervalSeconds { get; set; } public int BlastAliveMessageIntervalSeconds { get; set; } public string DefaultUserId { get; set; } @@ -16,6 +17,7 @@ namespace Emby.Dlna.Configuration EnablePlayTo = true; EnableServer = true; BlastAliveMessages = true; + SendOnlyMatchedHost = true; ClientDiscoveryIntervalSeconds = 60; BlastAliveMessageIntervalSeconds = 1800; } diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 8eff7f773d..427b3a5c0e 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -230,7 +230,7 @@ namespace Emby.Dlna.Main try { - _Publisher = new SsdpDevicePublisher(_communicationsServer, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion); + _Publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion, _config.GetDlnaConfiguration().SendOnlyMatchedHost); _Publisher.LogFunction = LogMessage; _Publisher.SupportPnpRootDevice = false; @@ -269,6 +269,8 @@ namespace Emby.Dlna.Main { 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), FriendlyName = "Jellyfin", Manufacturer = "Jellyfin", ModelName = "Jellyfin Server", diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index b7a125f207..8696d18968 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -636,6 +636,66 @@ namespace Emby.Server.Implementations.Networking return false; } + public bool IsInSameSubnet(IpAddressInfo address1, IpAddressInfo address2, IpAddressInfo subnetMask) + { + IPAddress network1 = GetNetworkAddress(ToIPAddress(address1), ToIPAddress(subnetMask)); + IPAddress network2 = GetNetworkAddress(ToIPAddress(address2), ToIPAddress(subnetMask)); + return network1.Equals(network2); + } + + private IPAddress GetNetworkAddress(IPAddress address, IPAddress subnetMask) + { + byte[] ipAdressBytes = address.GetAddressBytes(); + byte[] subnetMaskBytes = subnetMask.GetAddressBytes(); + + if (ipAdressBytes.Length != subnetMaskBytes.Length) + { + throw new ArgumentException("Lengths of IP address and subnet mask do not match."); + } + + byte[] broadcastAddress = new byte[ipAdressBytes.Length]; + for (int i = 0; i < broadcastAddress.Length; i++) + { + broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i])); + } + return new IPAddress(broadcastAddress); + } + + public IpAddressInfo GetLocalIpSubnetMask(IpAddressInfo address) + { + NetworkInterface[] interfaces; + IPAddress ipaddress = ToIPAddress(address); + + 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(ipaddress) && ip.IPv4Mask != null) + { + return ToIpAddressInfo(ip.IPv4Mask); + } + } + } + } + return null; + } + public static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint) { if (endpoint == null) diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index 3364230d1d..34c6f58665 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -62,5 +62,8 @@ namespace MediaBrowser.Common.Net Task GetHostAddressesAsync(string host); bool IsAddressInSubnets(string addressString, string[] subnets); + + bool IsInSameSubnet(IpAddressInfo address1, IpAddressInfo address2, IpAddressInfo subnetMask); + IpAddressInfo GetLocalIpSubnetMask(IpAddressInfo address); } } diff --git a/MediaBrowser.Model/Net/IpAddressInfo.cs b/MediaBrowser.Model/Net/IpAddressInfo.cs index 7a278d4d41..87fa55bcae 100644 --- a/MediaBrowser.Model/Net/IpAddressInfo.cs +++ b/MediaBrowser.Model/Net/IpAddressInfo.cs @@ -10,6 +10,7 @@ namespace MediaBrowser.Model.Net public static IpAddressInfo IPv6Loopback = new IpAddressInfo("::1", IpAddressFamily.InterNetworkV6); public string Address { get; set; } + public IpAddressInfo SubnetMask { get; set; } public IpAddressFamily AddressFamily { get; set; } public IpAddressInfo(string address, IpAddressFamily addressFamily) diff --git a/RSSDP/ISsdpCommunicationsServer.cs b/RSSDP/ISsdpCommunicationsServer.cs index ef75f997fb..c99d684a13 100644 --- a/RSSDP/ISsdpCommunicationsServer.cs +++ b/RSSDP/ISsdpCommunicationsServer.cs @@ -45,8 +45,8 @@ namespace Rssdp.Infrastructure /// /// Sends a message to the SSDP multicast address and port. /// - Task SendMulticastMessage(string message, CancellationToken cancellationToken); - Task SendMulticastMessage(string message, int sendCount, CancellationToken cancellationToken); + Task SendMulticastMessage(string message, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken); + Task SendMulticastMessage(string message, int sendCount, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken); #endregion @@ -63,4 +63,4 @@ namespace Rssdp.Infrastructure #endregion } -} \ No newline at end of file +} diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 9da906b496..ea4d79c991 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -240,15 +240,15 @@ namespace Rssdp.Infrastructure } } - public Task SendMulticastMessage(string message, CancellationToken cancellationToken) + public Task SendMulticastMessage(string message, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken) { - return SendMulticastMessage(message, SsdpConstants.UdpResendCount, cancellationToken); + return SendMulticastMessage(message, SsdpConstants.UdpResendCount, fromLocalIpAddress, cancellationToken); } /// /// Sends a message to the SSDP multicast address and port. /// - public async Task SendMulticastMessage(string message, int sendCount, CancellationToken cancellationToken) + public async Task SendMulticastMessage(string message, int sendCount, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken) { if (message == null) throw new ArgumentNullException(nameof(message)); @@ -268,7 +268,7 @@ namespace Rssdp.Infrastructure IpAddress = new IpAddressInfo(SsdpConstants.MulticastLocalAdminAddress, IpAddressFamily.InterNetwork), Port = SsdpConstants.MulticastPort - }, cancellationToken).ConfigureAwait(false); + }, fromLocalIpAddress, cancellationToken).ConfigureAwait(false); await Task.Delay(100, cancellationToken).ConfigureAwait(false); } @@ -336,14 +336,15 @@ namespace Rssdp.Infrastructure #region Private Methods - private Task SendMessageIfSocketNotDisposed(byte[] messageData, IpEndPointInfo destination, CancellationToken cancellationToken) + private Task SendMessageIfSocketNotDisposed(byte[] messageData, IpEndPointInfo destination, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken) { var sockets = _sendSockets; if (sockets != null) { sockets = sockets.ToList(); - var tasks = sockets.Select(s => SendFromSocket(s, messageData, destination, cancellationToken)); + var tasks = sockets.Where(s => (fromLocalIpAddress == null || fromLocalIpAddress.Equals(s.LocalIPAddress))) + .Select(s => SendFromSocket(s, messageData, destination, cancellationToken)); return Task.WhenAll(tasks); } diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs index 128bdfcbb4..e17e14c1a6 100644 --- a/RSSDP/SsdpDeviceLocator.cs +++ b/RSSDP/SsdpDeviceLocator.cs @@ -354,7 +354,7 @@ namespace Rssdp.Infrastructure var message = BuildMessage(header, values); - return _CommunicationsServer.SendMulticastMessage(message, cancellationToken); + return _CommunicationsServer.SendMulticastMessage(message, null, cancellationToken); } private void ProcessSearchResponseMessage(HttpResponseMessage message, IpAddressInfo localIpAddress) diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index ce64ba1176..076246b243 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Net; +using MediaBrowser.Common.Net; using Rssdp; namespace Rssdp.Infrastructure @@ -16,10 +17,12 @@ namespace Rssdp.Infrastructure /// public class SsdpDevicePublisher : DisposableManagedObjectBase, ISsdpDevicePublisher { + private readonly INetworkManager _networkManager; private ISsdpCommunicationsServer _CommsServer; private string _OSName; private string _OSVersion; + private bool _sendOnlyMatchedHost; private bool _SupportPnpRootDevice; @@ -37,9 +40,11 @@ namespace Rssdp.Infrastructure /// /// Default constructor. /// - public SsdpDevicePublisher(ISsdpCommunicationsServer communicationsServer, string osName, string osVersion) + public SsdpDevicePublisher(ISsdpCommunicationsServer communicationsServer, INetworkManager networkManager, + string osName, string osVersion, bool sendOnlyMatchedHost) { if (communicationsServer == null) throw new ArgumentNullException(nameof(communicationsServer)); + if (networkManager == null) throw new ArgumentNullException(nameof(networkManager)); if (osName == null) throw new ArgumentNullException(nameof(osName)); if (osName.Length == 0) throw new ArgumentException("osName cannot be an empty string.", nameof(osName)); if (osVersion == null) throw new ArgumentNullException(nameof(osVersion)); @@ -51,10 +56,12 @@ namespace Rssdp.Infrastructure _RecentSearchRequests = new Dictionary(StringComparer.OrdinalIgnoreCase); _Random = new Random(); + _networkManager = networkManager; _CommsServer = communicationsServer; _CommsServer.RequestReceived += CommsServer_RequestReceived; _OSName = osName; _OSVersion = osVersion; + _sendOnlyMatchedHost = sendOnlyMatchedHost; _CommsServer.BeginListeningForBroadcasts(); } @@ -250,7 +257,11 @@ namespace Rssdp.Infrastructure foreach (var device in deviceList) { - SendDeviceSearchResponses(device, remoteEndPoint, receivedOnlocalIpAddress, cancellationToken); + if (!_sendOnlyMatchedHost || + _networkManager.IsInSameSubnet(device.ToRootDevice().Address, remoteEndPoint.IpAddress, device.ToRootDevice().SubnetMask)) + { + SendDeviceSearchResponses(device, remoteEndPoint, receivedOnlocalIpAddress, cancellationToken); + } } } else @@ -427,7 +438,12 @@ namespace Rssdp.Infrastructure var message = BuildMessage(header, values); - _CommsServer.SendMulticastMessage(message, cancellationToken); + if (_sendOnlyMatchedHost) + { + _CommsServer.SendMulticastMessage(message, _sendOnlyMatchedHost ? rootDevice.Address : null, cancellationToken); + } else { + _CommsServer.SendMulticastMessage(message, null, cancellationToken); + } //WriteTrace(String.Format("Sent alive notification"), device); } @@ -472,7 +488,13 @@ namespace Rssdp.Infrastructure var sendCount = IsDisposed ? 1 : 3; WriteTrace(String.Format("Sent byebye notification"), device); - return _CommsServer.SendMulticastMessage(message, sendCount, cancellationToken); + if (_sendOnlyMatchedHost) + { + return _CommsServer.SendMulticastMessage(message, sendCount, + _sendOnlyMatchedHost ? device.ToRootDevice().Address : null, cancellationToken); + } else { + return _CommsServer.SendMulticastMessage(message, sendCount, null, cancellationToken); + } } private void DisposeRebroadcastTimer() diff --git a/RSSDP/SsdpRootDevice.cs b/RSSDP/SsdpRootDevice.cs index a2b0f60f54..d918b9040d 100644 --- a/RSSDP/SsdpRootDevice.cs +++ b/RSSDP/SsdpRootDevice.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Text; using System.Xml; using Rssdp.Infrastructure; +using MediaBrowser.Model.Net; namespace Rssdp { @@ -52,6 +53,15 @@ namespace Rssdp /// public Uri Location { get; set; } + /// + /// Gets or sets the Address used to check if the received message from same interface with this device/tree. Required. + /// + public IpAddressInfo Address { get; set; } + + /// + /// Gets or sets the SubnetMask used to check if the received message from same interface with this device/tree. Required. + /// + public IpAddressInfo SubnetMask { get; set; } /// /// The base URL to use for all relative url's provided in other propertise (and those of child devices). Optional. -- cgit v1.2.3 From 0c49079c16f62c85fe529150a9777e9f2ccc440f Mon Sep 17 00:00:00 2001 From: Xu Fasheng Date: Sat, 23 Feb 2019 09:56:55 +0800 Subject: Update comments for DLNA IPv6 --- Emby.Dlna/Main/DlnaEntryPoint.cs | 2 +- RSSDP/SsdpCommunicationsServer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'RSSDP') diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index d066bd1bd9..5a7c9b617e 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -252,9 +252,9 @@ namespace Emby.Dlna.Main foreach (var address in addresses) { - // TODO: Remove this condition on platforms that support it if (address.AddressFamily == IpAddressFamily.InterNetworkV6) { + // Not support IPv6 right now continue; } diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index ea4d79c991..d9a4b6ac01 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -372,7 +372,7 @@ namespace Rssdp.Infrastructure { if (address.AddressFamily == IpAddressFamily.InterNetworkV6) { - // Not supported ? + // Not support IPv6 right now continue; } -- cgit v1.2.3 From 47966793c0b9ca1d79f35f6010c0a256a4ab12b0 Mon Sep 17 00:00:00 2001 From: Xu Fasheng Date: Sat, 23 Feb 2019 10:15:38 +0800 Subject: Remove useless if..else in SsdpDevicePublisher --- RSSDP/SsdpDevicePublisher.cs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'RSSDP') diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index 076246b243..921f33c212 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -438,12 +438,7 @@ namespace Rssdp.Infrastructure var message = BuildMessage(header, values); - if (_sendOnlyMatchedHost) - { - _CommsServer.SendMulticastMessage(message, _sendOnlyMatchedHost ? rootDevice.Address : null, cancellationToken); - } else { - _CommsServer.SendMulticastMessage(message, null, cancellationToken); - } + _CommsServer.SendMulticastMessage(message, _sendOnlyMatchedHost ? rootDevice.Address : null, cancellationToken); //WriteTrace(String.Format("Sent alive notification"), device); } @@ -488,13 +483,7 @@ namespace Rssdp.Infrastructure var sendCount = IsDisposed ? 1 : 3; WriteTrace(String.Format("Sent byebye notification"), device); - if (_sendOnlyMatchedHost) - { - return _CommsServer.SendMulticastMessage(message, sendCount, - _sendOnlyMatchedHost ? device.ToRootDevice().Address : null, cancellationToken); - } else { - return _CommsServer.SendMulticastMessage(message, sendCount, null, cancellationToken); - } + return _CommsServer.SendMulticastMessage(message, sendCount, _sendOnlyMatchedHost ? device.ToRootDevice().Address : null, cancellationToken); } private void DisposeRebroadcastTimer() -- cgit v1.2.3