< Summary - Jellyfin

Information
Class: Jellyfin.Networking.Manager.NetworkManager
Assembly: Jellyfin.Networking
File(s): /srv/git/jellyfin/src/Jellyfin.Networking/Manager/NetworkManager.cs
Line coverage
74%
Covered lines: 365
Uncovered lines: 125
Coverable lines: 490
Total lines: 1209
Line coverage: 74.4%
Branch coverage
64%
Covered branches: 178
Total branches: 276
Branch coverage: 64.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 1/23/2026 - 12:11:06 AM Line coverage: 75.6% (361/477) Branch coverage: 66.9% (174/260) Total lines: 11773/28/2026 - 12:14:04 AM Line coverage: 75.4% (362/480) Branch coverage: 66.6% (176/264) Total lines: 11864/5/2026 - 12:14:16 AM Line coverage: 74.4% (365/490) Branch coverage: 64.4% (178/276) Total lines: 1209 1/23/2026 - 12:11:06 AM Line coverage: 75.6% (361/477) Branch coverage: 66.9% (174/260) Total lines: 11773/28/2026 - 12:14:04 AM Line coverage: 75.4% (362/480) Branch coverage: 66.6% (176/264) Total lines: 11864/5/2026 - 12:14:16 AM Line coverage: 74.4% (365/490) Branch coverage: 64.4% (178/276) Total lines: 1209

Coverage delta

Coverage delta 3 -3

Metrics

File(s)

