mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-03 19:17:24 -05:00 
			
		
		
		
	Merge pull request #5905 from BaronGreenback/TVFix
Fix for Livetv and DLNA when bind interfaces specified.
This commit is contained in:
		
						commit
						c3523e7cf7
					
				@ -52,7 +52,6 @@ namespace Emby.Dlna.Main
 | 
			
		||||
        private readonly ISocketFactory _socketFactory;
 | 
			
		||||
        private readonly INetworkManager _networkManager;
 | 
			
		||||
        private readonly object _syncLock = new object();
 | 
			
		||||
        private readonly NetworkConfiguration _netConfig;
 | 
			
		||||
        private readonly bool _disabled;
 | 
			
		||||
 | 
			
		||||
        private PlayToManager _manager;
 | 
			
		||||
@ -125,8 +124,8 @@ namespace Emby.Dlna.Main
 | 
			
		||||
                config);
 | 
			
		||||
            Current = this;
 | 
			
		||||
 | 
			
		||||
            _netConfig = config.GetConfiguration<NetworkConfiguration>("network");
 | 
			
		||||
            _disabled = appHost.ListenWithHttps && _netConfig.RequireHttps;
 | 
			
		||||
            var netConfig = config.GetConfiguration<NetworkConfiguration>("network");
 | 
			
		||||
            _disabled = appHost.ListenWithHttps && netConfig.RequireHttps;
 | 
			
		||||
 | 
			
		||||
            if (_disabled && _config.GetDlnaConfiguration().EnableServer)
 | 
			
		||||
            {
 | 
			
		||||
@ -318,15 +317,9 @@ namespace Emby.Dlna.Main
 | 
			
		||||
 | 
			
		||||
                var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
 | 
			
		||||
 | 
			
		||||
                _logger.LogInformation("Registering publisher for {0} on {1}", fullService, address);
 | 
			
		||||
                _logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, address);
 | 
			
		||||
 | 
			
		||||
                var uri = new UriBuilder(_appHost.GetSmartApiUrl(address.Address) + descriptorUri);
 | 
			
		||||
                if (!string.IsNullOrEmpty(_appHost.PublishedServerUrl))
 | 
			
		||||
                {
 | 
			
		||||
                    // DLNA will only work over http, so we must reset to http:// : {port}.
 | 
			
		||||
                    uri.Scheme = "http";
 | 
			
		||||
                    uri.Port = _netConfig.HttpServerPortNumber;
 | 
			
		||||
                }
 | 
			
		||||
                var uri = new UriBuilder(_appHost.GetApiUrlForLocalAccess(false) + descriptorUri);
 | 
			
		||||
 | 
			
		||||
                var device = new SsdpRootDevice
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
@ -1123,12 +1123,6 @@ namespace Emby.Server.Implementations
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            string smart = NetManager.GetBindInterface(remoteAddr, out port);
 | 
			
		||||
            // If the smartAPI doesn't start with http then treat it as a host or ip.
 | 
			
		||||
            if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
                return smart.Trim('/');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return GetLocalApiUrl(smart.Trim('/'), null, port);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -1155,12 +1149,6 @@ namespace Emby.Server.Implementations
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            string smart = NetManager.GetBindInterface(request, out port);
 | 
			
		||||
            // If the smartAPI doesn't start with http then treat it as a host or ip.
 | 
			
		||||
            if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
                return smart.Trim('/');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return GetLocalApiUrl(smart.Trim('/'), request.Scheme, port);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -1175,30 +1163,28 @@ namespace Emby.Server.Implementations
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            string smart = NetManager.GetBindInterface(hostname, out port);
 | 
			
		||||
 | 
			
		||||
            // If the smartAPI doesn't start with http then treat it as a host or ip.
 | 
			
		||||
            if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
                return smart.Trim('/');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return GetLocalApiUrl(smart.Trim('/'), null, port);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public string GetLoopbackHttpApiUrl()
 | 
			
		||||
        public string GetApiUrlForLocalAccess(bool allowHttps)
 | 
			
		||||
        {
 | 
			
		||||
            if (NetManager.IsIP6Enabled)
 | 
			
		||||
            {
 | 
			
		||||
                return GetLocalApiUrl("::1", Uri.UriSchemeHttp, HttpPort);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort);
 | 
			
		||||
            // With an empty source, the port will be null
 | 
			
		||||
            string smart = NetManager.GetBindInterface(string.Empty, out _);
 | 
			
		||||
            var scheme = allowHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
 | 
			
		||||
            var port = allowHttps ? HttpsPort : HttpPort;
 | 
			
		||||
            return GetLocalApiUrl(smart.Trim('/'), scheme, port);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public string GetLocalApiUrl(string hostname, string scheme = null, int? port = null)
 | 
			
		||||
        {
 | 
			
		||||
            // If the smartAPI doesn't start with http then treat it as a host or ip.
 | 
			
		||||
            if (hostname.StartsWith("http", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
                return hostname.TrimEnd('/');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
 | 
			
		||||
            // not. For consistency, always trim the trailing slash.
 | 
			
		||||
            return new UriBuilder
 | 
			
		||||
 | 
			
		||||
@ -1027,7 +1027,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 | 
			
		||||
        {
 | 
			
		||||
            var stream = new MediaSourceInfo
 | 
			
		||||
            {
 | 
			
		||||
                EncoderPath = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
 | 
			
		||||
                EncoderPath = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
 | 
			
		||||
                EncoderProtocol = MediaProtocol.Http,
 | 
			
		||||
                Path = info.Path,
 | 
			
		||||
                Protocol = MediaProtocol.File,
 | 
			
		||||
 | 
			
		||||
@ -104,7 +104,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
			
		||||
                // Dummy this up so that direct play checks can still run
 | 
			
		||||
                if (string.IsNullOrEmpty(source.Path) && source.Protocol == MediaProtocol.Http)
 | 
			
		||||
                {
 | 
			
		||||
                    source.Path = _appHost.GetSmartApiUrl(string.Empty);
 | 
			
		||||
                    source.Path = _appHost.GetApiUrlForLocalAccess();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -147,7 +147,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
			
		||||
            // OpenedMediaSource.Path = tempFile;
 | 
			
		||||
            // OpenedMediaSource.ReadAtNativeFramerate = true;
 | 
			
		||||
 | 
			
		||||
            MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
 | 
			
		||||
            MediaSource.Path = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
 | 
			
		||||
            MediaSource.Protocol = MediaProtocol.Http;
 | 
			
		||||
            // OpenedMediaSource.SupportsDirectPlay = false;
 | 
			
		||||
            // OpenedMediaSource.SupportsDirectStream = true;
 | 
			
		||||
 | 
			
		||||
@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 | 
			
		||||
            // OpenedMediaSource.Path = tempFile;
 | 
			
		||||
            // OpenedMediaSource.ReadAtNativeFramerate = true;
 | 
			
		||||
 | 
			
		||||
            MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
 | 
			
		||||
            MediaSource.Path = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
 | 
			
		||||
            MediaSource.Protocol = MediaProtocol.Http;
 | 
			
		||||
 | 
			
		||||
            // OpenedMediaSource.Path = TempFilePath;
 | 
			
		||||
 | 
			
		||||
@ -455,10 +455,10 @@ namespace Jellyfin.Networking.Manager
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // No bind address, so return all internal interfaces.
 | 
			
		||||
                return CreateCollection(_internalInterfaces.Where(p => !p.IsLoopback()));
 | 
			
		||||
                return CreateCollection(_internalInterfaces);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new Collection<IPObject>(_bindAddresses);
 | 
			
		||||
            return new Collection<IPObject>(_bindAddresses.Where(a => IsInLocalNetwork(a)).ToArray());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
@ -481,7 +481,7 @@ namespace Jellyfin.Networking.Manager
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // As private addresses can be redefined by Configuration.LocalNetworkAddresses
 | 
			
		||||
            return _lanSubnets.ContainsAddress(address) && !_excludedSubnets.ContainsAddress(address);
 | 
			
		||||
            return address.IsLoopback() || (_lanSubnets.ContainsAddress(address) && !_excludedSubnets.ContainsAddress(address));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
@ -647,16 +647,6 @@ namespace Jellyfin.Networking.Manager
 | 
			
		||||
                    _interfaceAddresses.AddItem(address, false);
 | 
			
		||||
                    _interfaceNames[parts[2]] = Math.Abs(index);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (IsIP4Enabled)
 | 
			
		||||
                {
 | 
			
		||||
                    _interfaceAddresses.AddItem(IPNetAddress.IP4Loopback);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (IsIP6Enabled)
 | 
			
		||||
                {
 | 
			
		||||
                    _interfaceAddresses.AddItem(IPNetAddress.IP6Loopback);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            InitialiseLAN(config);
 | 
			
		||||
@ -1037,17 +1027,14 @@ namespace Jellyfin.Networking.Manager
 | 
			
		||||
                    // Subnets are the same as the calculated internal interface.
 | 
			
		||||
                    _lanSubnets = new Collection<IPObject>();
 | 
			
		||||
 | 
			
		||||
                    // We must listen on loopback for LiveTV to function regardless of the settings.
 | 
			
		||||
                    if (IsIP6Enabled)
 | 
			
		||||
                    {
 | 
			
		||||
                        _lanSubnets.AddItem(IPNetAddress.IP6Loopback);
 | 
			
		||||
                        _lanSubnets.AddItem(IPNetAddress.Parse("fc00::/7")); // ULA
 | 
			
		||||
                        _lanSubnets.AddItem(IPNetAddress.Parse("fe80::/10")); // Site local
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (IsIP4Enabled)
 | 
			
		||||
                    {
 | 
			
		||||
                        _lanSubnets.AddItem(IPNetAddress.IP4Loopback);
 | 
			
		||||
                        _lanSubnets.AddItem(IPNetAddress.Parse("10.0.0.0/8"));
 | 
			
		||||
                        _lanSubnets.AddItem(IPNetAddress.Parse("172.16.0.0/12"));
 | 
			
		||||
                        _lanSubnets.AddItem(IPNetAddress.Parse("192.168.0.0/16"));
 | 
			
		||||
@ -1055,17 +1042,6 @@ namespace Jellyfin.Networking.Manager
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    // We must listen on loopback for LiveTV to function regardless of the settings.
 | 
			
		||||
                    if (IsIP6Enabled)
 | 
			
		||||
                    {
 | 
			
		||||
                        _lanSubnets.AddItem(IPNetAddress.IP6Loopback);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (IsIP4Enabled)
 | 
			
		||||
                    {
 | 
			
		||||
                        _lanSubnets.AddItem(IPNetAddress.IP4Loopback);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Internal interfaces must be private, not excluded and part of the LocalNetworkSubnet.
 | 
			
		||||
                    _internalInterfaces = CreateCollection(_interfaceAddresses.Where(IsInLocalNetwork));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -81,11 +81,11 @@ namespace MediaBrowser.Controller
 | 
			
		||||
        string GetSmartApiUrl(string hostname, int? port = null);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets a localhost URL that can be used to access the API using the loop-back IP address.
 | 
			
		||||
        /// over HTTP (not HTTPS).
 | 
			
		||||
        /// Gets an URL that can be used to access the API over LAN.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="allowHttps">A value indicating whether to allow HTTPS.</param>
 | 
			
		||||
        /// <returns>The API URL.</returns>
 | 
			
		||||
        string GetLoopbackHttpApiUrl();
 | 
			
		||||
        string GetApiUrlForLocalAccess(bool allowHttps = true);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets a local (LAN) URL that can be used to access the API.
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ namespace Jellyfin.Networking.Tests
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Checks that thge given IP address is not in the network provided.
 | 
			
		||||
        /// Checks that the given IP address is not in the network provided.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="network">Network address(es).</param>
 | 
			
		||||
        /// <param name="value">The IP to check.</param>
 | 
			
		||||
 | 
			
		||||
@ -35,9 +35,9 @@ namespace Jellyfin.Networking.Tests
 | 
			
		||||
        // eth16 only
 | 
			
		||||
        [InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.208/24]")]
 | 
			
		||||
        // All interfaces excluded. (including loopbacks)
 | 
			
		||||
        [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[127.0.0.1/8,::1/128]")]
 | 
			
		||||
        [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[]")]
 | 
			
		||||
        // vEthernet1 and vEthernet212 should be excluded.
 | 
			
		||||
        [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24,127.0.0.1/8,::1/128]")]
 | 
			
		||||
        [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24]")]
 | 
			
		||||
        // Overlapping interface,
 | 
			
		||||
        [InlineData("192.168.1.110/24,-20,br0|192.168.1.10/24,-16,br0|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.110/24,192.168.1.10/24]")]
 | 
			
		||||
        public void IgnoreVirtualInterfaces(string interfaces, string lan, string value)
 | 
			
		||||
@ -476,5 +476,51 @@ namespace Jellyfin.Networking.Tests
 | 
			
		||||
 | 
			
		||||
            Assert.NotEqual(nm.HasRemoteAccess(IPAddress.Parse(remoteIp)), denied);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Theory]
 | 
			
		||||
        [InlineData("192.168.1.209/24,-16,eth16", "192.168.1.0/24", "", "192.168.1.209")] // Only 1 address so use it.
 | 
			
		||||
        [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "", "192.168.1.208")] // LAN address is specified by default.
 | 
			
		||||
        [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "10.0.0.1", "10.0.0.1")] // return bind address
 | 
			
		||||
 | 
			
		||||
        public void GetBindInterface_NoSourceGiven_Success(string interfaces, string lan, string bind, string result)
 | 
			
		||||
        {
 | 
			
		||||
            var conf = new NetworkConfiguration
 | 
			
		||||
            {
 | 
			
		||||
                EnableIPV4 = true,
 | 
			
		||||
                LocalNetworkSubnets = lan.Split(','),
 | 
			
		||||
                LocalNetworkAddresses = bind.Split(',')
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            NetworkManager.MockNetworkSettings = interfaces;
 | 
			
		||||
            using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
 | 
			
		||||
 | 
			
		||||
            var interfaceToUse = nm.GetBindInterface(string.Empty, out _);
 | 
			
		||||
 | 
			
		||||
            Assert.Equal(result, interfaceToUse);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Theory]
 | 
			
		||||
        [InlineData("192.168.1.209/24,-16,eth16", "192.168.1.0/24", "", "192.168.1.210", "192.168.1.209")] // Source on LAN
 | 
			
		||||
        [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "", "192.168.1.209", "192.168.1.208")] // Source on LAN
 | 
			
		||||
        [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "", "8.8.8.8", "10.0.0.1")] // Source external.
 | 
			
		||||
        [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "10.0.0.1", "192.168.1.209", "10.0.0.1")] // LAN not bound, so return external.
 | 
			
		||||
        [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "192.168.1.208,10.0.0.1", "8.8.8.8", "10.0.0.1")] // return external bind address
 | 
			
		||||
        [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "192.168.1.208,10.0.0.1", "192.168.1.210", "192.168.1.208")] // return LAN bind address
 | 
			
		||||
        public void GetBindInterface_ValidSourceGiven_Success(string interfaces, string lan, string bind, string source, string result)
 | 
			
		||||
        {
 | 
			
		||||
            var conf = new NetworkConfiguration
 | 
			
		||||
            {
 | 
			
		||||
                EnableIPV4 = true,
 | 
			
		||||
                LocalNetworkSubnets = lan.Split(','),
 | 
			
		||||
                LocalNetworkAddresses = bind.Split(',')
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            NetworkManager.MockNetworkSettings = interfaces;
 | 
			
		||||
            using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
 | 
			
		||||
 | 
			
		||||
            var interfaceToUse = nm.GetBindInterface(source, out _);
 | 
			
		||||
 | 
			
		||||
            Assert.Equal(result, interfaceToUse);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user