diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 90c985a81..89119d31d 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -284,6 +284,7 @@ namespace Emby.Dlna.Main var udn = CreateUuid(_appHost.SystemId); var descriptorUri = "/dlna/" + udn + "/description.xml"; + // Only get bind addresses in LAN var bindAddresses = _networkManager .GetInternalBindAddresses() .Where(i => i.Address.AddressFamily == AddressFamily.InterNetwork @@ -304,12 +305,6 @@ namespace Emby.Dlna.Main continue; } - // Limit to LAN addresses only - if (!_networkManager.IsInLocalNetwork(address.Address)) - { - continue; - } - var fullService = "urn:schemas-upnp-org:device:MediaServer:1"; _logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, address); diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs index 21795c8f8..a1baf3006 100644 --- a/Emby.Server.Implementations/Net/SocketFactory.cs +++ b/Emby.Server.Implementations/Net/SocketFactory.cs @@ -61,13 +61,18 @@ namespace Emby.Server.Implementations.Net } /// - public ISocket CreateUdpMulticastSocket(IPAddress ipAddress, int multicastTimeToLive, int localPort) + public ISocket CreateUdpMulticastSocket(IPAddress ipAddress, IPAddress bindIpAddress, int multicastTimeToLive, int localPort) { if (ipAddress == null) { throw new ArgumentNullException(nameof(ipAddress)); } + if (bindIpAddress == null) + { + bindIpAddress = IPAddress.Any; + } + if (multicastTimeToLive <= 0) { 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.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive); - var localIp = IPAddress.Any; - - retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ipAddress, localIp)); + retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ipAddress, bindIpAddress)); retVal.MulticastLoopback = true; - return new UdpSocket(retVal, localPort, localIp); + return new UdpSocket(retVal, localPort, bindIpAddress); } catch { diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index af2429cc2..b1112626c 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -298,20 +298,22 @@ namespace Jellyfin.Networking.Manager 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."); if (IsIpv6Enabled) { - _lanSubnets.Add(new IPNetwork(IPAddress.Parse("fc00::"), 7)); // ULA - _lanSubnets.Add(new IPNetwork(IPAddress.Parse("fe80::"), 10)); // Site local + _lanSubnets.Add(new IPNetwork(IPAddress.IPv6Loopback, 128)); // RFC 4291 (Loopback) + _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) { - _lanSubnets.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8)); - _lanSubnets.Add(new IPNetwork(IPAddress.Parse("172.16.0.0"), 12)); - _lanSubnets.Add(new IPNetwork(IPAddress.Parse("192.168.0.0"), 16)); + _lanSubnets.Add(new IPNetwork(IPAddress.Loopback, 8)); // RFC 5735 (Loopback) + _lanSubnets.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8)); // RFC 1918 (private) + _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) { _interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetwork); } + // Remove all IPv6 interfaces if IPv6 is disabled if (!IsIpv6Enabled) { _interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetworkV6); diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs index a2835b711..e5cf7adbc 100644 --- a/MediaBrowser.Model/Net/ISocketFactory.cs +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -1,5 +1,6 @@ #pragma warning disable CS1591 +using System.Collections.Generic; using System.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. /// /// The multicast IP address to bind to. + /// The bind IP address. /// The multicast time to live value. Actually a maximum number of network hops for UDP packets. /// The local port to bind to. /// A implementation. - ISocket CreateUdpMulticastSocket(IPAddress ipAddress, int multicastTimeToLive, int localPort); + ISocket CreateUdpMulticastSocket(IPAddress ipAddress, IPAddress bindIpAddress, int multicastTimeToLive, int localPort); } } diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 53f872b60..e6c0a4403 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -33,7 +33,7 @@ namespace Rssdp.Infrastructure */ private object _BroadcastListenSocketSynchroniser = new object(); - private ISocket _BroadcastListenSocket; + private List _BroadcastListenSockets; private object _SendSocketSynchroniser = new object(); private List _sendSockets; @@ -111,24 +111,21 @@ namespace Rssdp.Infrastructure { ThrowIfDisposed(); - if (_BroadcastListenSocket == null) + lock (_BroadcastListenSocketSynchroniser) { - lock (_BroadcastListenSocketSynchroniser) + if (_BroadcastListenSockets == null) { - if (_BroadcastListenSocket == null) + try { - try - { - _BroadcastListenSocket = ListenForBroadcastsAsync(); - } - catch (SocketException ex) - { - _logger.LogError("Failed to bind to port 1900: {Message}. DLNA will be unavailable", ex.Message); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error in BeginListeningForBroadcasts"); - } + _BroadcastListenSockets = ListenForBroadcastsAsync(); + } + catch (SocketException ex) + { + _logger.LogError("Failed to bind to port 1900: {Message}. DLNA will be unavailable", ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error in BeginListeningForBroadcasts"); } } } @@ -142,11 +139,15 @@ namespace Rssdp.Infrastructure { lock (_BroadcastListenSocketSynchroniser) { - if (_BroadcastListenSocket != null) + if (_BroadcastListenSockets != null) { _logger.LogInformation("{0} disposing _BroadcastListenSocket", GetType().Name); - _BroadcastListenSocket.Dispose(); - _BroadcastListenSocket = null; + foreach (var socket in _BroadcastListenSockets) + { + socket.Dispose(); + } + + _BroadcastListenSockets = null; } } } @@ -336,12 +337,40 @@ namespace Rssdp.Infrastructure return Task.CompletedTask; } - private ISocket ListenForBroadcastsAsync() + private List ListenForBroadcastsAsync() { - var socket = _SocketFactory.CreateUdpMulticastSocket(IPAddress.Parse(SsdpConstants.MulticastLocalAdminAddress), _MulticastTtl, SsdpConstants.MulticastPort); - _ = ListenToSocketInternal(socket); + var sockets = new List(); + 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 CreateSocketAndListenForResponsesAsync()