/srv/git/jellyfin/src/Jellyfin.Networking/Manager/NetworkManager.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Diagnostics.CodeAnalysis;
 4using System.Globalization;
 5using System.Linq;
 6using System.Net;
 7using System.Net.NetworkInformation;
 8using System.Net.Sockets;
 9using System.Threading;
 10using J2N.Collections.Generic.Extensions;
 11using MediaBrowser.Common.Configuration;
 12using MediaBrowser.Common.Net;
 13using MediaBrowser.Model.Net;
 14using Microsoft.AspNetCore.Http;
 15using Microsoft.Extensions.Configuration;
 16using Microsoft.Extensions.Logging;
 17using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
 18using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager;
 19
 20namespace Jellyfin.Networking.Manager;
 21
 22/// <summary>
 23/// Class to take care of network interface management.
 24/// </summary>
 25public class NetworkManager : INetworkManager, IDisposable
 26{
 27    /// <summary>
 28    /// Threading lock for network properties.
 29    /// </summary>
 30    private readonly Lock _initLock;
 31
 32    private readonly ILogger<NetworkManager> _logger;
 33
 34    private readonly IConfigurationManager _configurationManager;
 35
 36    private readonly IConfiguration _startupConfig;
 37
 38    private readonly Lock _networkEventLock;
 39
 40    /// <summary>
 41    /// Holds the published server URLs and the IPs to use them on.
 42    /// </summary>
 43    private IReadOnlyList<PublishedServerUriOverride> _publishedServerUrls;
 44
 45    private IReadOnlyList<IPNetwork> _remoteAddressFilter;
 46
 47    /// <summary>
 48    /// Used to stop "event-racing conditions".
 49    /// </summary>
 50    private bool _eventfire;
 51
 52    /// <summary>
 53    /// Dictionary containing interface addresses and their subnets.
 54    /// </summary>
 55    private List<IPData> _interfaces;
 56
 57    /// <summary>
 58    /// Unfiltered user defined LAN subnets (<see cref="NetworkConfiguration.LocalNetworkSubnets"/>)
 59    /// or internal interface network subnets if undefined by user.
 60    /// </summary>
 61    private IReadOnlyList<IPNetwork> _lanSubnets;
 62
 63    /// <summary>
 64    /// User defined list of subnets to excluded from the LAN.
 65    /// </summary>
 66    private IReadOnlyList<IPNetwork> _excludedSubnets;
 67
 68    /// <summary>
 69    /// True if this object is disposed.
 70    /// </summary>
 71    private bool _disposed;
 72
 73    /// <summary>
 74    /// Initializes a new instance of the <see cref="NetworkManager"/> class.
 75    /// </summary>
 76    /// <param name="configurationManager">The <see cref="IConfigurationManager"/> instance.</param>
 77    /// <param name="startupConfig">The <see cref="IConfiguration"/> instance holding startup parameters.</param>
 78    /// <param name="logger">Logger to use for messages.</param>
 79    public NetworkManager(IConfigurationManager configurationManager, IConfiguration startupConfig, ILogger<NetworkManag
 80    {
 8081        ArgumentNullException.ThrowIfNull(logger);
 8082        ArgumentNullException.ThrowIfNull(configurationManager);
 83
 8084        _logger = logger;
 8085        _configurationManager = configurationManager;
 8086        _startupConfig = startupConfig;
 8087        _initLock = new();
 8088        _interfaces = new List<IPData>();
 8089        _publishedServerUrls = new List<PublishedServerUriOverride>();
 8090        _networkEventLock = new();
 8091        _remoteAddressFilter = new List<IPNetwork>();
 92
 8093        _ = bool.TryParse(startupConfig[DetectNetworkChangeKey], out var detectNetworkChange);
 94
 8095        UpdateSettings(_configurationManager.GetNetworkConfiguration());
 96
 8097        if (detectNetworkChange)
 98        {
 2199            NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;
 21100            NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
 101        }
 102
 80103        _configurationManager.NamedConfigurationUpdated += ConfigurationUpdated;
 80104    }
 105
 106    /// <summary>
 107    /// Event triggered on network changes.
 108    /// </summary>
 109    public event EventHandler? NetworkChanged;
 110
 111    /// <summary>
 112    /// Gets or sets a value indicating whether testing is taking place.
 113    /// </summary>
 3114    public static string MockNetworkSettings { get; set; } = string.Empty;
 115
 116    /// <summary>
 117    /// Gets a value indicating whether IPv4 is enabled.
 118    /// </summary>
 257119    public bool IsIPv4Enabled => _configurationManager.GetNetworkConfiguration().EnableIPv4;
 120
 121    /// <summary>
 122    /// Gets a value indicating whether IP6 is enabled.
 123    /// </summary>
 228124    public bool IsIPv6Enabled => _configurationManager.GetNetworkConfiguration().EnableIPv6;
 125
 126    /// <summary>
 127    /// Gets a value indicating whether is all IPv6 interfaces are trusted as internal.
 128    /// </summary>
 129    public bool TrustAllIPv6Interfaces { get; private set; }
 130
 131    /// <summary>
 132    /// Gets the Published server override list.
 133    /// </summary>
 0134    public IReadOnlyList<PublishedServerUriOverride> PublishedServerUrls => _publishedServerUrls;
 135
 136    /// <inheritdoc/>
 137    public void Dispose()
 138    {
 59139        Dispose(true);
 59140        GC.SuppressFinalize(this);
 59141    }
 142
 143    /// <summary>
 144    /// Handler for network change events.
 145    /// </summary>
 146    /// <param name="sender">Sender.</param>
 147    /// <param name="e">A <see cref="NetworkAvailabilityEventArgs"/> containing network availability information.</param
 148    private void OnNetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs e)
 149    {
 0150        _logger.LogDebug("Network availability changed.");
 0151        HandleNetworkChange();
 0152    }
 153
 154    /// <summary>
 155    /// Handler for network change events.
 156    /// </summary>
 157    /// <param name="sender">Sender.</param>
 158    /// <param name="e">An <see cref="EventArgs"/>.</param>
 159    private void OnNetworkAddressChanged(object? sender, EventArgs e)
 160    {
 0161        _logger.LogDebug("Network address change detected.");
 0162        HandleNetworkChange();
 0163    }
 164
 165    /// <summary>
 166    /// Triggers our event, and re-loads interface information.
 167    /// </summary>
 168    private void HandleNetworkChange()
 0169    {
 170        lock (_networkEventLock)
 171        {
 0172            if (!_eventfire)
 173            {
 174                // As network events tend to fire one after the other only fire once every second.
 0175                _eventfire = true;
 0176                OnNetworkChange();
 177            }
 0178        }
 0179    }
 180
 181    /// <summary>
 182    /// Waits for 2 seconds before re-initialising the settings, as typically these events fire multiple times in succes
 183    /// </summary>
 184    private void OnNetworkChange()
 185    {
 186        try
 187        {
 0188            Thread.Sleep(2000);
 0189            var networkConfig = _configurationManager.GetNetworkConfiguration();
 0190            if (IsIPv6Enabled && !Socket.OSSupportsIPv6)
 191            {
 0192                UpdateSettings(networkConfig);
 193            }
 194            else
 195            {
 0196                InitializeInterfaces();
 0197                InitializeLan(networkConfig);
 0198                EnforceBindSettings(networkConfig);
 199            }
 200
 0201            PrintNetworkInformation(networkConfig);
 0202            NetworkChanged?.Invoke(this, EventArgs.Empty);
 0203        }
 204        finally
 205        {
 0206            _eventfire = false;
 0207        }
 0208    }
 209
 210    /// <summary>
 211    /// Generate a list of all the interface ip addresses and submasks where that are in the active/unknown state.
 212    /// </summary>
 213    private void InitializeInterfaces()
 38214    {
 215        lock (_initLock)
 216        {
 38217            _interfaces = GetInterfacesCore(_logger, IsIPv4Enabled, IsIPv6Enabled).ToList();
 38218        }
 38219    }
 220
 221    /// <summary>
 222    /// Generate a list of all the interface ip addresses and submasks where that are in the active/unknown state.
 223    /// </summary>
 224    /// <param name="logger">The logger.</param>
 225    /// <param name="isIPv4Enabled">If true evaluates IPV4 type ip addresses.</param>
 226    /// <param name="isIPv6Enabled">If true evaluates IPV6 type ip addresses.</param>
 227    /// <returns>A list of all locally known up addresses and submasks that are to be considered usable.</returns>
 228    public static IReadOnlyList<IPData> GetInterfacesCore(ILogger logger, bool isIPv4Enabled, bool isIPv6Enabled)
 229    {
 38230        logger.LogDebug("Refreshing interfaces.");
 231
 38232        var interfaces = new List<IPData>();
 233
 234        try
 235        {
 38236            var nics = NetworkInterface.GetAllNetworkInterfaces()
 38237                .Where(i => i.OperationalStatus == OperationalStatus.Up);
 238
 228239            foreach (NetworkInterface adapter in nics)
 240            {
 241                try
 242                {
 76243                    var ipProperties = adapter.GetIPProperties();
 244
 245                    // Populate interface list
 456246                    foreach (var info in ipProperties.UnicastAddresses)
 247                    {
 152248                        if (isIPv4Enabled && info.Address.AddressFamily == AddressFamily.InterNetwork)
 249                        {
 76250                            var interfaceObject = new IPData(info.Address, new IPNetwork(info.Address, info.PrefixLength
 76251                            {
 76252                                Index = ipProperties.GetIPv4Properties().Index,
 76253                                Name = adapter.Name,
 76254                                SupportsMulticast = adapter.SupportsMulticast
 76255                            };
 256
 76257                            interfaces.Add(interfaceObject);
 258                        }
 76259                        else if (isIPv6Enabled && info.Address.AddressFamily == AddressFamily.InterNetworkV6)
 260                        {
 34261                            var interfaceObject = new IPData(info.Address, new IPNetwork(info.Address, info.PrefixLength
 34262                            {
 34263                                Index = ipProperties.GetIPv6Properties().Index,
 34264                                Name = adapter.Name,
 34265                                SupportsMulticast = adapter.SupportsMulticast
 34266                            };
 267
 34268                            interfaces.Add(interfaceObject);
 269                        }
 270                    }
 76271                }
 0272                catch (Exception ex)
 273                {
 274                    // Ignore error, and attempt to continue.
 0275                    logger.LogError(ex, "Error encountered parsing interfaces.");
 0276                }
 277            }
 38278        }
 0279        catch (Exception ex)
 280        {
 0281            logger.LogError(ex, "Error obtaining interfaces.");
 0282        }
 283
 284        // If no interfaces are found, fallback to loopback interfaces.
 38285        if (interfaces.Count == 0)
 286        {
 0287            logger.LogWarning("No interface information available. Using loopback interface(s).");
 288
 0289            if (isIPv4Enabled)
 290            {
 0291                interfaces.Add(new IPData(IPAddress.Loopback, NetworkConstants.IPv4RFC5735Loopback, "lo"));
 292            }
 293
 0294            if (isIPv6Enabled)
 295            {
 0296                interfaces.Add(new IPData(IPAddress.IPv6Loopback, NetworkConstants.IPv6RFC4291Loopback, "lo"));
 297            }
 298        }
 299
 38300        logger.LogDebug("Discovered {NumberOfInterfaces} interfaces.", interfaces.Count);
 38301        logger.LogDebug("Interfaces addresses: {Addresses}", interfaces.OrderByDescending(s => s.AddressFamily == Addres
 38302        return interfaces;
 303    }
 304
 305    /// <summary>
 306    /// Initializes internal LAN cache.
 307    /// </summary>
 308    [MemberNotNull(nameof(_lanSubnets), nameof(_excludedSubnets))]
 309    private void InitializeLan(NetworkConfiguration config)
 80310    {
 311        lock (_initLock)
 312        {
 80313            _logger.LogDebug("Refreshing LAN information.");
 314
 315            // Get configuration options
 80316            var subnets = config.LocalNetworkSubnets;
 317
 318            // If no LAN addresses are specified, all private subnets and Loopback are deemed to be the LAN
 80319            if (!NetworkUtils.TryParseToSubnets(subnets, out var lanSubnets, false) || lanSubnets.Count == 0)
 320            {
 45321                _logger.LogDebug("Using LAN interface addresses as user provided no LAN details.");
 322
 45323                var fallbackLanSubnets = new List<IPNetwork>();
 45324                if (IsIPv6Enabled)
 325                {
 7326                    fallbackLanSubnets.Add(NetworkConstants.IPv6RFC4291Loopback); // RFC 4291 (Loopback)
 7327                    fallbackLanSubnets.Add(NetworkConstants.IPv6RFC4291SiteLocal); // RFC 4291 (Site local)
 7328                    fallbackLanSubnets.Add(NetworkConstants.IPv6RFC4193UniqueLocal); // RFC 4193 (Unique local)
 329                }
 330
 45331                if (IsIPv4Enabled)
 332                {
 45333                    fallbackLanSubnets.Add(NetworkConstants.IPv4RFC5735Loopback); // RFC 5735 (Loopback)
 45334                    fallbackLanSubnets.Add(NetworkConstants.IPv4RFC1918PrivateClassA); // RFC 1918 (private Class A)
 45335                    fallbackLanSubnets.Add(NetworkConstants.IPv4RFC1918PrivateClassB); // RFC 1918 (private Class B)
 45336                    fallbackLanSubnets.Add(NetworkConstants.IPv4RFC1918PrivateClassC); // RFC 1918 (private Class C)
 337                }
 338
 45339                _lanSubnets = fallbackLanSubnets;
 340            }
 341            else
 342            {
 35343                _lanSubnets = lanSubnets.Select(x => x.Subnet).ToArray();
 344            }
 345
 80346            _excludedSubnets = NetworkUtils.TryParseToSubnets(subnets, out var excludedSubnets, true)
 80347                ? excludedSubnets.Select(x => x.Subnet).ToArray()
 80348                : Array.Empty<IPNetwork>();
 80349        }
 80350    }
 351
 352    /// <summary>
 353    /// Enforce bind addresses and exclusions on available interfaces.
 354    /// </summary>
 355    private void EnforceBindSettings(NetworkConfiguration config)
 80356    {
 357        lock (_initLock)
 358        {
 80359           _interfaces = FilterBindSettings(config, _interfaces, IsIPv4Enabled, IsIPv6Enabled).ToList();
 80360        }
 80361    }
 362
 363    /// <summary>
 364    /// Filters a list of bind addresses and exclusions on available interfaces.
 365    /// </summary>
 366    /// <param name="config">The network config to be filtered by.</param>
 367    /// <param name="interfaces">A list of possible interfaces to be filtered.</param>
 368    /// <param name="isIPv4Enabled">If true evaluates IPV4 type ip addresses.</param>
 369    /// <param name="isIPv6Enabled">If true evaluates IPV6 type ip addresses.</param>
 370    /// <returns>A list of all locally known up addresses and submasks that are to be considered usable.</returns>
 371    public static IReadOnlyList<IPData> FilterBindSettings(NetworkConfiguration config, IList<IPData> interfaces, bool i
 372    {
 373        // Respect explicit bind addresses
 80374        var localNetworkAddresses = config.LocalNetworkAddresses;
 80375        if (localNetworkAddresses.Length > 0 && !string.IsNullOrWhiteSpace(localNetworkAddresses[0]))
 376        {
 12377            var bindAddresses = localNetworkAddresses.Select(p => NetworkUtils.TryParseToSubnet(p, out var network)
 12378                    ? network.Address
 12379                    : (interfaces.Where(x => x.Name.Equals(p, StringComparison.OrdinalIgnoreCase))
 12380                        .Select(x => x.Address)
 12381                        .FirstOrDefault() ?? IPAddress.None))
 12382                .Where(x => x != IPAddress.None)
 12383                .ToHashSet();
 12384            interfaces = interfaces.Where(x => bindAddresses.Contains(x.Address)).ToList();
 385
 12386            if (bindAddresses.Contains(IPAddress.Loopback) && !interfaces.Any(i => i.Address.Equals(IPAddress.Loopback))
 387            {
 0388                interfaces.Add(new IPData(IPAddress.Loopback, NetworkConstants.IPv4RFC5735Loopback, "lo"));
 389            }
 390
 12391            if (bindAddresses.Contains(IPAddress.IPv6Loopback) && !interfaces.Any(i => i.Address.Equals(IPAddress.IPv6Lo
 392            {
 0393                interfaces.Add(new IPData(IPAddress.IPv6Loopback, NetworkConstants.IPv6RFC4291Loopback, "lo"));
 394            }
 395        }
 396
 397        // Remove all interfaces matching any virtual machine interface prefix
 80398        if (config.IgnoreVirtualInterfaces)
 399        {
 400            // Remove potentially existing * and split config string into prefixes
 80401            var virtualInterfacePrefixes = config.VirtualInterfaceNames
 80402                .Select(i => i.Replace("*", string.Empty, StringComparison.OrdinalIgnoreCase));
 403
 404            // Check all interfaces for matches against the prefixes and remove them
 80405            if (interfaces.Count > 0)
 406            {
 320407                foreach (var virtualInterfacePrefix in virtualInterfacePrefixes)
 408                {
 80409                    interfaces.RemoveAll(x => x.Name.StartsWith(virtualInterfacePrefix, StringComparison.OrdinalIgnoreCa
 410                }
 411            }
 412        }
 413
 414        // Remove all IPv4 interfaces if IPv4 is disabled
 80415        if (!isIPv4Enabled)
 416        {
 0417            interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetwork);
 418        }
 419
 420        // Remove all IPv6 interfaces if IPv6 is disabled
 80421        if (!isIPv6Enabled)
 422        {
 56423            interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetworkV6);
 424        }
 425
 426        // Users may have complex networking configuration that multiple interfaces sharing the same IP address
 427        // Only return one IP for binding, and let the OS handle the rest
 80428        return interfaces.DistinctBy(iface => iface.Address).ToList();
 429    }
 430
 431    /// <summary>
 432    /// Initializes the remote address values.
 433    /// </summary>
 434    private void InitializeRemote(NetworkConfiguration config)
 80435    {
 436        lock (_initLock)
 437        {
 438            // Parse config values into filter collection
 80439            var remoteIPFilter = config.RemoteIPFilter;
 80440            if (remoteIPFilter.Length != 0 && !string.IsNullOrWhiteSpace(remoteIPFilter[0]))
 441            {
 442                // Parse all IPs with netmask to a subnet
 6443                var remoteAddressFilter = new List<IPNetwork>();
 6444                var remoteFilteredSubnets = remoteIPFilter.Where(x => x.Contains('/', StringComparison.OrdinalIgnoreCase
 6445                if (NetworkUtils.TryParseToSubnets(remoteFilteredSubnets, out var remoteAddressFilterResult, false))
 446                {
 0447                    remoteAddressFilter = remoteAddressFilterResult.Select(x => x.Subnet).ToList();
 448                }
 449
 450                // Parse everything else as an IP and construct subnet with a single IP
 6451                var remoteFilteredIPs = remoteIPFilter.Where(x => !x.Contains('/', StringComparison.OrdinalIgnoreCase));
 28452                foreach (var ip in remoteFilteredIPs)
 453                {
 8454                    if (IPAddress.TryParse(ip, out var ipp))
 455                    {
 8456                        remoteAddressFilter.Add(new IPNetwork(ipp, ipp.AddressFamily == AddressFamily.InterNetwork ? Net
 457                    }
 458                }
 459
 6460                _remoteAddressFilter = remoteAddressFilter;
 461            }
 80462        }
 80463    }
 464
 465    /// <summary>
 466    /// Parses the user defined overrides into the dictionary object.
 467    /// Overrides are the equivalent of localised publishedServerUrl, enabling
 468    /// different addresses to be advertised over different subnets.
 469    /// format is subnet=ipaddress|host|uri
 470    /// when subnet = 0.0.0.0, any external address matches.
 471    /// </summary>
 472    private void InitializeOverrides(NetworkConfiguration config)
 80473    {
 474        lock (_initLock)
 475        {
 80476            var publishedServerUrls = new List<PublishedServerUriOverride>();
 477
 478            // Prefer startup configuration.
 80479            var startupOverrideKey = _startupConfig[AddressOverrideKey];
 80480            if (!string.IsNullOrEmpty(startupOverrideKey))
 481            {
 0482                publishedServerUrls.Add(
 0483                    new PublishedServerUriOverride(
 0484                        new IPData(IPAddress.Any, NetworkConstants.IPv4Any),
 0485                        startupOverrideKey,
 0486                        true,
 0487                        true));
 0488                publishedServerUrls.Add(
 0489                    new PublishedServerUriOverride(
 0490                        new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any),
 0491                        startupOverrideKey,
 0492                        true,
 0493                        true));
 0494                _publishedServerUrls = publishedServerUrls;
 0495                return;
 496            }
 497
 80498            var overrides = config.PublishedServerUriBySubnet;
 174499            foreach (var entry in overrides)
 500            {
 8501                var parts = entry.Split('=');
 8502                if (parts.Length != 2)
 503                {
 0504                    _logger.LogError("Unable to parse bind override: {Entry}", entry);
 0505                    return;
 506                }
 507
 8508                var replacement = parts[1].Trim();
 8509                var identifier = parts[0];
 8510                if (string.Equals(identifier, "all", StringComparison.OrdinalIgnoreCase))
 511                {
 512                    // Drop any other overrides in case an "all" override exists
 2513                    publishedServerUrls.Clear();
 2514                    publishedServerUrls.Add(
 2515                        new PublishedServerUriOverride(
 2516                            new IPData(IPAddress.Any, NetworkConstants.IPv4Any),
 2517                            replacement,
 2518                            true,
 2519                            true));
 2520                    publishedServerUrls.Add(
 2521                        new PublishedServerUriOverride(
 2522                            new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any),
 2523                            replacement,
 2524                            true,
 2525                            true));
 2526                    break;
 527                }
 6528                else if (string.Equals(identifier, "external", StringComparison.OrdinalIgnoreCase))
 529                {
 4530                    publishedServerUrls.Add(
 4531                        new PublishedServerUriOverride(
 4532                            new IPData(IPAddress.Any, NetworkConstants.IPv4Any),
 4533                            replacement,
 4534                            false,
 4535                            true));
 4536                    publishedServerUrls.Add(
 4537                        new PublishedServerUriOverride(
 4538                            new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any),
 4539                            replacement,
 4540                            false,
 4541                            true));
 542                }
 2543                else if (string.Equals(identifier, "internal", StringComparison.OrdinalIgnoreCase))
 544                {
 0545                    foreach (var lan in _lanSubnets)
 546                    {
 0547                        var lanPrefix = lan.BaseAddress;
 0548                        publishedServerUrls.Add(
 0549                            new PublishedServerUriOverride(
 0550                                new IPData(lanPrefix, new IPNetwork(lanPrefix, lan.PrefixLength)),
 0551                                replacement,
 0552                                true,
 0553                                false));
 554                    }
 555                }
 2556                else if (NetworkUtils.TryParseToSubnet(identifier, out var result))
 557                {
 1558                    publishedServerUrls.Add(
 1559                        new PublishedServerUriOverride(
 1560                            result,
 1561                            replacement,
 1562                            true,
 1563                            true));
 564                }
 1565                else if (TryParseInterface(identifier, out var ifaces))
 566                {
 4567                    foreach (var iface in ifaces)
 568                    {
 1569                        publishedServerUrls.Add(
 1570                            new PublishedServerUriOverride(
 1571                                iface,
 1572                                replacement,
 1573                                true,
 1574                                true));
 575                    }
 576                }
 577                else
 578                {
 0579                    _logger.LogError("Unable to parse bind override: {Entry}", entry);
 580                }
 581            }
 582
 80583            _publishedServerUrls = publishedServerUrls;
 80584        }
 80585    }
 586
 587    private void ConfigurationUpdated(object? sender, ConfigurationUpdateEventArgs evt)
 588    {
 22589        if (evt.Key.Equals(NetworkConfigurationStore.StoreKey, StringComparison.Ordinal))
 590        {
 0591            UpdateSettings((NetworkConfiguration)evt.NewConfiguration);
 592        }
 22593    }
 594
 595    /// <summary>
 596    /// Reloads all settings and re-Initializes the instance.
 597    /// </summary>
 598    /// <param name="configuration">The <see cref="NetworkConfiguration"/> to use.</param>
 599    [MemberNotNull(nameof(_lanSubnets), nameof(_excludedSubnets))]
 600    public void UpdateSettings(object configuration)
 601    {
 80602        ArgumentNullException.ThrowIfNull(configuration);
 603
 80604        var config = (NetworkConfiguration)configuration;
 80605        HappyEyeballs.HttpClientExtension.UseIPv6 = config.EnableIPv6;
 606
 80607        InitializeLan(config);
 80608        InitializeRemote(config);
 609
 80610        if (string.IsNullOrEmpty(MockNetworkSettings))
 611        {
 38612            InitializeInterfaces();
 613        }
 614        else // Used in testing only.
 615        {
 616            // Format is <IPAddress>,<Index>,<Name>: <next interface>. Set index to -ve to simulate a gateway.
 42617            var interfaceList = MockNetworkSettings.Split('|');
 42618            var interfaces = new List<IPData>();
 242619            foreach (var details in interfaceList)
 620            {
 79621                var parts = details.Split(',');
 79622                if (NetworkUtils.TryParseToSubnet(parts[0], out var data))
 623                {
 79624                    data.Index = int.Parse(parts[1], CultureInfo.InvariantCulture);
 79625                    if (data.AddressFamily == AddressFamily.InterNetwork || data.AddressFamily == AddressFamily.InterNet
 626                    {
 79627                        data.Name = parts[2];
 79628                        interfaces.Add(data);
 629                    }
 630                }
 631                else
 632                {
 0633                    _logger.LogWarning("Could not parse mock interface settings: {Part}", details);
 634                }
 635            }
 636
 42637            _interfaces = interfaces;
 638        }
 639
 80640        EnforceBindSettings(config);
 80641        InitializeOverrides(config);
 642
 80643        PrintNetworkInformation(config, false);
 80644    }
 645
 646    /// <summary>
 647    /// Protected implementation of Dispose pattern.
 648    /// </summary>
 649    /// <param name="disposing"><c>True</c> to dispose the managed state.</param>
 650    protected virtual void Dispose(bool disposing)
 651    {
 59652        if (!_disposed)
 653        {
 59654            if (disposing)
 655            {
 59656                _configurationManager.NamedConfigurationUpdated -= ConfigurationUpdated;
 59657                NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged;
 59658                NetworkChange.NetworkAvailabilityChanged -= OnNetworkAvailabilityChanged;
 659            }
 660
 59661            _disposed = true;
 662        }
 59663    }
 664
 665    /// <inheritdoc/>
 666    public bool TryParseInterface(string intf, [NotNullWhen(true)] out IReadOnlyList<IPData>? result)
 667    {
 15668        if (string.IsNullOrEmpty(intf)
 15669            || _interfaces is null
 15670            || _interfaces.Count == 0)
 671        {
 0672            result = null;
 0673            return false;
 674        }
 675
 676        // Match all interfaces starting with names starting with token
 15677        result = _interfaces
 15678            .Where(i => i.Name.Equals(intf, StringComparison.OrdinalIgnoreCase)
 15679                        && ((IsIPv4Enabled && i.Address.AddressFamily == AddressFamily.InterNetwork)
 15680                            || (IsIPv6Enabled && i.Address.AddressFamily == AddressFamily.InterNetworkV6)))
 15681            .OrderBy(x => x.Index)
 15682            .ToArray();
 15683        return result.Count > 0;
 684    }
 685
 686    /// <inheritdoc/>
 687    public RemoteAccessPolicyResult ShouldAllowServerAccess(IPAddress remoteIP)
 688    {
 9689        var config = _configurationManager.GetNetworkConfiguration();
 9690        if (IsInLocalNetwork(remoteIP))
 691        {
 1692            return RemoteAccessPolicyResult.Allow;
 693        }
 694
 8695        if (!config.EnableRemoteAccess)
 696        {
 697            // Remote not enabled. So everyone should be LAN.
 2698            return RemoteAccessPolicyResult.RejectDueToRemoteAccessDisabled;
 699        }
 700
 6701        if (!_remoteAddressFilter.Any())
 702        {
 703            // No filter on remote addresses, allow any of them.
 2704            return RemoteAccessPolicyResult.Allow;
 705        }
 706
 707        // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remot
 708        // If left blank, all remote addresses will be allowed.
 709
 710        // remoteAddressFilter is a whitelist or blacklist.
 4711        var anyMatches = _remoteAddressFilter.Any(remoteNetwork => NetworkUtils.SubnetContainsAddress(remoteNetwork, rem
 4712        if (config.IsRemoteIPFilterBlacklist)
 713        {
 2714            return anyMatches
 2715                ? RemoteAccessPolicyResult.RejectDueToIPBlocklist
 2716                : RemoteAccessPolicyResult.Allow;
 717        }
 718
 719        // Allow-list
 2720        return anyMatches
 2721            ? RemoteAccessPolicyResult.Allow
 2722            : RemoteAccessPolicyResult.RejectDueToNotAllowlistedRemoteIP;
 723    }
 724
 725    /// <inheritdoc/>
 726    public IReadOnlyList<IPData> GetLoopbacks()
 727    {
 0728        if (!IsIPv4Enabled && !IsIPv6Enabled)
 729        {
 0730            return Array.Empty<IPData>();
 731        }
 732
 0733        var loopbackNetworks = new List<IPData>();
 0734        if (IsIPv4Enabled)
 735        {
 0736            loopbackNetworks.Add(new IPData(IPAddress.Loopback, NetworkConstants.IPv4RFC5735Loopback, "lo"));
 737        }
 738
 0739        if (IsIPv6Enabled)
 740        {
 0741            loopbackNetworks.Add(new IPData(IPAddress.IPv6Loopback, NetworkConstants.IPv6RFC4291Loopback, "lo"));
 742        }
 743
 0744        return loopbackNetworks;
 745    }
 746
 747    /// <inheritdoc/>
 748    public IReadOnlyList<IPData> GetAllBindInterfaces(bool individualInterfaces = false)
 749    {
 21750        return NetworkManager.GetAllBindInterfaces(_logger, individualInterfaces, _configurationManager, _interfaces, Is
 751    }
 752
 753    /// <summary>
 754    /// Reads the jellyfin configuration of the configuration manager and produces a list of interfaces that should be b
 755    /// </summary>
 756    /// <param name="logger">Logger to use for messages.</param>
 757    /// <param name="individualInterfaces">Defines that only known interfaces should be used.</param>
 758    /// <param name="configurationManager">The ConfigurationManager.</param>
 759    /// <param name="knownInterfaces">The known interfaces that gets returned if possible or instructed.</param>
 760    /// <param name="readIpv4">Include IPV4 type interfaces.</param>
 761    /// <param name="readIpv6">Include IPV6 type interfaces.</param>
 762    /// <returns>A list of ip address of which jellyfin should bind to.</returns>
 763    public static IReadOnlyList<IPData> GetAllBindInterfaces(
 764        ILogger<NetworkManager> logger,
 765        bool individualInterfaces,
 766        IConfigurationManager configurationManager,
 767        IReadOnlyList<IPData> knownInterfaces,
 768        bool readIpv4,
 769        bool readIpv6)
 770    {
 21771        var config = configurationManager.GetNetworkConfiguration();
 21772        var localNetworkAddresses = config.LocalNetworkAddresses;
 21773        if ((localNetworkAddresses.Length > 0 && !string.IsNullOrWhiteSpace(localNetworkAddresses[0]) && knownInterfaces
 774        {
 0775            return knownInterfaces;
 776        }
 777
 778        // TODO: remove when upgrade to dotnet 11 is done
 21779        if (readIpv6 && !Socket.OSSupportsIPv6)
 780        {
 0781            logger.LogWarning("IPv6 Unsupported by OS, not listening on IPv6");
 0782            readIpv6 = false;
 783        }
 784
 785        // No bind address and no exclusions, so listen on all interfaces.
 21786        var result = new List<IPData>();
 21787        if (readIpv4 && readIpv6)
 788        {
 789            // Kestrel source code shows it uses Sockets.DualMode - so this also covers IPAddress.Any by default
 0790            result.Add(new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any));
 791        }
 21792        else if (readIpv4)
 793        {
 21794            result.Add(new IPData(IPAddress.Any, NetworkConstants.IPv4Any));
 795        }
 0796        else if (readIpv6)
 797        {
 798            // Cannot use IPv6Any as Kestrel will bind to IPv4 addresses too.
 0799            foreach (var iface in knownInterfaces)
 800            {
 0801                if (iface.AddressFamily == AddressFamily.InterNetworkV6)
 802                {
 0803                    result.Add(iface);
 804                }
 805            }
 806        }
 807
 21808        return result;
 809    }
 810
 811    /// <inheritdoc/>
 812    public string GetBindAddress(string source, out int? port)
 813    {
 24814        if (!NetworkUtils.TryParseHost(source, out var addresses, IsIPv4Enabled, IsIPv6Enabled))
 815        {
 4816            addresses = Array.Empty<IPAddress>();
 817        }
 818
 24819        var result = GetBindAddress(addresses.FirstOrDefault(), out port);
 24820        return result;
 821    }
 822
 823    /// <inheritdoc/>
 824    public string GetBindAddress(HttpRequest source, out int? port)
 825    {
 0826        var result = GetBindAddress(source.Host.Host, out port);
 0827        port ??= source.Host.Port;
 828
 0829        return result;
 830    }
 831
 832    /// <inheritdoc/>
 833    public string GetBindAddress(IPAddress? source, out int? port, bool skipOverrides = false)
 834    {
 24835        port = null;
 836
 837        string result;
 838
 24839        if (source is not null)
 840        {
 20841            if (IsIPv4Enabled && !IsIPv6Enabled && source.AddressFamily == AddressFamily.InterNetworkV6)
 842            {
 0843                _logger.LogWarning("IPv6 is disabled in Jellyfin, but enabled in the OS. This may affect how the interfa
 844            }
 845
 20846            if (!IsIPv4Enabled && IsIPv6Enabled && source.AddressFamily == AddressFamily.InterNetwork)
 847            {
 0848                _logger.LogWarning("IPv4 is disabled in Jellyfin, but enabled in the OS. This may affect how the interfa
 849            }
 850
 20851            bool isExternal = !IsInLocalNetwork(source);
 20852            _logger.LogDebug("Trying to get bind address for source {Source} - External: {IsExternal}", source, isExtern
 853
 20854            if (!skipOverrides && MatchesPublishedServerUrl(source, isExternal, out result))
 855            {
 6856                return result;
 857            }
 858
 859            // No preference given, so move on to bind addresses.
 14860            if (MatchesBindInterface(source, isExternal, out result))
 861            {
 11862                return result;
 863            }
 864
 3865            if (isExternal && MatchesExternalInterface(source, out result))
 866            {
 0867                return result;
 868            }
 869        }
 870
 871        // Get the first LAN interface address that's not excluded and not a loopback address.
 872        // Get all available interfaces, prefer local interfaces
 7873        var availableInterfaces = _interfaces.Where(x => !IPAddress.IsLoopback(x.Address))
 7874            .OrderByDescending(x => IsInLocalNetwork(x.Address))
 7875            .ThenBy(x => x.Index)
 7876            .ToList();
 877
 7878        if (availableInterfaces.Count == 0)
 879        {
 880            // There isn't any others, so we'll use the loopback.
 881            // Prefer loopback address matching the source's address family
 0882            if (source is not null && source.AddressFamily == AddressFamily.InterNetwork && IsIPv4Enabled)
 883            {
 0884                result = "127.0.0.1";
 885            }
 0886            else if (source is not null && source.AddressFamily == AddressFamily.InterNetworkV6 && IsIPv6Enabled)
 887            {
 0888                result = "::1";
 889            }
 890            else
 891            {
 0892                result = IsIPv4Enabled ? "127.0.0.1" : "::1";
 893            }
 894
 0895            _logger.LogWarning("{Source}: Only loopback {Result} returned, using that as bind address.", source, result)
 0896            return result;
 897        }
 898
 899        // If no source address is given, use the preferred (first) interface
 7900        if (source is null)
 901        {
 4902            result = NetworkUtils.FormatIPString(availableInterfaces.First().Address);
 4903            _logger.LogDebug("{Source}: Using first internal interface as bind address: {Result}", source, result);
 4904            return result;
 905        }
 906
 907        // Does the request originate in one of the interface subnets?
 908        // (For systems with multiple internal network cards, and multiple subnets)
 12909        foreach (var intf in availableInterfaces)
 910        {
 3911            if (NetworkUtils.SubnetContainsAddress(intf.Subnet, source))
 912            {
 0913                result = NetworkUtils.FormatIPString(intf.Address);
 0914                _logger.LogDebug("{Source}: Found interface with matching subnet, using it as bind address: {Result}", s
 0915                return result;
 916            }
 917        }
 918
 919        // Fallback to an interface matching the source's address family, or first available
 3920        var preferredInterface = availableInterfaces
 3921            .FirstOrDefault(x => x.Address.AddressFamily == source.AddressFamily);
 922
 3923        if (preferredInterface is not null)
 924        {
 3925            result = NetworkUtils.FormatIPString(preferredInterface.Address);
 3926            _logger.LogDebug("{Source}: No matching subnet found, using interface with matching address family: {Result}
 3927            return result;
 928        }
 929
 0930        result = NetworkUtils.FormatIPString(availableInterfaces[0].Address);
 0931        _logger.LogDebug("{Source}: No matching interfaces found, using first available interface as bind address: {Resu
 0932        return result;
 0933    }
 934
 935    /// <inheritdoc/>
 936    public IReadOnlyList<IPData> GetInternalBindAddresses()
 937    {
 938        // Select all local bind addresses
 6939        return _interfaces.Where(x => IsInLocalNetwork(x.Address))
 6940            .OrderBy(x => x.Index)
 6941            .ToList();
 942    }
 943
 944    /// <inheritdoc/>
 945    public bool IsInLocalNetwork(string address)
 946    {
 0947        if (NetworkUtils.TryParseToSubnet(address, out var subnet))
 948        {
 0949            return IsInLocalNetwork(subnet.Address);
 950        }
 951
 0952        return NetworkUtils.TryParseHost(address, out var addresses, IsIPv4Enabled, IsIPv6Enabled)
 0953               && addresses.Any(IsInLocalNetwork);
 954    }
 955
 956    /// <summary>
 957    ///  Get if the IPAddress is Link-local.
 958    /// </summary>
 959    /// <param name="address">The IP Address.</param>
 960    /// <returns>Bool indicates if the address is link-local.</returns>
 961    public bool IsLinkLocalAddress(IPAddress address)
 962    {
 4963        ArgumentNullException.ThrowIfNull(address);
 4964        return NetworkConstants.IPv4RFC3927LinkLocal.Contains(address) || address.IsIPv6LinkLocal;
 965    }
 966
 967    /// <inheritdoc/>
 968    public bool IsInLocalNetwork(IPAddress address)
 969    {
 160970        ArgumentNullException.ThrowIfNull(address);
 971
 972        // Map IPv6 mapped IPv4 back to IPv4 (happens if Kestrel runs in dual-socket mode)
 160973        if (address.IsIPv4MappedToIPv6)
 974        {
 0975            address = address.MapToIPv4();
 976        }
 977
 160978        if ((TrustAllIPv6Interfaces && address.AddressFamily == AddressFamily.InterNetworkV6)
 160979            || IPAddress.IsLoopback(address))
 980        {
 81981            return true;
 982        }
 983
 984        // As private addresses can be redefined by Configuration.LocalNetworkAddresses
 79985        return CheckIfLanAndNotExcluded(address);
 986    }
 987
 988    /// <summary>
 989    /// Check if the address is in the LAN and not excluded.
 990    /// </summary>
 991    /// <param name="address">The IP address to check. The caller should make sure this is not an IPv4MappedToIPv6 addre
 992    /// <returns>Boolean indicates whether the address is in LAN.</returns>
 993    private bool CheckIfLanAndNotExcluded(IPAddress address)
 994    {
 426995        foreach (var lanSubnet in _lanSubnets)
 996        {
 154997            if (lanSubnet.Contains(address))
 998            {
 86999                foreach (var excludedSubnet in _excludedSubnets)
 1000                {
 41001                    if (excludedSubnet.Contains(address))
 1002                    {
 21003                        return false;
 1004                    }
 1005                }
 1006
 381007                return true;
 1008            }
 1009        }
 1010
 391011        return false;
 401012    }
 1013
 1014    /// <summary>
 1015    /// Attempts to match the source against the published server URL overrides.
 1016    /// </summary>
 1017    /// <param name="source">IP source address to use.</param>
 1018    /// <param name="isInExternalSubnet">True if the source is in an external subnet.</param>
 1019    /// <param name="bindPreference">The published server URL that matches the source address.</param>
 1020    /// <returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
 1021    private bool MatchesPublishedServerUrl(IPAddress source, bool isInExternalSubnet, out string bindPreference)
 1022    {
 201023        bindPreference = string.Empty;
 201024        int? port = null;
 1025
 1026        // Only consider subnets including the source IP, preferring specific overrides
 1027        List<PublishedServerUriOverride> validPublishedServerUrls;
 201028        if (!isInExternalSubnet)
 1029        {
 1030            // Only use matching internal subnets
 1031            // Prefer more specific (bigger subnet prefix) overrides
 101032            validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsInternalOverride && NetworkUtils.SubnetContai
 101033                .OrderByDescending(x => x.Data.Subnet.PrefixLength)
 101034                .ToList();
 1035        }
 1036        else
 1037        {
 1038            // Only use matching external subnets
 1039            // Prefer more specific (bigger subnet prefix) overrides
 101040            validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsExternalOverride && NetworkUtils.SubnetContai
 101041                .OrderByDescending(x => x.Data.Subnet.PrefixLength)
 101042                .ToList();
 1043        }
 1044
 461045        foreach (var data in validPublishedServerUrls)
 1046        {
 1047            // Get interface matching override subnet
 61048            var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(x => NetworkUtils.SubnetContainsAddress(data.Dat
 1049
 61050            if (intf?.Address is not null
 61051                || (data.Data.AddressFamily == AddressFamily.InterNetwork && data.Data.Address.Equals(IPAddress.Any))
 61052                || (data.Data.AddressFamily == AddressFamily.InterNetworkV6 && data.Data.Address.Equals(IPAddress.IPv6An
 1053            {
 1054                // If matching interface is found, use override
 61055                bindPreference = data.OverrideUri;
 61056                break;
 1057            }
 1058        }
 1059
 201060        if (string.IsNullOrEmpty(bindPreference))
 1061        {
 141062            _logger.LogDebug("{Source}: No matching bind address override found", source);
 141063            return false;
 1064        }
 1065
 1066        // Handle override specifying port
 61067        var parts = bindPreference.Split(':');
 61068        if (parts.Length > 1)
 1069        {
 51070            if (int.TryParse(parts[1], out int p))
 1071            {
 01072                bindPreference = parts[0];
 01073                port = p;
 01074                _logger.LogDebug("{Source}: Matching bind address override found: {Address}:{Port}", source, bindPrefere
 01075                return true;
 1076            }
 1077        }
 1078
 61079        _logger.LogDebug("{Source}: Matching bind address override found: {Address}", source, bindPreference);
 1080
 61081        return true;
 1082    }
 1083
 1084    /// <summary>
 1085    /// Attempts to match the source against the user defined bind interfaces.
 1086    /// </summary>
 1087    /// <param name="source">IP source address to use.</param>
 1088    /// <param name="isInExternalSubnet">True if the source is in the external subnet.</param>
 1089    /// <param name="result">The result, if a match is found.</param>
 1090    /// <returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
 1091    private bool MatchesBindInterface(IPAddress source, bool isInExternalSubnet, out string result)
 1092    {
 141093        result = string.Empty;
 1094
 141095        int count = _interfaces.Count;
 141096        if (count == 1 && (_interfaces[0].Address.Equals(IPAddress.Any) || _interfaces[0].Address.Equals(IPAddress.IPv6A
 1097        {
 1098            // Ignore IPAny addresses.
 01099            count = 0;
 1100        }
 1101
 141102        if (count == 0)
 1103        {
 01104            return false;
 1105        }
 1106
 141107        IPAddress? bindAddress = null;
 141108        if (isInExternalSubnet)
 1109        {
 61110            var externalInterfaces = _interfaces.Where(x => !IsInLocalNetwork(x.Address))
 61111                .Where(x => !IsLinkLocalAddress(x.Address))
 61112                .OrderBy(x => x.Index)
 61113                .ToList();
 61114            if (externalInterfaces.Count > 0)
 1115            {
 1116                // Check to see if any of the external bind interfaces are in the same subnet as the source.
 1117                // If none exists, this will select the first external interface if there is one.
 41118                bindAddress = externalInterfaces
 41119                    .OrderByDescending(x => NetworkUtils.SubnetContainsAddress(x.Subnet, source))
 41120                    .ThenByDescending(x => x.Subnet.PrefixLength)
 41121                    .ThenBy(x => x.Index)
 41122                    .Select(x => x.Address)
 41123                    .First();
 1124
 41125                result = NetworkUtils.FormatIPString(bindAddress);
 41126                _logger.LogDebug("{Source}: External request received, matching external bind address found: {Result}", 
 41127                return true;
 1128            }
 1129
 21130            _logger.LogDebug("{Source}: External request received, no matching external bind address found, trying inter
 1131        }
 1132        else
 1133        {
 1134            // Check to see if any of the internal bind interfaces are in the same subnet as the source.
 1135            // If none exists, this will select the first internal interface if there is one.
 81136            bindAddress = _interfaces.Where(x => IsInLocalNetwork(x.Address))
 81137                .OrderByDescending(x => NetworkUtils.SubnetContainsAddress(x.Subnet, source))
 81138                .ThenByDescending(x => x.Subnet.PrefixLength)
 81139                .ThenBy(x => x.Index)
 81140                .Select(x => x.Address)
 81141                .FirstOrDefault();
 1142
 81143            if (bindAddress is not null)
 1144            {
 71145                result = NetworkUtils.FormatIPString(bindAddress);
 71146                _logger.LogDebug("{Source}: Internal request received, matching internal bind address found: {Result}", 
 71147                return true;
 1148            }
 1149        }
 1150
 31151        return false;
 1152    }
 1153
 1154    /// <summary>
 1155    /// Attempts to match the source against external interfaces.
 1156    /// </summary>
 1157    /// <param name="source">IP source address to use.</param>
 1158    /// <param name="result">The result, if a match is found.</param>
 1159    /// <returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
 1160    private bool MatchesExternalInterface(IPAddress source, out string result)
 1161    {
 1162        // Get the first external interface address that isn't a loopback.
 21163        var extResult = _interfaces
 21164            .Where(p => !IsInLocalNetwork(p.Address))
 21165            .Where(p => p.Address.AddressFamily.Equals(source.AddressFamily))
 21166            .Where(p => !IsLinkLocalAddress(p.Address))
 21167            .OrderBy(x => x.Index).ToArray();
 1168
 1169        // No external interface found
 21170        if (extResult.Length == 0)
 1171        {
 21172            result = string.Empty;
 21173            _logger.LogDebug("{Source}: External request received, but no external interface found. Need to route throug
 21174            return false;
 1175        }
 1176
 1177        // Does the request originate in one of the interface subnets?
 1178        // (For systems with multiple network cards and/or multiple subnets)
 01179        foreach (var intf in extResult)
 1180        {
 01181            if (NetworkUtils.SubnetContainsAddress(intf.Subnet, source))
 1182            {
 01183                result = NetworkUtils.FormatIPString(intf.Address);
 01184                _logger.LogDebug("{Source}: Found external interface with matching subnet, using it as bind address: {Re
 01185                return true;
 1186            }
 1187        }
 1188
 1189        // Fallback to first external interface.
 01190        result = NetworkUtils.FormatIPString(extResult[0].Address);
 01191        _logger.LogDebug("{Source}: Using first external interface as bind address: {Result}", source, result);
 01192        return true;
 1193    }
 1194
 1195    private void PrintNetworkInformation(NetworkConfiguration config, bool debug = true)
 1196    {
 801197        var logLevel = debug ? LogLevel.Debug : LogLevel.Information;
 801198        if (_logger.IsEnabled(logLevel))
 1199        {
 211200            _logger.Log(logLevel, "Defined LAN subnets: {Subnets}", _lanSubnets.Select(s => s.BaseAddress + "/" + s.Pref
 211201            _logger.Log(logLevel, "Defined LAN exclusions: {Subnets}", _excludedSubnets.Select(s => s.BaseAddress + "/" 
 211202            _logger.Log(logLevel, "Used LAN subnets: {Subnets}", _lanSubnets.Where(s => !_excludedSubnets.Contains(s)).S
 211203            _logger.Log(logLevel, "Filtered interface addresses: {Addresses}", _interfaces.OrderByDescending(x => x.Addr
 211204            _logger.Log(logLevel, "Bind Addresses {Addresses}", GetAllBindInterfaces(false).OrderByDescending(x => x.Add
 211205            _logger.Log(logLevel, "Remote IP filter is {Type}", config.IsRemoteIPFilterBlacklist ? "Blocklist" : "Allowl
 211206            _logger.Log(logLevel, "Filtered subnets: {Subnets}", _remoteAddressFilter.Select(s => s.BaseAddress + "/" + 
 1207        }
 801208    }
 1209}

Methods/Properties

.ctor(MediaBrowser.Common.Configuration.IConfigurationManager,Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger`1<Jellyfin.Networking.Manager.NetworkManager>)
.cctor()
get_IsIPv4Enabled()
get_IsIPv6Enabled()
get_PublishedServerUrls()
Dispose()
OnNetworkAvailabilityChanged(System.Object,System.Net.NetworkInformation.NetworkAvailabilityEventArgs)
OnNetworkAddressChanged(System.Object,System.EventArgs)
HandleNetworkChange()
OnNetworkChange()
InitializeInterfaces()
GetInterfacesCore(Microsoft.Extensions.Logging.ILogger,System.Boolean,System.Boolean)
InitializeLan(MediaBrowser.Common.Net.NetworkConfiguration)
EnforceBindSettings(MediaBrowser.Common.Net.NetworkConfiguration)
FilterBindSettings(MediaBrowser.Common.Net.NetworkConfiguration,System.Collections.Generic.IList`1<MediaBrowser.Model.Net.IPData>,System.Boolean,System.Boolean)
InitializeRemote(MediaBrowser.Common.Net.NetworkConfiguration)
InitializeOverrides(MediaBrowser.Common.Net.NetworkConfiguration)
ConfigurationUpdated(System.Object,MediaBrowser.Common.Configuration.ConfigurationUpdateEventArgs)
UpdateSettings(System.Object)
Dispose(System.Boolean)
TryParseInterface(System.String,System.Collections.Generic.IReadOnlyList`1<MediaBrowser.Model.Net.IPData>&)
ShouldAllowServerAccess(System.Net.IPAddress)
GetLoopbacks()
GetAllBindInterfaces(System.Boolean)
GetAllBindInterfaces(Microsoft.Extensions.Logging.ILogger`1<Jellyfin.Networking.Manager.NetworkManager>,System.Boolean,MediaBrowser.Common.Configuration.IConfigurationManager,System.Collections.Generic.IReadOnlyList`1<MediaBrowser.Model.Net.IPData>,System.Boolean,System.Boolean)
GetBindAddress(System.String,System.Nullable`1<System.Int32>&)
GetBindAddress(Microsoft.AspNetCore.Http.HttpRequest,System.Nullable`1<System.Int32>&)
GetBindAddress(System.Net.IPAddress,System.Nullable`1<System.Int32>&,System.Boolean)
GetInternalBindAddresses()
IsInLocalNetwork(System.String)
IsLinkLocalAddress(System.Net.IPAddress)
IsInLocalNetwork(System.Net.IPAddress)
CheckIfLanAndNotExcluded(System.Net.IPAddress)
MatchesPublishedServerUrl(System.Net.IPAddress,System.Boolean,System.String&)
MatchesBindInterface(System.Net.IPAddress,System.Boolean,System.String&)
MatchesExternalInterface(System.Net.IPAddress,System.String&)
PrintNetworkInformation(MediaBrowser.Common.Net.NetworkConfiguration,System.Boolean)