Enforce interface bindings on SSDP, add Loopback to LAN if no LAN defined

This commit is contained in:
Shadowghost 2022-07-21 09:26:18 +02:00
parent 05458a4a42
commit f6e41269d9
5 changed files with 74 additions and 41 deletions

View File

@ -284,6 +284,7 @@ namespace Emby.Dlna.Main
var udn = CreateUuid(_appHost.SystemId); var udn = CreateUuid(_appHost.SystemId);
var descriptorUri = "/dlna/" + udn + "/description.xml"; var descriptorUri = "/dlna/" + udn + "/description.xml";
// Only get bind addresses in LAN
var bindAddresses = _networkManager var bindAddresses = _networkManager
.GetInternalBindAddresses() .GetInternalBindAddresses()
.Where(i => i.Address.AddressFamily == AddressFamily.InterNetwork .Where(i => i.Address.AddressFamily == AddressFamily.InterNetwork
@ -304,12 +305,6 @@ namespace Emby.Dlna.Main
continue; continue;
} }
// Limit to LAN addresses only
if (!_networkManager.IsInLocalNetwork(address.Address))
{
continue;
}
var fullService = "urn:schemas-upnp-org:device:MediaServer:1"; var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
_logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, address); _logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, address);

View File

@ -61,13 +61,18 @@ namespace Emby.Server.Implementations.Net
} }
/// <inheritdoc /> /// <inheritdoc />
public ISocket CreateUdpMulticastSocket(IPAddress ipAddress, int multicastTimeToLive, int localPort) public ISocket CreateUdpMulticastSocket(IPAddress ipAddress, IPAddress bindIpAddress, int multicastTimeToLive, int localPort)
{ {
if (ipAddress == null) if (ipAddress == null)
{ {
throw new ArgumentNullException(nameof(ipAddress)); throw new ArgumentNullException(nameof(ipAddress));
} }
if (bindIpAddress == null)
{
bindIpAddress = IPAddress.Any;
}
if (multicastTimeToLive <= 0) if (multicastTimeToLive <= 0)
{ {
throw new ArgumentException("multicastTimeToLive cannot be zero or less.", nameof(multicastTimeToLive)); throw new ArgumentException("multicastTimeToLive cannot be zero or less.", nameof(multicastTimeToLive));
@ -98,12 +103,10 @@ namespace Emby.Server.Implementations.Net
// retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); // retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive); retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive);
var localIp = IPAddress.Any; retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ipAddress, bindIpAddress));
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ipAddress, localIp));
retVal.MulticastLoopback = true; retVal.MulticastLoopback = true;
return new UdpSocket(retVal, localPort, localIp); return new UdpSocket(retVal, localPort, bindIpAddress);
} }
catch catch
{ {

View File

@ -298,20 +298,22 @@ namespace Jellyfin.Networking.Manager
if (_lanSubnets.Count == 0) if (_lanSubnets.Count == 0)
{ {
// If no LAN addresses are specified, all private subnets are deemed to be the LAN // If no LAN addresses are specified, all private subnets and Loopback are deemed to be the LAN
_logger.LogDebug("Using LAN interface addresses as user provided no LAN details."); _logger.LogDebug("Using LAN interface addresses as user provided no LAN details.");
if (IsIpv6Enabled) if (IsIpv6Enabled)
{ {
_lanSubnets.Add(new IPNetwork(IPAddress.Parse("fc00::"), 7)); // ULA _lanSubnets.Add(new IPNetwork(IPAddress.IPv6Loopback, 128)); // RFC 4291 (Loopback)
_lanSubnets.Add(new IPNetwork(IPAddress.Parse("fe80::"), 10)); // Site local _lanSubnets.Add(new IPNetwork(IPAddress.Parse("fe80::"), 10)); // RFC 4291 (Site local)
_lanSubnets.Add(new IPNetwork(IPAddress.Parse("fc00::"), 7)); // RFC 4193 (Unique local)
} }
if (IsIpv4Enabled) if (IsIpv4Enabled)
{ {
_lanSubnets.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8)); _lanSubnets.Add(new IPNetwork(IPAddress.Loopback, 8)); // RFC 5735 (Loopback)
_lanSubnets.Add(new IPNetwork(IPAddress.Parse("172.16.0.0"), 12)); _lanSubnets.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8)); // RFC 1918 (private)
_lanSubnets.Add(new IPNetwork(IPAddress.Parse("192.168.0.0"), 16)); _lanSubnets.Add(new IPNetwork(IPAddress.Parse("172.16.0.0"), 12)); // RFC 1918 (private)
_lanSubnets.Add(new IPNetwork(IPAddress.Parse("192.168.0.0"), 16)); // RFC 1918 (private)
} }
} }
@ -371,11 +373,13 @@ namespace Jellyfin.Networking.Manager
} }
} }
// Remove all IPv4 interfaces if IPv4 is disabled
if (!IsIpv4Enabled) if (!IsIpv4Enabled)
{ {
_interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetwork); _interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetwork);
} }
// Remove all IPv6 interfaces if IPv6 is disabled
if (!IsIpv6Enabled) if (!IsIpv6Enabled)
{ {
_interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetworkV6); _interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetworkV6);

View File

