aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Dlna/Configuration/DlnaOptions.cs2
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs4
-rw-r--r--Emby.Server.Implementations/Networking/NetworkManager.cs60
-rw-r--r--MediaBrowser.Common/Net/INetworkManager.cs3
-rw-r--r--MediaBrowser.Model/Net/IpAddressInfo.cs1
-rw-r--r--RSSDP/ISsdpCommunicationsServer.cs6
-rw-r--r--RSSDP/SsdpCommunicationsServer.cs13
-rw-r--r--RSSDP/SsdpDeviceLocator.cs2
-rw-r--r--RSSDP/SsdpDevicePublisher.cs30
-rw-r--r--RSSDP/SsdpRootDevice.cs10
10 files changed, 116 insertions, 15 deletions
diff --git a/Emby.Dlna/Configuration/DlnaOptions.cs b/Emby.Dlna/Configuration/DlnaOptions.cs
index 0ebb490a1..c7cb364a8 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 8eff7f773..427b3a5c0 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 b7a125f20..8696d1896 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 3364230d1..34c6f5866 100644
--- a/MediaBrowser.Common/Net/INetworkManager.cs
+++ b/MediaBrowser.Common/Net/INetworkManager.cs
@@ -62,5 +62,8 @@ namespace MediaBrowser.Common.Net
Task<IpAddressInfo[]> 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 7a278d4d4..87fa55bca 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 ef75f997f..c99d684a1 100644
--- a/RSSDP/ISsdpCommunicationsServer.cs
+++ b/RSSDP/ISsdpCommunicationsServer.cs
@@ -45,8 +45,8 @@ namespace Rssdp.Infrastructure
/// <summary>
/// Sends a message to the SSDP multicast address and port.
/// </summary>
- 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 9da906b49..ea4d79c99 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);
}
/// <summary>
/// Sends a message to the SSDP multicast address and port.
/// </summary>
- 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 128bdfcbb..e17e14c1a 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 ce64ba117..076246b24 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
/// </summary>
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
/// <summary>
/// Default constructor.
/// </summary>
- 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<string, SearchRequest>(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 a2b0f60f5..d918b9040 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
/// </summary>
public Uri Location { get; set; }
+ /// <summary>
+ /// Gets or sets the Address used to check if the received message from same interface with this device/tree. Required.
+ /// </summary>
+ public IpAddressInfo Address { get; set; }
+
+ /// <summary>
+ /// Gets or sets the SubnetMask used to check if the received message from same interface with this device/tree. Required.
+ /// </summary>
+ public IpAddressInfo SubnetMask { get; set; }
/// <summary>
/// The base URL to use for all relative url's provided in other propertise (and those of child devices). Optional.