< Summary - Jellyfin

Information
Class: Jellyfin.Networking.Manager.NetworkManager
Assembly: Jellyfin.Networking
File(s): /srv/git/jellyfin/src/Jellyfin.Networking/Manager/NetworkManager.cs
Line coverage
75%
Covered lines: 360
Uncovered lines: 118
Coverable lines: 478
Total lines: 1174
Line coverage: 75.3%
Branch coverage
66%
Covered branches: 177
Total branches: 266
Branch coverage: 66.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

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;
 19using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
 20
 21namespace Jellyfin.Networking.Manager;
 22
 23/// <summary>
 24/// Class to take care of network interface management.
 25/// </summary>
 26public class NetworkManager : INetworkManager, IDisposable
 27{
 28    /// <summary>
 29    /// Threading lock for network properties.
 30    /// </summary>
 31    private readonly Lock _initLock;
 32
 33    private readonly ILogger<NetworkManager> _logger;
 34
 35    private readonly IConfigurationManager _configurationManager;
 36
 37    private readonly IConfiguration _startupConfig;
 38
 39    private readonly Lock _networkEventLock;
 40
 41    /// <summary>
 42    /// Holds the published server URLs and the IPs to use them on.
 43    /// </summary>
 44    private IReadOnlyList<PublishedServerUriOverride> _publishedServerUrls;
 45
 46    private IReadOnlyList<IPNetwork> _remoteAddressFilter;
 47
 48    /// <summary>
 49    /// Used to stop "event-racing conditions".
 50    /// </summary>
 51    private bool _eventfire;
 52
 53    /// <summary>
 54    /// Dictionary containing interface addresses and their subnets.
 55    /// </summary>
 56    private List<IPData> _interfaces;
 57
 58    /// <summary>
 59    /// Unfiltered user defined LAN subnets (<see cref="NetworkConfiguration.LocalNetworkSubnets"/>)
 60    /// or internal interface network subnets if undefined by user.
 61    /// </summary>
 62    private IReadOnlyList<IPNetwork> _lanSubnets;
 63
 64    /// <summary>
 65    /// User defined list of subnets to excluded from the LAN.
 66    /// </summary>
 67    private IReadOnlyList<IPNetwork> _excludedSubnets;
 68
 69    /// <summary>
 70    /// True if this object is disposed.
 71    /// </summary>
 72    private bool _disposed;
 73
 74    /// <summary>
 75    /// Initializes a new instance of the <see cref="NetworkManager"/> class.
 76    /// </summary>
 77    /// <param name="configurationManager">The <see cref="IConfigurationManager"/> instance.</param>
 78    /// <param name="startupConfig">The <see cref="IConfiguration"/> instance holding startup parameters.</param>
 79    /// <param name="logger">Logger to use for messages.</param>
 80    public NetworkManager(IConfigurationManager configurationManager, IConfiguration startupConfig, ILogger<NetworkManag
 81    {
 7682        ArgumentNullException.ThrowIfNull(logger);
 7683        ArgumentNullException.ThrowIfNull(configurationManager);
 84
 7685        _logger = logger;
 7686        _configurationManager = configurationManager;
 7687        _startupConfig = startupConfig;
 7688        _initLock = new();
 7689        _interfaces = new List<IPData>();
 7690        _publishedServerUrls = new List<PublishedServerUriOverride>();
 7691        _networkEventLock = new();
 7692        _remoteAddressFilter = new List<IPNetwork>();
 93
 7694        _ = bool.TryParse(startupConfig[DetectNetworkChangeKey], out var detectNetworkChange);
 95
 7696        UpdateSettings(_configurationManager.GetNetworkConfiguration());
 97
 7698        if (detectNetworkChange)
 99        {
 21100            NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;
 21101            NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
 102        }
 103
 76104        _configurationManager.NamedConfigurationUpdated += ConfigurationUpdated;
 76105    }
 106
 107    /// <summary>
 108    /// Event triggered on network changes.
 109    /// </summary>
 110    public event EventHandler? NetworkChanged;
 111
 112    /// <summary>
 113    /// Gets or sets a value indicating whether testing is taking place.
 114    /// </summary>
 3115    public static string MockNetworkSettings { get; set; } = string.Empty;
 116
 117    /// <summary>
 118    /// Gets a value indicating whether IP4 is enabled.
 119    /// </summary>
 243120    public bool IsIPv4Enabled => _configurationManager.GetNetworkConfiguration().EnableIPv4;
 121
 122    /// <summary>
 123    /// Gets a value indicating whether IP6 is enabled.
 124    /// </summary>
 215125    public bool IsIPv6Enabled => _configurationManager.GetNetworkConfiguration().EnableIPv6;
 126
 127    /// <summary>
 128    /// Gets a value indicating whether is all IPv6 interfaces are trusted as internal.
 129    /// </summary>
 130    public bool TrustAllIPv6Interfaces { get; private set; }
 131
 132    /// <summary>
 133    /// Gets the Published server override list.
 134    /// </summary>
 0135    public IReadOnlyList<PublishedServerUriOverride> PublishedServerUrls => _publishedServerUrls;
 136
 137    /// <inheritdoc/>
 138    public void Dispose()
 139    {
 55140        Dispose(true);
 55141        GC.SuppressFinalize(this);
 55142    }
 143
 144    /// <summary>
 145    /// Handler for network change events.
 146    /// </summary>
 147    /// <param name="sender">Sender.</param>
 148    /// <param name="e">A <see cref="NetworkAvailabilityEventArgs"/> containing network availability information.</param
 149    private void OnNetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs e)
 150    {
 0151        _logger.LogDebug("Network availability changed.");
 0152        HandleNetworkChange();
 0153    }
 154
 155    /// <summary>
 156    /// Handler for network change events.
 157    /// </summary>
 158    /// <param name="sender">Sender.</param>
 159    /// <param name="e">An <see cref="EventArgs"/>.</param>
 160    private void OnNetworkAddressChanged(object? sender, EventArgs e)
 161    {
 0162        _logger.LogDebug("Network address change detected.");
 0163        HandleNetworkChange();
 0164    }
 165
 166    /// <summary>
 167    /// Triggers our event, and re-loads interface information.
 168    /// </summary>
 169    private void HandleNetworkChange()
 0170    {
 171        lock (_networkEventLock)
 172        {
 0173            if (!_eventfire)
 174            {
 175                // As network events tend to fire one after the other only fire once every second.
 0176                _eventfire = true;
 0177                OnNetworkChange();
 178            }
 0179        }
 0180    }
 181
 182    /// <summary>
 183    /// Waits for 2 seconds before re-initialising the settings, as typically these events fire multiple times in succes
 184    /// </summary>
 185    private void OnNetworkChange()
 186    {
 187        try
 188        {
 0189            Thread.Sleep(2000);
 0190            var networkConfig = _configurationManager.GetNetworkConfiguration();
 0191            if (IsIPv6Enabled && !Socket.OSSupportsIPv6)
 192            {
 0193                UpdateSettings(networkConfig);
 194            }
 195            else
 196            {
 0197                InitializeInterfaces();
 0198                InitializeLan(networkConfig);
 0199                EnforceBindSettings(networkConfig);
 200            }
 201
 0202            PrintNetworkInformation(networkConfig);
 0203            NetworkChanged?.Invoke(this, EventArgs.Empty);
 0204        }
 205        finally
 206        {
 0207            _eventfire = false;
 0208        }
 0209    }
 210
 211    /// <summary>
 212    /// Generate a list of all the interface ip addresses and submasks where that are in the active/unknown state.
 213    /// </summary>
 214    private void InitializeInterfaces()
 34215    {
 216        lock (_initLock)
 217        {
 34218            _interfaces = GetInterfacesCore(_logger, IsIPv4Enabled, IsIPv6Enabled).ToList();
 34219        }
 34220    }
 221
 222    /// <summary>
 223    /// Generate a list of all the interface ip addresses and submasks where that are in the active/unknown state.
 224    /// </summary>
 225    /// <param name="logger">The logger.</param>
 226    /// <param name="isIPv4Enabled">If true evaluates IPV4 type ip addresses.</param>
 227    /// <param name="isIPv6Enabled">If true evaluates IPV6 type ip addresses.</param>
 228    /// <returns>A list of all locally known up addresses and submasks that are to be considered usable.</returns>
 229    public static IReadOnlyList<IPData> GetInterfacesCore(ILogger logger, bool isIPv4Enabled, bool isIPv6Enabled)
 230    {
 34231        logger.LogDebug("Refreshing interfaces.");
 232
 34233        var interfaces = new List<IPData>();
 234
 235        try
 236        {
 34237            var nics = NetworkInterface.GetAllNetworkInterfaces()
 34238                .Where(i => i.OperationalStatus == OperationalStatus.Up);
 239
 204240            foreach (NetworkInterface adapter in nics)
 241            {
 242                try
 243                {
 68244                    var ipProperties = adapter.GetIPProperties();
 245
 246                    // Populate interface list
 408247                    foreach (var info in ipProperties.UnicastAddresses)
 248                    {
 136249                        if (isIPv4Enabled && info.Address.AddressFamily == AddressFamily.InterNetwork)
 250                        {
 68251                            var interfaceObject = new IPData(info.Address, new IPNetwork(info.Address, info.PrefixLength
 68252                            {
 68253                                Index = ipProperties.GetIPv4Properties().Index,
 68254                                Name = adapter.Name,
 68255                                SupportsMulticast = adapter.SupportsMulticast
 68256                            };
 257
 68258                            interfaces.Add(interfaceObject);
 259                        }
 68260                        else if (isIPv6Enabled && info.Address.AddressFamily == AddressFamily.InterNetworkV6)
 261                        {
 14262                            var interfaceObject = new IPData(info.Address, new IPNetwork(info.Address, info.PrefixLength
 14263                            {
 14264                                Index = ipProperties.GetIPv6Properties().Index,
 14265                                Name = adapter.Name,
 14266                                SupportsMulticast = adapter.SupportsMulticast
 14267                            };
 268
 14269                            interfaces.Add(interfaceObject);
 270                        }
 271                    }
 68272                }
 0273                catch (Exception ex)
 274                {
 275                    // Ignore error, and attempt to continue.
 0276                    logger.LogError(ex, "Error encountered parsing interfaces.");
 0277                }
 278            }
 34279        }
 0280        catch (Exception ex)
 281        {
 0282            logger.LogError(ex, "Error obtaining interfaces.");
 0283        }
 284
 285        // If no interfaces are found, fallback to loopback interfaces.
 34286        if (interfaces.Count == 0)
 287        {
 0288            logger.LogWarning("No interface information available. Using loopback interface(s).");
 289
 0290            if (isIPv4Enabled)
 291            {
 0292                interfaces.Add(new IPData(IPAddress.Loopback, NetworkConstants.IPv4RFC5735Loopback, "lo"));
 293            }
 294
 0295            if (isIPv6Enabled)
 296            {
 0297                interfaces.Add(new IPData(IPAddress.IPv6Loopback, NetworkConstants.IPv6RFC4291Loopback, "lo"));
 298            }
 299        }
 300
 34301        logger.LogDebug("Discovered {NumberOfInterfaces} interfaces.", interfaces.Count);
 34302        logger.LogDebug("Interfaces addresses: {Addresses}", interfaces.OrderByDescending(s => s.AddressFamily == Addres
 34303        return interfaces;
 304    }
 305
 306    /// <summary>
 307    /// Initializes internal LAN cache.
 308    /// </summary>
 309    [MemberNotNull(nameof(_lanSubnets), nameof(_excludedSubnets))]
 310    private void InitializeLan(NetworkConfiguration config)
 76311    {
 312        lock (_initLock)
 313        {
 76314            _logger.LogDebug("Refreshing LAN information.");
 315
 316            // Get configuration options
 76317            var subnets = config.LocalNetworkSubnets;
 318
 319            // If no LAN addresses are specified, all private subnets and Loopback are deemed to be the LAN
 76320            if (!NetworkUtils.TryParseToSubnets(subnets, out var lanSubnets, false) || lanSubnets.Count == 0)
 321            {
 42322                _logger.LogDebug("Using LAN interface addresses as user provided no LAN details.");
 323
 42324                var fallbackLanSubnets = new List<IPNetwork>();
 42325                if (IsIPv6Enabled)
 326                {
 7327                    fallbackLanSubnets.Add(NetworkConstants.IPv6RFC4291Loopback); // RFC 4291 (Loopback)
 7328                    fallbackLanSubnets.Add(NetworkConstants.IPv6RFC4291SiteLocal); // RFC 4291 (Site local)
 7329                    fallbackLanSubnets.Add(NetworkConstants.IPv6RFC4193UniqueLocal); // RFC 4193 (Unique local)
 330                }
 331
 42332                if (IsIPv4Enabled)
 333                {
 42334                    fallbackLanSubnets.Add(NetworkConstants.IPv4RFC5735Loopback); // RFC 5735 (Loopback)
 42335                    fallbackLanSubnets.Add(NetworkConstants.IPv4RFC1918PrivateClassA); // RFC 1918 (private Class A)
 42336                    fallbackLanSubnets.Add(NetworkConstants.IPv4RFC1918PrivateClassB); // RFC 1918 (private Class B)
 42337                    fallbackLanSubnets.Add(NetworkConstants.IPv4RFC1918PrivateClassC); // RFC 1918 (private Class C)
 338                }
 339
 42340                _lanSubnets = fallbackLanSubnets;
 341            }
 342            else
 343            {
 34344                _lanSubnets = lanSubnets;
 345            }
 346
 76347            _excludedSubnets = NetworkUtils.TryParseToSubnets(subnets, out var excludedSubnets, true)
 76348                ? excludedSubnets
 76349                : new List<IPNetwork>();
 76350        }
 76351    }
 352
 353    /// <summary>
 354    /// Enforce bind addresses and exclusions on available interfaces.
 355    /// </summary>
 356    private void EnforceBindSettings(NetworkConfiguration config)
 76357    {
 358        lock (_initLock)
 359        {
 76360           _interfaces = FilterBindSettings(config, _interfaces, IsIPv4Enabled, IsIPv6Enabled).ToList();
 76361        }
 76362    }
 363
 364    /// <summary>
 365    /// Filteres a list of bind addresses and exclusions on available interfaces.
 366    /// </summary>
 367    /// <param name="config">The network config to be filtered by.</param>
 368    /// <param name="interfaces">A list of possible interfaces to be filtered.</param>
 369    /// <param name="isIPv4Enabled">If true evaluates IPV4 type ip addresses.</param>
 370    /// <param name="isIPv6Enabled">If true evaluates IPV6 type ip addresses.</param>
 371    /// <returns>A list of all locally known up addresses and submasks that are to be considered usable.</returns>
 372    public static IReadOnlyList<IPData> FilterBindSettings(NetworkConfiguration config, IList<IPData> interfaces, bool i
 373    {
 374        // Respect explicit bind addresses
 76375        var localNetworkAddresses = config.LocalNetworkAddresses;
 76376        if (localNetworkAddresses.Length > 0 && !string.IsNullOrWhiteSpace(localNetworkAddresses[0]))
 377        {
 12378            var bindAddresses = localNetworkAddresses.Select(p => NetworkUtils.TryParseToSubnet(p, out var network)
 12379                    ? network.Prefix
 12380                    : (interfaces.Where(x => x.Name.Equals(p, StringComparison.OrdinalIgnoreCase))
 12381                        .Select(x => x.Address)
 12382                        .FirstOrDefault() ?? IPAddress.None))
 12383                .Where(x => x != IPAddress.None)
 12384                .ToHashSet();
 12385            interfaces = interfaces.Where(x => bindAddresses.Contains(x.Address)).ToList();
 386
 12387            if (bindAddresses.Contains(IPAddress.Loopback) && !interfaces.Any(i => i.Address.Equals(IPAddress.Loopback))
 388            {
 0389                interfaces.Add(new IPData(IPAddress.Loopback, NetworkConstants.IPv4RFC5735Loopback, "lo"));
 390            }
 391
 12392            if (bindAddresses.Contains(IPAddress.IPv6Loopback) && !interfaces.Any(i => i.Address.Equals(IPAddress.IPv6Lo
 393            {
 0394                interfaces.Add(new IPData(IPAddress.IPv6Loopback, NetworkConstants.IPv6RFC4291Loopback, "lo"));
 395            }
 396        }
 397
 398        // Remove all interfaces matching any virtual machine interface prefix
 76399        if (config.IgnoreVirtualInterfaces)
 400        {
 401            // Remove potentially existing * and split config string into prefixes
 76402            var virtualInterfacePrefixes = config.VirtualInterfaceNames
 76403                .Select(i => i.Replace("*", string.Empty, StringComparison.OrdinalIgnoreCase));
 404
 405            // Check all interfaces for matches against the prefixes and remove them
 76406            if (interfaces.Count > 0)
 407            {
 304408                foreach (var virtualInterfacePrefix in virtualInterfacePrefixes)
 409                {
 76410                    interfaces.RemoveAll(x => x.Name.StartsWith(virtualInterfacePrefix, StringComparison.OrdinalIgnoreCa
 411                }
 412            }
 413        }
 414
 415        // Remove all IPv4 interfaces if IPv4 is disabled
 76416        if (!isIPv4Enabled)
 417        {
 0418            interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetwork);
 419        }
 420
 421        // Remove all IPv6 interfaces if IPv6 is disabled
 76422        if (!isIPv6Enabled)
 423        {
 52424            interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetworkV6);
 425        }
 426
 427        // Users may have complex networking configuration that multiple interfaces sharing the same IP address
 428        // Only return one IP for binding, and let the OS handle the rest
 76429        return interfaces.DistinctBy(iface => iface.Address).ToList();
 430    }
 431
 432    /// <summary>
 433    /// Initializes the remote address values.
 434    /// </summary>
 435    private void InitializeRemote(NetworkConfiguration config)
 76436    {
 437        lock (_initLock)
 438        {
 439            // Parse config values into filter collection
 76440            var remoteIPFilter = config.RemoteIPFilter;
 76441            if (remoteIPFilter.Length != 0 && !string.IsNullOrWhiteSpace(remoteIPFilter[0]))
 442            {
 443                // Parse all IPs with netmask to a subnet
 4444                var remoteAddressFilter = new List<IPNetwork>();
 4445                var remoteFilteredSubnets = remoteIPFilter.Where(x => x.Contains('/', StringComparison.OrdinalIgnoreCase
 4446                if (NetworkUtils.TryParseToSubnets(remoteFilteredSubnets, out var remoteAddressFilterResult, false))
 447                {
 0448                    remoteAddressFilter = remoteAddressFilterResult.ToList();
 449                }
 450
 451                // Parse everything else as an IP and construct subnet with a single IP
 4452                var remoteFilteredIPs = remoteIPFilter.Where(x => !x.Contains('/', StringComparison.OrdinalIgnoreCase));
 18453                foreach (var ip in remoteFilteredIPs)
 454                {
 5455                    if (IPAddress.TryParse(ip, out var ipp))
 456                    {
 5457                        remoteAddressFilter.Add(new IPNetwork(ipp, ipp.AddressFamily == AddressFamily.InterNetwork ? Net
 458                    }
 459                }
 460
 4461                _remoteAddressFilter = remoteAddressFilter;
 462            }
 76463        }
 76464    }
 465
 466    /// <summary>
 467    /// Parses the user defined overrides into the dictionary object.
 468    /// Overrides are the equivalent of localised publishedServerUrl, enabling
 469    /// different addresses to be advertised over different subnets.
 470    /// format is subnet=ipaddress|host|uri
 471    /// when subnet = 0.0.0.0, any external address matches.
 472    /// </summary>
 473    private void InitializeOverrides(NetworkConfiguration config)
 76474    {
 475        lock (_initLock)
 476        {
 76477            var publishedServerUrls = new List<PublishedServerUriOverride>();
 478
 479            // Prefer startup configuration.
 76480            var startupOverrideKey = _startupConfig[AddressOverrideKey];
 76481            if (!string.IsNullOrEmpty(startupOverrideKey))
 482            {
 0483                publishedServerUrls.Add(
 0484                    new PublishedServerUriOverride(
 0485                        new IPData(IPAddress.Any, NetworkConstants.IPv4Any),
 0486                        startupOverrideKey,
 0487                        true,
 0488                        true));
 0489                publishedServerUrls.Add(
 0490                    new PublishedServerUriOverride(
 0491                        new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any),
 0492                        startupOverrideKey,
 0493                        true,
 0494                        true));
 0495                _publishedServerUrls = publishedServerUrls;
 0496                return;
 497            }
 498
 76499            var overrides = config.PublishedServerUriBySubnet;
 166500            foreach (var entry in overrides)
 501            {
 8502                var parts = entry.Split('=');
 8503                if (parts.Length != 2)
 504                {
 0505                    _logger.LogError("Unable to parse bind override: {Entry}", entry);
 0506                    return;
 507                }
 508
 8509                var replacement = parts[1].Trim();
 8510                var identifier = parts[0];
 8511                if (string.Equals(identifier, "all", StringComparison.OrdinalIgnoreCase))
 512                {
 513                    // Drop any other overrides in case an "all" override exists
 2514                    publishedServerUrls.Clear();
 2515                    publishedServerUrls.Add(
 2516                        new PublishedServerUriOverride(
 2517                            new IPData(IPAddress.Any, NetworkConstants.IPv4Any),
 2518                            replacement,
 2519                            true,
 2520                            true));
 2521                    publishedServerUrls.Add(
 2522                        new PublishedServerUriOverride(
 2523                            new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any),
 2524                            replacement,
 2525                            true,
 2526                            true));
 2527                    break;
 528                }
 6529                else if (string.Equals(identifier, "external", StringComparison.OrdinalIgnoreCase))
 530                {
 4531                    publishedServerUrls.Add(
 4532                        new PublishedServerUriOverride(
 4533                            new IPData(IPAddress.Any, NetworkConstants.IPv4Any),
 4534                            replacement,
 4535                            false,
 4536                            true));
 4537                    publishedServerUrls.Add(
 4538                        new PublishedServerUriOverride(
 4539                            new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any),
 4540                            replacement,
 4541                            false,
 4542                            true));
 543                }
 2544                else if (string.Equals(identifier, "internal", StringComparison.OrdinalIgnoreCase))
 545                {
 0546                    foreach (var lan in _lanSubnets)
 547                    {
 0548                        var lanPrefix = lan.Prefix;
 0549                        publishedServerUrls.Add(
 0550                            new PublishedServerUriOverride(
 0551                                new IPData(lanPrefix, new IPNetwork(lanPrefix, lan.PrefixLength)),
 0552                                replacement,
 0553                                true,
 0554                                false));
 555                    }
 556                }
 2557                else if (NetworkUtils.TryParseToSubnet(identifier, out var result) && result is not null)
 558                {
 1559                    var data = new IPData(result.Prefix, result);
 1560                    publishedServerUrls.Add(
 1561                        new PublishedServerUriOverride(
 1562                            data,
 1563                            replacement,
 1564                            true,
 1565                            true));
 566                }
 1567                else if (TryParseInterface(identifier, out var ifaces))
 568                {
 4569                    foreach (var iface in ifaces)
 570                    {
 1571                        publishedServerUrls.Add(
 1572                            new PublishedServerUriOverride(
 1573                                iface,
 1574                                replacement,
 1575                                true,
 1576                                true));
 577                    }
 578                }
 579                else
 580                {
 0581                    _logger.LogError("Unable to parse bind override: {Entry}", entry);
 582                }
 583            }
 584
 76585            _publishedServerUrls = publishedServerUrls;
 76586        }
 76587    }
 588
 589    private void ConfigurationUpdated(object? sender, ConfigurationUpdateEventArgs evt)
 590    {
 22591        if (evt.Key.Equals(NetworkConfigurationStore.StoreKey, StringComparison.Ordinal))
 592        {
 0593            UpdateSettings((NetworkConfiguration)evt.NewConfiguration);
 594        }
 22595    }
 596
 597    /// <summary>
 598    /// Reloads all settings and re-Initializes the instance.
 599    /// </summary>
 600    /// <param name="configuration">The <see cref="NetworkConfiguration"/> to use.</param>
 601    [MemberNotNull(nameof(_lanSubnets), nameof(_excludedSubnets))]
 602    public void UpdateSettings(object configuration)
 603    {
 76604        ArgumentNullException.ThrowIfNull(configuration);
 605
 76606        var config = (NetworkConfiguration)configuration;
 76607        HappyEyeballs.HttpClientExtension.UseIPv6 = config.EnableIPv6;
 608
 76609        InitializeLan(config);
 76610        InitializeRemote(config);
 611
 76612        if (string.IsNullOrEmpty(MockNetworkSettings))
 613        {
 34614            InitializeInterfaces();
 615        }
 616        else // Used in testing only.
 617        {
 618            // Format is <IPAddress>,<Index>,<Name>: <next interface>. Set index to -ve to simulate a gateway.
 42619            var interfaceList = MockNetworkSettings.Split('|');
 42620            var interfaces = new List<IPData>();
 254621            foreach (var details in interfaceList)
 622            {
 85623                var parts = details.Split(',');
 85624                if (NetworkUtils.TryParseToSubnet(parts[0], out var subnet))
 625                {
 85626                    var address = subnet.Prefix;
 85627                    var index = int.Parse(parts[1], CultureInfo.InvariantCulture);
 85628                    if (address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.In
 629                    {
 85630                        var data = new IPData(address, subnet, parts[2])
 85631                        {
 85632                            Index = index
 85633                        };
 85634                        interfaces.Add(data);
 635                    }
 636                }
 637                else
 638                {
 0639                    _logger.LogWarning("Could not parse mock interface settings: {Part}", details);
 640                }
 641            }
 642
 42643            _interfaces = interfaces;
 644        }
 645
 76646        EnforceBindSettings(config);
 76647        InitializeOverrides(config);
 648
 76649        PrintNetworkInformation(config, false);
 76650    }
 651
 652    /// <summary>
 653    /// Protected implementation of Dispose pattern.
 654    /// </summary>
 655    /// <param name="disposing"><c>True</c> to dispose the managed state.</param>
 656    protected virtual void Dispose(bool disposing)
 657    {
 55658        if (!_disposed)
 659        {
 55660            if (disposing)
 661            {
 55662                _configurationManager.NamedConfigurationUpdated -= ConfigurationUpdated;
 55663                NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged;
 55664                NetworkChange.NetworkAvailabilityChanged -= OnNetworkAvailabilityChanged;
 665            }
 666
 55667            _disposed = true;
 668        }
 55669    }
 670
 671    /// <inheritdoc/>
 672    public bool TryParseInterface(string intf, [NotNullWhen(true)] out IReadOnlyList<IPData>? result)
 673    {
 15674        if (string.IsNullOrEmpty(intf)
 15675            || _interfaces is null
 15676            || _interfaces.Count == 0)
 677        {
 0678            result = null;
 0679            return false;
 680        }
 681
 682        // Match all interfaces starting with names starting with token
 15683        result = _interfaces
 15684            .Where(i => i.Name.Equals(intf, StringComparison.OrdinalIgnoreCase)
 15685                        && ((IsIPv4Enabled && i.Address.AddressFamily == AddressFamily.InterNetwork)
 15686                            || (IsIPv6Enabled && i.Address.AddressFamily == AddressFamily.InterNetworkV6)))
 15687            .OrderBy(x => x.Index)
 15688            .ToArray();
 15689        return result.Count > 0;
 690    }
 691
 692    /// <inheritdoc/>
 693    public bool HasRemoteAccess(IPAddress remoteIP)
 694    {
 6695        var config = _configurationManager.GetNetworkConfiguration();
 6696        if (config.EnableRemoteAccess)
 697        {
 698            // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect r
 699            // If left blank, all remote addresses will be allowed.
 6700            if (_remoteAddressFilter.Any() && !IsInLocalNetwork(remoteIP))
 701            {
 702                // remoteAddressFilter is a whitelist or blacklist.
 4703                var matches = _remoteAddressFilter.Count(remoteNetwork => NetworkUtils.SubnetContainsAddress(remoteNetwo
 4704                if ((!config.IsRemoteIPFilterBlacklist && matches > 0)
 4705                    || (config.IsRemoteIPFilterBlacklist && matches == 0))
 706                {
 2707                    return true;
 708                }
 709
 2710                return false;
 711            }
 712        }
 0713        else if (!IsInLocalNetwork(remoteIP))
 714        {
 715            // Remote not enabled. So everyone should be LAN.
 0716            return false;
 717        }
 718
 2719        return true;
 720    }
 721
 722    /// <inheritdoc/>
 723    public IReadOnlyList<IPData> GetLoopbacks()
 724    {
 0725        if (!IsIPv4Enabled && !IsIPv6Enabled)
 726        {
 0727            return Array.Empty<IPData>();
 728        }
 729
 0730        var loopbackNetworks = new List<IPData>();
 0731        if (IsIPv4Enabled)
 732        {
 0733            loopbackNetworks.Add(new IPData(IPAddress.Loopback, NetworkConstants.IPv4RFC5735Loopback, "lo"));
 734        }
 735
 0736        if (IsIPv6Enabled)
 737        {
 0738            loopbackNetworks.Add(new IPData(IPAddress.IPv6Loopback, NetworkConstants.IPv6RFC4291Loopback, "lo"));
 739        }
 740
 0741        return loopbackNetworks;
 742    }
 743
 744    /// <inheritdoc/>
 745    public IReadOnlyList<IPData> GetAllBindInterfaces(bool individualInterfaces = false)
 746    {
 21747        return NetworkManager.GetAllBindInterfaces(individualInterfaces, _configurationManager, _interfaces, IsIPv4Enabl
 748    }
 749
 750    /// <summary>
 751    /// Reads the jellyfin configuration of the configuration manager and produces a list of interfaces that should be b
 752    /// </summary>
 753    /// <param name="individualInterfaces">Defines that only known interfaces should be used.</param>
 754    /// <param name="configurationManager">The ConfigurationManager.</param>
 755    /// <param name="knownInterfaces">The known interfaces that gets returned if possible or instructed.</param>
 756    /// <param name="readIpv4">Include IPV4 type interfaces.</param>
 757    /// <param name="readIpv6">Include IPV6 type interfaces.</param>
 758    /// <returns>A list of ip address of which jellyfin should bind to.</returns>
 759    public static IReadOnlyList<IPData> GetAllBindInterfaces(
 760        bool individualInterfaces,
 761        IConfigurationManager configurationManager,
 762        IReadOnlyList<IPData> knownInterfaces,
 763        bool readIpv4,
 764        bool readIpv6)
 765    {
 21766        var config = configurationManager.GetNetworkConfiguration();
 21767        var localNetworkAddresses = config.LocalNetworkAddresses;
 21768        if ((localNetworkAddresses.Length > 0 && !string.IsNullOrWhiteSpace(localNetworkAddresses[0]) && knownInterfaces
 769        {
 0770            return knownInterfaces;
 771        }
 772
 773        // No bind address and no exclusions, so listen on all interfaces.
 21774        var result = new List<IPData>();
 21775        if (readIpv4 && readIpv6)
 776        {
 777            // Kestrel source code shows it uses Sockets.DualMode - so this also covers IPAddress.Any by default
 0778            result.Add(new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any));
 779        }
 21780        else if (readIpv4)
 781        {
 21782            result.Add(new IPData(IPAddress.Any, NetworkConstants.IPv4Any));
 783        }
 0784        else if (readIpv6)
 785        {
 786            // Cannot use IPv6Any as Kestrel will bind to IPv4 addresses too.
 0787            foreach (var iface in knownInterfaces)
 788            {
 0789                if (iface.AddressFamily == AddressFamily.InterNetworkV6)
 790                {
 0791                    result.Add(iface);
 792                }
 793            }
 794        }
 795
 21796        return result;
 797    }
 798
 799    /// <inheritdoc/>
 800    public string GetBindAddress(string source, out int? port)
 801    {
 23802        if (!NetworkUtils.TryParseHost(source, out var addresses, IsIPv4Enabled, IsIPv6Enabled))
 803        {
 4804            addresses = Array.Empty<IPAddress>();
 805        }
 806
 23807        var result = GetBindAddress(addresses.FirstOrDefault(), out port);
 23808        return result;
 809    }
 810
 811    /// <inheritdoc/>
 812    public string GetBindAddress(HttpRequest source, out int? port)
 813    {
 0814        var result = GetBindAddress(source.Host.Host, out port);
 0815        port ??= source.Host.Port;
 816
 0817        return result;
 818    }
 819
 820    /// <inheritdoc/>
 821    public string GetBindAddress(IPAddress? source, out int? port, bool skipOverrides = false)
 822    {
 23823        port = null;
 824
 825        string result;
 826
 23827        if (source is not null)
 828        {
 19829            if (IsIPv4Enabled && !IsIPv6Enabled && source.AddressFamily == AddressFamily.InterNetworkV6)
 830            {
 0831                _logger.LogWarning("IPv6 is disabled in Jellyfin, but enabled in the OS. This may affect how the interfa
 832            }
 833
 19834            if (!IsIPv4Enabled && IsIPv6Enabled && source.AddressFamily == AddressFamily.InterNetwork)
 835            {
 0836                _logger.LogWarning("IPv4 is disabled in Jellyfin, but enabled in the OS. This may affect how the interfa
 837            }
 838
 19839            bool isExternal = !IsInLocalNetwork(source);
 19840            _logger.LogDebug("Trying to get bind address for source {Source} - External: {IsExternal}", source, isExtern
 841
 19842            if (!skipOverrides && MatchesPublishedServerUrl(source, isExternal, out result))
 843            {
 6844                return result;
 845            }
 846
 847            // No preference given, so move on to bind addresses.
 13848            if (MatchesBindInterface(source, isExternal, out result))
 849            {
 11850                return result;
 851            }
 852
 2853            if (isExternal && MatchesExternalInterface(source, out result))
 854            {
 0855                return result;
 856            }
 857        }
 858
 859        // Get the first LAN interface address that's not excluded and not a loopback address.
 860        // Get all available interfaces, prefer local interfaces
 6861        var availableInterfaces = _interfaces.Where(x => !IPAddress.IsLoopback(x.Address))
 6862            .OrderByDescending(x => IsInLocalNetwork(x.Address))
 6863            .ThenBy(x => x.Index)
 6864            .ToList();
 865
 6866        if (availableInterfaces.Count == 0)
 867        {
 868            // There isn't any others, so we'll use the loopback.
 0869            result = IsIPv4Enabled && !IsIPv6Enabled ? "127.0.0.1" : "::1";
 0870            _logger.LogWarning("{Source}: Only loopback {Result} returned, using that as bind address.", source, result)
 0871            return result;
 872        }
 873
 874        // If no source address is given, use the preferred (first) interface
 6875        if (source is null)
 876        {
 4877            result = NetworkUtils.FormatIPString(availableInterfaces.First().Address);
 4878            _logger.LogDebug("{Source}: Using first internal interface as bind address: {Result}", source, result);
 4879            return result;
 880        }
 881
 882        // Does the request originate in one of the interface subnets?
 883        // (For systems with multiple internal network cards, and multiple subnets)
 8884        foreach (var intf in availableInterfaces)
 885        {
 2886            if (NetworkUtils.SubnetContainsAddress(intf.Subnet, source))
 887            {
 0888                result = NetworkUtils.FormatIPString(intf.Address);
 0889                _logger.LogDebug("{Source}: Found interface with matching subnet, using it as bind address: {Result}", s
 0890                return result;
 891            }
 892        }
 893
 894        // Fallback to first available interface
 2895        result = NetworkUtils.FormatIPString(availableInterfaces[0].Address);
 2896        _logger.LogDebug("{Source}: No matching interfaces found, using preferred interface as bind address: {Result}", 
 2897        return result;
 0898    }
 899
 900    /// <inheritdoc/>
 901    public IReadOnlyList<IPData> GetInternalBindAddresses()
 902    {
 903        // Select all local bind addresses
 6904        return _interfaces.Where(x => IsInLocalNetwork(x.Address))
 6905            .OrderBy(x => x.Index)
 6906            .ToList();
 907    }
 908
 909    /// <inheritdoc/>
 910    public bool IsInLocalNetwork(string address)
 911    {
 0912        if (NetworkUtils.TryParseToSubnet(address, out var subnet))
 913        {
 0914            return IsInLocalNetwork(subnet.Prefix);
 915        }
 916
 0917        return NetworkUtils.TryParseHost(address, out var addresses, IsIPv4Enabled, IsIPv6Enabled)
 0918               && addresses.Any(IsInLocalNetwork);
 919    }
 920
 921    /// <summary>
 922    ///  Get if the IPAddress is Link-local.
 923    /// </summary>
 924    /// <param name="address">The IP Address.</param>
 925    /// <returns>Bool indicates if the address is link-local.</returns>
 926    public bool IsLinkLocalAddress(IPAddress address)
 927    {
 4928        ArgumentNullException.ThrowIfNull(address);
 4929        return NetworkConstants.IPv4RFC3927LinkLocal.Contains(address) || address.IsIPv6LinkLocal;
 930    }
 931
 932    /// <inheritdoc/>
 933    public bool IsInLocalNetwork(IPAddress address)
 934    {
 157935        ArgumentNullException.ThrowIfNull(address);
 936
 937        // Map IPv6 mapped IPv4 back to IPv4 (happens if Kestrel runs in dual-socket mode)
 157938        if (address.IsIPv4MappedToIPv6)
 939        {
 0940            address = address.MapToIPv4();
 941        }
 942
 157943        if ((TrustAllIPv6Interfaces && address.AddressFamily == AddressFamily.InterNetworkV6)
 157944            || IPAddress.IsLoopback(address))
 945        {
 85946            return true;
 947        }
 948
 949        // As private addresses can be redefined by Configuration.LocalNetworkAddresses
 72950        return CheckIfLanAndNotExcluded(address);
 951    }
 952
 953    /// <summary>
 954    /// Check if the address is in the LAN and not excluded.
 955    /// </summary>
 956    /// <param name="address">The IP address to check. The caller should make sure this is not an IPv4MappedToIPv6 addre
 957    /// <returns>Boolean indicates whether the address is in LAN.</returns>
 958    private bool CheckIfLanAndNotExcluded(IPAddress address)
 959    {
 376960        foreach (var lanSubnet in _lanSubnets)
 961        {
 135962            if (lanSubnet.Contains(address))
 963            {
 82964                foreach (var excludedSubnet in _excludedSubnets)
 965                {
 4966                    if (excludedSubnet.Contains(address))
 967                    {
 2968                        return false;
 969                    }
 970                }
 971
 36972                return true;
 973            }
 974        }
 975
 34976        return false;
 38977    }
 978
 979    /// <summary>
 980    /// Attempts to match the source against the published server URL overrides.
 981    /// </summary>
 982    /// <param name="source">IP source address to use.</param>
 983    /// <param name="isInExternalSubnet">True if the source is in an external subnet.</param>
 984    /// <param name="bindPreference">The published server URL that matches the source address.</param>
 985    /// <returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
 986    private bool MatchesPublishedServerUrl(IPAddress source, bool isInExternalSubnet, out string bindPreference)
 987    {
 19988        bindPreference = string.Empty;
 19989        int? port = null;
 990
 991        // Only consider subnets including the source IP, preferring specific overrides
 992        List<PublishedServerUriOverride> validPublishedServerUrls;
 19993        if (!isInExternalSubnet)
 994        {
 995            // Only use matching internal subnets
 996            // Prefer more specific (bigger subnet prefix) overrides
 10997            validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsInternalOverride && NetworkUtils.SubnetContai
 10998                .OrderByDescending(x => x.Data.Subnet.PrefixLength)
 10999                .ToList();
 1000        }
 1001        else
 1002        {
 1003            // Only use matching external subnets
 1004            // Prefer more specific (bigger subnet prefix) overrides
 91005            validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsExternalOverride && NetworkUtils.SubnetContai
 91006                .OrderByDescending(x => x.Data.Subnet.PrefixLength)
 91007                .ToList();
 1008        }
 1009
 441010        foreach (var data in validPublishedServerUrls)
 1011        {
 1012            // Get interface matching override subnet
 61013            var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(x => NetworkUtils.SubnetContainsAddress(data.Dat
 1014
 61015            if (intf?.Address is not null
 61016                || (data.Data.AddressFamily == AddressFamily.InterNetwork && data.Data.Address.Equals(IPAddress.Any))
 61017                || (data.Data.AddressFamily == AddressFamily.InterNetworkV6 && data.Data.Address.Equals(IPAddress.IPv6An
 1018            {
 1019                // If matching interface is found, use override
 61020                bindPreference = data.OverrideUri;
 61021                break;
 1022            }
 1023        }
 1024
 191025        if (string.IsNullOrEmpty(bindPreference))
 1026        {
 131027            _logger.LogDebug("{Source}: No matching bind address override found", source);
 131028            return false;
 1029        }
 1030
 1031        // Handle override specifying port
 61032        var parts = bindPreference.Split(':');
 61033        if (parts.Length > 1)
 1034        {
 51035            if (int.TryParse(parts[1], out int p))
 1036            {
 01037                bindPreference = parts[0];
 01038                port = p;
 01039                _logger.LogDebug("{Source}: Matching bind address override found: {Address}:{Port}", source, bindPrefere
 01040                return true;
 1041            }
 1042        }
 1043
 61044        _logger.LogDebug("{Source}: Matching bind address override found: {Address}", source, bindPreference);
 1045
 61046        return true;
 1047    }
 1048
 1049    /// <summary>
 1050    /// Attempts to match the source against the user defined bind interfaces.
 1051    /// </summary>
 1052    /// <param name="source">IP source address to use.</param>
 1053    /// <param name="isInExternalSubnet">True if the source is in the external subnet.</param>
 1054    /// <param name="result">The result, if a match is found.</param>
 1055    /// <returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
 1056    private bool MatchesBindInterface(IPAddress source, bool isInExternalSubnet, out string result)
 1057    {
 131058        result = string.Empty;
 1059
 131060        int count = _interfaces.Count;
 131061        if (count == 1 && (_interfaces[0].Address.Equals(IPAddress.Any) || _interfaces[0].Address.Equals(IPAddress.IPv6A
 1062        {
 1063            // Ignore IPAny addresses.
 01064            count = 0;
 1065        }
 1066
 131067        if (count == 0)
 1068        {
 01069            return false;
 1070        }
 1071
 131072        IPAddress? bindAddress = null;
 131073        if (isInExternalSubnet)
 1074        {
 51075            var externalInterfaces = _interfaces.Where(x => !IsInLocalNetwork(x.Address))
 51076                .Where(x => !IsLinkLocalAddress(x.Address))
 51077                .OrderBy(x => x.Index)
 51078                .ToList();
 51079            if (externalInterfaces.Count > 0)
 1080            {
 1081                // Check to see if any of the external bind interfaces are in the same subnet as the source.
 1082                // If none exists, this will select the first external interface if there is one.
 41083                bindAddress = externalInterfaces
 41084                    .OrderByDescending(x => NetworkUtils.SubnetContainsAddress(x.Subnet, source))
 41085                    .ThenByDescending(x => x.Subnet.PrefixLength)
 41086                    .ThenBy(x => x.Index)
 41087                    .Select(x => x.Address)
 41088                    .First();
 1089
 41090                result = NetworkUtils.FormatIPString(bindAddress);
 41091                _logger.LogDebug("{Source}: External request received, matching external bind address found: {Result}", 
 41092                return true;
 1093            }
 1094
 11095            _logger.LogDebug("{Source}: External request received, no matching external bind address found, trying inter
 1096        }
 1097        else
 1098        {
 1099            // Check to see if any of the internal bind interfaces are in the same subnet as the source.
 1100            // If none exists, this will select the first internal interface if there is one.
 81101            bindAddress = _interfaces.Where(x => IsInLocalNetwork(x.Address))
 81102                .OrderByDescending(x => NetworkUtils.SubnetContainsAddress(x.Subnet, source))
 81103                .ThenByDescending(x => x.Subnet.PrefixLength)
 81104                .ThenBy(x => x.Index)
 81105                .Select(x => x.Address)
 81106                .FirstOrDefault();
 1107
 81108            if (bindAddress is not null)
 1109            {
 71110                result = NetworkUtils.FormatIPString(bindAddress);
 71111                _logger.LogDebug("{Source}: Internal request received, matching internal bind address found: {Result}", 
 71112                return true;
 1113            }
 1114        }
 1115
 21116        return false;
 1117    }
 1118
 1119    /// <summary>
 1120    /// Attempts to match the source against external interfaces.
 1121    /// </summary>
 1122    /// <param name="source">IP source address to use.</param>
 1123    /// <param name="result">The result, if a match is found.</param>
 1124    /// <returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
 1125    private bool MatchesExternalInterface(IPAddress source, out string result)
 1126    {
 1127        // Get the first external interface address that isn't a loopback.
 11128        var extResult = _interfaces
 11129            .Where(p => !IsInLocalNetwork(p.Address))
 11130            .Where(p => p.Address.AddressFamily.Equals(source.AddressFamily))
 11131            .Where(p => !IsLinkLocalAddress(p.Address))
 11132            .OrderBy(x => x.Index).ToArray();
 1133
 1134        // No external interface found
 11135        if (extResult.Length == 0)
 1136        {
 11137            result = string.Empty;
 11138            _logger.LogDebug("{Source}: External request received, but no external interface found. Need to route throug
 11139            return false;
 1140        }
 1141
 1142        // Does the request originate in one of the interface subnets?
 1143        // (For systems with multiple network cards and/or multiple subnets)
 01144        foreach (var intf in extResult)
 1145        {
 01146            if (NetworkUtils.SubnetContainsAddress(intf.Subnet, source))
 1147            {
 01148                result = NetworkUtils.FormatIPString(intf.Address);
 01149                _logger.LogDebug("{Source}: Found external interface with matching subnet, using it as bind address: {Re
 01150                return true;
 1151            }
 1152        }
 1153
 1154        // Fallback to first external interface.
 01155        result = NetworkUtils.FormatIPString(extResult[0].Address);
 01156        _logger.LogDebug("{Source}: Using first external interface as bind address: {Result}", source, result);
 01157        return true;
 1158    }
 1159
 1160    private void PrintNetworkInformation(NetworkConfiguration config, bool debug = true)
 1161    {
 761162        var logLevel = debug ? LogLevel.Debug : LogLevel.Information;
 761163        if (_logger.IsEnabled(logLevel))
 1164        {
 211165            _logger.Log(logLevel, "Defined LAN subnets: {Subnets}", _lanSubnets.Select(s => s.Prefix + "/" + s.PrefixLen
 211166            _logger.Log(logLevel, "Defined LAN exclusions: {Subnets}", _excludedSubnets.Select(s => s.Prefix + "/" + s.P
 211167            _logger.Log(logLevel, "Used LAN subnets: {Subnets}", _lanSubnets.Where(s => !_excludedSubnets.Contains(s)).S
 211168            _logger.Log(logLevel, "Filtered interface addresses: {Addresses}", _interfaces.OrderByDescending(x => x.Addr
 211169            _logger.Log(logLevel, "Bind Addresses {Addresses}", GetAllBindInterfaces(false).OrderByDescending(x => x.Add
 211170            _logger.Log(logLevel, "Remote IP filter is {Type}", config.IsRemoteIPFilterBlacklist ? "Blocklist" : "Allowl
 211171            _logger.Log(logLevel, "Filtered subnets: {Subnets}", _remoteAddressFilter.Select(s => s.Prefix + "/" + s.Pre
 1172        }
 761173    }
 1174}

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>&)
HasRemoteAccess(System.Net.IPAddress)
GetLoopbacks()
GetAllBindInterfaces(System.Boolean)
GetAllBindInterfaces(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)