@ -1,5 +1,6 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using System.Collections.Generic;
using System.Net; using System.Net;
namespace MediaBrowser.Model.Net namespace MediaBrowser.Model.Net
@ -23,9 +24,10 @@ namespace MediaBrowser.Model.Net
/// Creates a new multicast socket using the specified multicast IP address, multicast time to live and local port. /// Creates a new multicast socket using the specified multicast IP address, multicast time to live and local port.
/// </summary> /// </summary>
/// <param name="ipAddress">The multicast IP address to bind to.</param> /// <param name="ipAddress">The multicast IP address to bind to.</param>
/// <param name="bindIpAddress">The bind IP address.</param>
/// <param name="multicastTimeToLive">The multicast time to live value. Actually a maximum number of network hops for UDP packets.</param> /// <param name="multicastTimeToLive">The multicast time to live value. Actually a maximum number of network hops for UDP packets.</param>
/// <param name="localPort">The local port to bind to.</param> /// <param name="localPort">The local port to bind to.</param>
/// <returns>A <see cref="ISocket"/> implementation.</returns> /// <returns>A <see cref="ISocket"/> implementation.</returns>
ISocket CreateUdpMulticastSocket(IPAddress ipAddress, int multicastTimeToLive, int localPort); ISocket CreateUdpMulticastSocket(IPAddress ipAddress, IPAddress bindIpAddress, int multicastTimeToLive, int localPort);
} }
} }

View File

@ -33,7 +33,7 @@ namespace Rssdp.Infrastructure
*/ */
private object _BroadcastListenSocketSynchroniser = new object(); private object _BroadcastListenSocketSynchroniser = new object();
private ISocket _BroadcastListenSocket; private List<ISocket> _BroadcastListenSockets;
private object _SendSocketSynchroniser = new object(); private object _SendSocketSynchroniser = new object();
private List<ISocket> _sendSockets; private List<ISocket> _sendSockets;
@ -111,24 +111,21 @@ namespace Rssdp.Infrastructure
{ {
ThrowIfDisposed(); ThrowIfDisposed();
if (_BroadcastListenSocket == null) lock (_BroadcastListenSocketSynchroniser)
{ {
lock (_BroadcastListenSocketSynchroniser) if (_BroadcastListenSockets == null)
{ {
if (_BroadcastListenSocket == null) try
{ {
try _BroadcastListenSockets = ListenForBroadcastsAsync();
{ }
_BroadcastListenSocket = ListenForBroadcastsAsync(); catch (SocketException ex)
} {
catch (SocketException ex) _logger.LogError("Failed to bind to port 1900: {Message}. DLNA will be unavailable", ex.Message);
{ }
_logger.LogError("Failed to bind to port 1900: {Message}. DLNA will be unavailable", ex.Message); catch (Exception ex)
} {
catch (Exception ex) _logger.LogError(ex, "Error in BeginListeningForBroadcasts");
{
_logger.LogError(ex, "Error in BeginListeningForBroadcasts");
}
} }
} }
} }
@ -142,11 +139,15 @@ namespace Rssdp.Infrastructure
{ {
lock (_BroadcastListenSocketSynchroniser) lock (_BroadcastListenSocketSynchroniser)
{ {
if (_BroadcastListenSocket != null) if (_BroadcastListenSockets != null)
{ {
_logger.LogInformation("{0} disposing _BroadcastListenSocket", GetType().Name); _logger.LogInformation("{0} disposing _BroadcastListenSocket", GetType().Name);
_BroadcastListenSocket.Dispose(); foreach (var socket in _BroadcastListenSockets)
_BroadcastListenSocket = null; {
socket.Dispose();
}
_BroadcastListenSockets = null;
} }
} }
} }
@ -336,12 +337,40 @@ namespace Rssdp.Infrastructure
return Task.CompletedTask; return Task.CompletedTask;
} }
private ISocket ListenForBroadcastsAsync() private List<ISocket> ListenForBroadcastsAsync()
{ {
var socket = _SocketFactory.CreateUdpMulticastSocket(IPAddress.Parse(SsdpConstants.MulticastLocalAdminAddress), _MulticastTtl, SsdpConstants.MulticastPort); var sockets = new List<ISocket>();
_ = ListenToSocketInternal(socket); if (_enableMultiSocketBinding)
{
foreach (var address in _networkManager.GetInternalBindAddresses())
{
if (address.AddressFamily == AddressFamily.InterNetworkV6)
{
// Not support IPv6 right now
continue;
}
return socket; try
{
sockets.Add(_SocketFactory.CreateUdpMulticastSocket(IPAddress.Parse(SsdpConstants.MulticastLocalAdminAddress), address.Address, _MulticastTtl, SsdpConstants.MulticastPort));
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in ListenForBroadcastsAsync. IPAddress: {0}", address);
}
}
}
else
{
sockets.Add(_SocketFactory.CreateUdpMulticastSocket(IPAddress.Parse(SsdpConstants.MulticastLocalAdminAddress), IPAddress.Any, _MulticastTtl, SsdpConstants.MulticastPort));
}
foreach (var socket in sockets)
{
_ = ListenToSocketInternal(socket);
}
return sockets;
} }
private List<ISocket> CreateSocketAndListenForResponsesAsync() private List<ISocket> CreateSocketAndListenForResponsesAsync()