< 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: 357
Uncovered lines: 124
Coverable lines: 481
Total lines: 1153
Line coverage: 74.2%
Branch coverage
64%
Covered branches: 179
Total branches: 276
Branch coverage: 64.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%22100%
.cctor()100%11100%
get_IsIPv4Enabled()100%11100%
get_IsIPv6Enabled()100%11100%
get_PublishedServerUrls()100%210%
Dispose()100%11100%
OnNetworkAvailabilityChanged(...)100%210%
OnNetworkAddressChanged(...)100%210%
HandleNetworkChange()0%620%
OnNetworkChange()0%4260%
InitializeInterfaces()81.81%27.832277.08%
InitializeLan(...)90%10.011095.45%
EnforceBindSettings(...)77.27%22.542289.65%
InitializeRemote(...)83.33%12.051292.85%
InitializeOverrides(...)72.72%35.242269.87%
ConfigurationUpdated(...)50%2.15266.66%
UpdateSettings(...)80%10.011096.15%
Dispose(...)100%44100%
TryParseInterface(...)50%6.17683.33%
HasRemoteAccess(...)87.5%17.541681.81%
GetMacAddresses()100%210%
GetLoopbacks()0%7280%
GetAllBindInterfaces(...)44.44%43.511857.14%
GetBindAddress(...)100%22100%
GetBindAddress(...)0%620%
GetBindAddress(...)69.44%66.253671.42%
GetInternalBindAddresses()100%11100%
IsInLocalNetwork(...)0%272160%
IsLinkLocalAddress(...)0%620%
IsInLocalNetwork(...)62.5%9.49871.42%
CheckIfLanAndNotExcluded(...)100%88100%
MatchesPublishedServerUrl(...)85.71%14.711484.61%
MatchesBindInterface(...)78.57%14.051493.54%
MatchesExternalInterface(...)16.66%9.75652.94%
PrintNetworkInformation(...)66.66%66100%

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 MediaBrowser.Common.Configuration;
 11using MediaBrowser.Common.Net;
 12using MediaBrowser.Model.Net;
 13using Microsoft.AspNetCore.Http;
 14using Microsoft.Extensions.Configuration;
 15using Microsoft.Extensions.Logging;
 16using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
 17using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager;
 18using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
 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 object _initLock;
 31
 32    private readonly ILogger<NetworkManager> _logger;
 33
 34    private readonly IConfigurationManager _configurationManager;
 35
 36    private readonly IConfiguration _startupConfig;
 37
 38    private readonly object _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    /// List of all interface MAC addresses.
 54    /// </summary>
 55    private IReadOnlyList<PhysicalAddress> _macAddresses;
 56
 57    /// <summary>
 58    /// Dictionary containing interface addresses and their subnets.
 59    /// </summary>
 60    private IReadOnlyList<IPData> _interfaces;
 61
 62    /// <summary>
 63    /// Unfiltered user defined LAN subnets (<see cref="NetworkConfiguration.LocalNetworkSubnets"/>)
 64    /// or internal interface network subnets if undefined by user.
 65    /// </summary>
 66    private IReadOnlyList<IPNetwork> _lanSubnets;
 67
 68    /// <summary>
 69    /// User defined list of subnets to excluded from the LAN.
 70    /// </summary>
 71    private IReadOnlyList<IPNetwork> _excludedSubnets;
 72
 73    /// <summary>
 74    /// True if this object is disposed.
 75    /// </summary>
 76    private bool _disposed;
 77
 78    /// <summary>
 79    /// Initializes a new instance of the <see cref="NetworkManager"/> class.
 80    /// </summary>
 81    /// <param name="configurationManager">The <see cref="IConfigurationManager"/> instance.</param>
 82    /// <param name="startupConfig">The <see cref="IConfiguration"/> instance holding startup parameters.</param>
 83    /// <param name="logger">Logger to use for messages.</param>
 84#pragma warning disable CS8618 // Non-nullable field is uninitialized. : Values are set in UpdateSettings function. Comp
 85    public NetworkManager(IConfigurationManager configurationManager, IConfiguration startupConfig, ILogger<NetworkManag
 86    {
 7787        ArgumentNullException.ThrowIfNull(logger);
 7788        ArgumentNullException.ThrowIfNull(configurationManager);
 89
 7790        _logger = logger;
 7791        _configurationManager = configurationManager;
 7792        _startupConfig = startupConfig;
 7793        _initLock = new();
 7794        _interfaces = new List<IPData>();
 7795        _macAddresses = new List<PhysicalAddress>();
 7796        _publishedServerUrls = new List<PublishedServerUriOverride>();
 7797        _networkEventLock = new object();
 7798        _remoteAddressFilter = new List<IPNetwork>();
 99
 77100        _ = bool.TryParse(startupConfig[DetectNetworkChangeKey], out var detectNetworkChange);
 101
 77102        UpdateSettings(_configurationManager.GetNetworkConfiguration());
 103
 77104        if (detectNetworkChange)
 105        {
 22106            NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;
 22107            NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
 108        }
 109
 77110        _configurationManager.NamedConfigurationUpdated += ConfigurationUpdated;
 77111    }
 112#pragma warning restore CS8618 // Non-nullable field is uninitialized.
 113
 114    /// <summary>
 115    /// Event triggered on network changes.
 116    /// </summary>
 117    public event EventHandler? NetworkChanged;
 118
 119    /// <summary>
 120    /// Gets or sets a value indicating whether testing is taking place.
 121    /// </summary>
 3122    public static string MockNetworkSettings { get; set; } = string.Empty;
 123
 124    /// <summary>
 125    /// Gets a value indicating whether IP4 is enabled.
 126    /// </summary>
 374127    public bool IsIPv4Enabled => _configurationManager.GetNetworkConfiguration().EnableIPv4;
 128
 129    /// <summary>
 130    /// Gets a value indicating whether IP6 is enabled.
 131    /// </summary>
 254132    public bool IsIPv6Enabled => _configurationManager.GetNetworkConfiguration().EnableIPv6;
 133
 134    /// <summary>
 135    /// Gets a value indicating whether is all IPv6 interfaces are trusted as internal.
 136    /// </summary>
 137    public bool TrustAllIPv6Interfaces { get; private set; }
 138
 139    /// <summary>
 140    /// Gets the Published server override list.
 141    /// </summary>
 0142    public IReadOnlyList<PublishedServerUriOverride> PublishedServerUrls => _publishedServerUrls;
 143
 144    /// <inheritdoc/>
 145    public void Dispose()
 146    {
 55147        Dispose(true);
 55148        GC.SuppressFinalize(this);
 55149    }
 150
 151    /// <summary>
 152    /// Handler for network change events.
 153    /// </summary>
 154    /// <param name="sender">Sender.</param>
 155    /// <param name="e">A <see cref="NetworkAvailabilityEventArgs"/> containing network availability information.</param
 156    private void OnNetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs e)
 157    {
 0158        _logger.LogDebug("Network availability changed.");
 0159        HandleNetworkChange();
 0160    }
 161
 162    /// <summary>
 163    /// Handler for network change events.
 164    /// </summary>
 165    /// <param name="sender">Sender.</param>
 166    /// <param name="e">An <see cref="EventArgs"/>.</param>
 167    private void OnNetworkAddressChanged(object? sender, EventArgs e)
 168    {
 0169        _logger.LogDebug("Network address change detected.");
 0170        HandleNetworkChange();
 0171    }
 172
 173    /// <summary>
 174    /// Triggers our event, and re-loads interface information.
 175    /// </summary>
 176    private void HandleNetworkChange()
 177    {
 0178        lock (_networkEventLock)
 179        {
 0180            if (!_eventfire)
 181            {
 182                // As network events tend to fire one after the other only fire once every second.
 0183                _eventfire = true;
 0184                OnNetworkChange();
 185            }
 0186        }
 0187    }
 188
 189    /// <summary>
 190    /// Waits for 2 seconds before re-initialising the settings, as typically these events fire multiple times in succes
 191    /// </summary>
 192    private void OnNetworkChange()
 193    {
 194        try
 195        {
 0196            Thread.Sleep(2000);
 0197            var networkConfig = _configurationManager.GetNetworkConfiguration();
 0198            if (IsIPv6Enabled && !Socket.OSSupportsIPv6)
 199            {
 0200                UpdateSettings(networkConfig);
 201            }
 202            else
 203            {
 0204                InitializeInterfaces();
 0205                InitializeLan(networkConfig);
 0206                EnforceBindSettings(networkConfig);
 207            }
 208
 0209            PrintNetworkInformation(networkConfig);
 0210            NetworkChanged?.Invoke(this, EventArgs.Empty);
 0211        }
 212        finally
 213        {
 0214            _eventfire = false;
 0215        }
 0216    }
 217
 218    /// <summary>
 219    /// Generate a list of all the interface ip addresses and submasks where that are in the active/unknown state.
 220    /// Generate a list of all active mac addresses that aren't loopback addresses.
 221    /// </summary>
 222    private void InitializeInterfaces()
 223    {
 35224        lock (_initLock)
 225        {
 35226            _logger.LogDebug("Refreshing interfaces.");
 227
 35228            var interfaces = new List<IPData>();
 35229            var macAddresses = new List<PhysicalAddress>();
 230
 231            try
 232            {
 35233                var nics = NetworkInterface.GetAllNetworkInterfaces()
 35234                    .Where(i => i.OperationalStatus == OperationalStatus.Up);
 235
 210236                foreach (NetworkInterface adapter in nics)
 237                {
 238                    try
 239                    {
 70240                        var ipProperties = adapter.GetIPProperties();
 70241                        var mac = adapter.GetPhysicalAddress();
 242
 243                        // Populate MAC list
 70244                        if (adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && !PhysicalAddress.None.Equal
 245                        {
 35246                            macAddresses.Add(mac);
 247                        }
 248
 249                        // Populate interface list
 420250                        foreach (var info in ipProperties.UnicastAddresses)
 251                        {
 140252                            if (IsIPv4Enabled && info.Address.AddressFamily == AddressFamily.InterNetwork)
 253                            {
 70254                                var interfaceObject = new IPData(info.Address, new IPNetwork(info.Address, info.PrefixLe
 70255                                {
 70256                                    Index = ipProperties.GetIPv4Properties().Index,
 70257                                    Name = adapter.Name,
 70258                                    SupportsMulticast = adapter.SupportsMulticast
 70259                                };
 260
 70261                                interfaces.Add(interfaceObject);
 262                            }
 70263                            else if (IsIPv6Enabled && info.Address.AddressFamily == AddressFamily.InterNetworkV6)
 264                            {
 14265                                var interfaceObject = new IPData(info.Address, new IPNetwork(info.Address, info.PrefixLe
 14266                                {
 14267                                    Index = ipProperties.GetIPv6Properties().Index,
 14268                                    Name = adapter.Name,
 14269                                    SupportsMulticast = adapter.SupportsMulticast
 14270                                };
 271
 14272                                interfaces.Add(interfaceObject);
 273                            }
 274                        }
 70275                    }
 0276                    catch (Exception ex)
 277                    {
 278                        // Ignore error, and attempt to continue.
 0279                        _logger.LogError(ex, "Error encountered parsing interfaces.");
 0280                    }
 281                }
 35282            }
 0283            catch (Exception ex)
 284            {
 0285                _logger.LogError(ex, "Error obtaining interfaces.");
 0286            }
 287
 288            // If no interfaces are found, fallback to loopback interfaces.
 35289            if (interfaces.Count == 0)
 290            {
 0291                _logger.LogWarning("No interface information available. Using loopback interface(s).");
 292
 0293                if (IsIPv4Enabled)
 294                {
 0295                    interfaces.Add(new IPData(IPAddress.Loopback, NetworkConstants.IPv4RFC5735Loopback, "lo"));
 296                }
 297
 0298                if (IsIPv6Enabled)
 299                {
 0300                    interfaces.Add(new IPData(IPAddress.IPv6Loopback, NetworkConstants.IPv6RFC4291Loopback, "lo"));
 301                }
 302            }
 303
 35304            _logger.LogDebug("Discovered {NumberOfInterfaces} interfaces.", interfaces.Count);
 35305            _logger.LogDebug("Interfaces addresses: {Addresses}", interfaces.OrderByDescending(s => s.AddressFamily == A
 306
 35307            _macAddresses = macAddresses;
 35308            _interfaces = interfaces;
 35309        }
 35310    }
 311
 312    /// <summary>
 313    /// Initializes internal LAN cache.
 314    /// </summary>
 315    private void InitializeLan(NetworkConfiguration config)
 316    {
 77317        lock (_initLock)
 318        {
 77319            _logger.LogDebug("Refreshing LAN information.");
 320
 321            // Get configuration options
 77322            var subnets = config.LocalNetworkSubnets;
 323
 324            // If no LAN addresses are specified, all private subnets and Loopback are deemed to be the LAN
 77325            if (!NetworkUtils.TryParseToSubnets(subnets, out var lanSubnets, false) || lanSubnets.Count == 0)
 326            {
 43327                _logger.LogDebug("Using LAN interface addresses as user provided no LAN details.");
 328
 43329                var fallbackLanSubnets = new List<IPNetwork>();
 43330                if (IsIPv6Enabled)
 331                {
 7332                    fallbackLanSubnets.Add(NetworkConstants.IPv6RFC4291Loopback); // RFC 4291 (Loopback)
 7333                    fallbackLanSubnets.Add(NetworkConstants.IPv6RFC4291SiteLocal); // RFC 4291 (Site local)
 7334                    fallbackLanSubnets.Add(NetworkConstants.IPv6RFC4193UniqueLocal); // RFC 4193 (Unique local)
 335                }
 336
 43337                if (IsIPv4Enabled)
 338                {
 43339                    fallbackLanSubnets.Add(NetworkConstants.IPv4RFC5735Loopback); // RFC 5735 (Loopback)
 43340                    fallbackLanSubnets.Add(NetworkConstants.IPv4RFC1918PrivateClassA); // RFC 1918 (private Class A)
 43341                    fallbackLanSubnets.Add(NetworkConstants.IPv4RFC1918PrivateClassB); // RFC 1918 (private Class B)
 43342                    fallbackLanSubnets.Add(NetworkConstants.IPv4RFC1918PrivateClassC); // RFC 1918 (private Class C)
 343                }
 344
 43345                _lanSubnets = fallbackLanSubnets;
 346            }
 347            else
 348            {
 34349                _lanSubnets = lanSubnets;
 350            }
 351
 77352            _excludedSubnets = NetworkUtils.TryParseToSubnets(subnets, out var excludedSubnets, true)
 77353                ? excludedSubnets
 77354                : new List<IPNetwork>();
 77355        }
 77356    }
 357
 358    /// <summary>
 359    /// Enforce bind addresses and exclusions on available interfaces.
 360    /// </summary>
 361    private void EnforceBindSettings(NetworkConfiguration config)
 362    {
 77363        lock (_initLock)
 364        {
 365            // Respect explicit bind addresses
 77366            var interfaces = _interfaces.ToList();
 77367            var localNetworkAddresses = config.LocalNetworkAddresses;
 77368            if (localNetworkAddresses.Length > 0 && !string.IsNullOrWhiteSpace(localNetworkAddresses[0]))
 369            {
 12370                var bindAddresses = localNetworkAddresses.Select(p => NetworkUtils.TryParseToSubnet(p, out var network)
 12371                        ? network.Prefix
 12372                        : (interfaces.Where(x => x.Name.Equals(p, StringComparison.OrdinalIgnoreCase))
 12373                            .Select(x => x.Address)
 12374                            .FirstOrDefault() ?? IPAddress.None))
 12375                    .Where(x => x != IPAddress.None)
 12376                    .ToHashSet();
 12377                interfaces = interfaces.Where(x => bindAddresses.Contains(x.Address)).ToList();
 378
 12379                if (bindAddresses.Contains(IPAddress.Loopback) && !interfaces.Any(i => i.Address.Equals(IPAddress.Loopba
 380                {
 0381                    interfaces.Add(new IPData(IPAddress.Loopback, NetworkConstants.IPv4RFC5735Loopback, "lo"));
 382                }
 383
 12384                if (bindAddresses.Contains(IPAddress.IPv6Loopback) && !interfaces.Any(i => i.Address.Equals(IPAddress.IP
 385                {
 0386                    interfaces.Add(new IPData(IPAddress.IPv6Loopback, NetworkConstants.IPv6RFC4291Loopback, "lo"));
 387                }
 388            }
 389
 390            // Remove all interfaces matching any virtual machine interface prefix
 77391            if (config.IgnoreVirtualInterfaces)
 392            {
 393                // Remove potentially existing * and split config string into prefixes
 77394                var virtualInterfacePrefixes = config.VirtualInterfaceNames
 77395                    .Select(i => i.Replace("*", string.Empty, StringComparison.OrdinalIgnoreCase));
 396
 397                // Check all interfaces for matches against the prefixes and remove them
 77398                if (_interfaces.Count > 0)
 399                {
 308400                    foreach (var virtualInterfacePrefix in virtualInterfacePrefixes)
 401                    {
 77402                        interfaces.RemoveAll(x => x.Name.StartsWith(virtualInterfacePrefix, StringComparison.OrdinalIgno
 403                    }
 404                }
 405            }
 406
 407            // Remove all IPv4 interfaces if IPv4 is disabled
 77408            if (!IsIPv4Enabled)
 409            {
 0410                interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetwork);
 411            }
 412
 413            // Remove all IPv6 interfaces if IPv6 is disabled
 77414            if (!IsIPv6Enabled)
 415            {
 53416                interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetworkV6);
 417            }
 418
 419            // Users may have complex networking configuration that multiple interfaces sharing the same IP address
 420            // Only return one IP for binding, and let the OS handle the rest
 77421            _interfaces = interfaces.DistinctBy(iface => iface.Address).ToList();
 77422        }
 77423    }
 424
 425    /// <summary>
 426    /// Initializes the remote address values.
 427    /// </summary>
 428    private void InitializeRemote(NetworkConfiguration config)
 429    {
 77430        lock (_initLock)
 431        {
 432            // Parse config values into filter collection
 77433            var remoteIPFilter = config.RemoteIPFilter;
 77434            if (remoteIPFilter.Length != 0 && !string.IsNullOrWhiteSpace(remoteIPFilter[0]))
 435            {
 436                // Parse all IPs with netmask to a subnet
 4437                var remoteAddressFilter = new List<IPNetwork>();
 4438                var remoteFilteredSubnets = remoteIPFilter.Where(x => x.Contains('/', StringComparison.OrdinalIgnoreCase
 4439                if (NetworkUtils.TryParseToSubnets(remoteFilteredSubnets, out var remoteAddressFilterResult, false))
 440                {
 0441                    remoteAddressFilter = remoteAddressFilterResult.ToList();
 442                }
 443
 444                // Parse everything else as an IP and construct subnet with a single IP
 4445                var remoteFilteredIPs = remoteIPFilter.Where(x => !x.Contains('/', StringComparison.OrdinalIgnoreCase));
 18446                foreach (var ip in remoteFilteredIPs)
 447                {
 5448                    if (IPAddress.TryParse(ip, out var ipp))
 449                    {
 5450                        remoteAddressFilter.Add(new IPNetwork(ipp, ipp.AddressFamily == AddressFamily.InterNetwork ? Net
 451                    }
 452                }
 453
 4454                _remoteAddressFilter = remoteAddressFilter;
 455            }
 77456        }
 77457    }
 458
 459    /// <summary>
 460    /// Parses the user defined overrides into the dictionary object.
 461    /// Overrides are the equivalent of localised publishedServerUrl, enabling
 462    /// different addresses to be advertised over different subnets.
 463    /// format is subnet=ipaddress|host|uri
 464    /// when subnet = 0.0.0.0, any external address matches.
 465    /// </summary>
 466    private void InitializeOverrides(NetworkConfiguration config)
 467    {
 77468        lock (_initLock)
 469        {
 77470            var publishedServerUrls = new List<PublishedServerUriOverride>();
 471
 472            // Prefer startup configuration.
 77473            var startupOverrideKey = _startupConfig[AddressOverrideKey];
 77474            if (!string.IsNullOrEmpty(startupOverrideKey))
 475            {
 0476                publishedServerUrls.Add(
 0477                    new PublishedServerUriOverride(
 0478                        new IPData(IPAddress.Any, NetworkConstants.IPv4Any),
 0479                        startupOverrideKey,
 0480                        true,
 0481                        true));
 0482                publishedServerUrls.Add(
 0483                    new PublishedServerUriOverride(
 0484                        new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any),
 0485                        startupOverrideKey,
 0486                        true,
 0487                        true));
 0488                _publishedServerUrls = publishedServerUrls;
 0489                return;
 490            }
 491
 77492            var overrides = config.PublishedServerUriBySubnet;
 168493            foreach (var entry in overrides)
 494            {
 8495                var parts = entry.Split('=');
 8496                if (parts.Length != 2)
 497                {
 0498                    _logger.LogError("Unable to parse bind override: {Entry}", entry);
 0499                    return;
 500                }
 501
 8502                var replacement = parts[1].Trim();
 8503                var identifier = parts[0];
 8504                if (string.Equals(identifier, "all", StringComparison.OrdinalIgnoreCase))
 505                {
 506                    // Drop any other overrides in case an "all" override exists
 2507                    publishedServerUrls.Clear();
 2508                    publishedServerUrls.Add(
 2509                        new PublishedServerUriOverride(
 2510                            new IPData(IPAddress.Any, NetworkConstants.IPv4Any),
 2511                            replacement,
 2512                            true,
 2513                            true));
 2514                    publishedServerUrls.Add(
 2515                        new PublishedServerUriOverride(
 2516                            new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any),
 2517                            replacement,
 2518                            true,
 2519                            true));
 2520                    break;
 521                }
 6522                else if (string.Equals(identifier, "external", StringComparison.OrdinalIgnoreCase))
 523                {
 4524                    publishedServerUrls.Add(
 4525                        new PublishedServerUriOverride(
 4526                            new IPData(IPAddress.Any, NetworkConstants.IPv4Any),
 4527                            replacement,
 4528                            false,
 4529                            true));
 4530                    publishedServerUrls.Add(
 4531                        new PublishedServerUriOverride(
 4532                            new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any),
 4533                            replacement,
 4534                            false,
 4535                            true));
 536                }
 2537                else if (string.Equals(identifier, "internal", StringComparison.OrdinalIgnoreCase))
 538                {
 0539                    foreach (var lan in _lanSubnets)
 540                    {
 0541                        var lanPrefix = lan.Prefix;
 0542                        publishedServerUrls.Add(
 0543                            new PublishedServerUriOverride(
 0544                                new IPData(lanPrefix, new IPNetwork(lanPrefix, lan.PrefixLength)),
 0545                                replacement,
 0546                                true,
 0547                                false));
 548                    }
 549                }
 2550                else if (NetworkUtils.TryParseToSubnet(identifier, out var result) && result is not null)
 551                {
 1552                    var data = new IPData(result.Prefix, result);
 1553                    publishedServerUrls.Add(
 1554                        new PublishedServerUriOverride(
 1555                            data,
 1556                            replacement,
 1557                            true,
 1558                            true));
 559                }
 1560                else if (TryParseInterface(identifier, out var ifaces))
 561                {
 4562                    foreach (var iface in ifaces)
 563                    {
 1564                        publishedServerUrls.Add(
 1565                            new PublishedServerUriOverride(
 1566                                iface,
 1567                                replacement,
 1568                                true,
 1569                                true));
 570                    }
 571                }
 572                else
 573                {
 0574                    _logger.LogError("Unable to parse bind override: {Entry}", entry);
 575                }
 576            }
 577
 77578            _publishedServerUrls = publishedServerUrls;
 77579        }
 77580    }
 581
 582    private void ConfigurationUpdated(object? sender, ConfigurationUpdateEventArgs evt)
 583    {
 1584        if (evt.Key.Equals(NetworkConfigurationStore.StoreKey, StringComparison.Ordinal))
 585        {
 0586            UpdateSettings((NetworkConfiguration)evt.NewConfiguration);
 587        }
 1588    }
 589
 590    /// <summary>
 591    /// Reloads all settings and re-Initializes the instance.
 592    /// </summary>
 593    /// <param name="configuration">The <see cref="NetworkConfiguration"/> to use.</param>
 594    public void UpdateSettings(object configuration)
 595    {
 77596        ArgumentNullException.ThrowIfNull(configuration);
 597
 77598        var config = (NetworkConfiguration)configuration;
 77599        HappyEyeballs.HttpClientExtension.UseIPv6 = config.EnableIPv6;
 600
 77601        InitializeLan(config);
 77602        InitializeRemote(config);
 603
 77604        if (string.IsNullOrEmpty(MockNetworkSettings))
 605        {
 35606            InitializeInterfaces();
 607        }
 608        else // Used in testing only.
 609        {
 610            // Format is <IPAddress>,<Index>,<Name>: <next interface>. Set index to -ve to simulate a gateway.
 42611            var interfaceList = MockNetworkSettings.Split('|');
 42612            var interfaces = new List<IPData>();
 254613            foreach (var details in interfaceList)
 614            {
 85615                var parts = details.Split(',');
 85616                if (NetworkUtils.TryParseToSubnet(parts[0], out var subnet))
 617                {
 85618                    var address = subnet.Prefix;
 85619                    var index = int.Parse(parts[1], CultureInfo.InvariantCulture);
 85620                    if (address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.In
 621                    {
 85622                        var data = new IPData(address, subnet, parts[2])
 85623                        {
 85624                            Index = index
 85625                        };
 85626                        interfaces.Add(data);
 627                    }
 628                }
 629                else
 630                {
 0631                    _logger.LogWarning("Could not parse mock interface settings: {Part}", details);
 632                }
 633            }
 634
 42635            _interfaces = interfaces;
 636        }
 637
 77638        EnforceBindSettings(config);
 77639        InitializeOverrides(config);
 640
 77641        PrintNetworkInformation(config, false);
 77642    }
 643
 644    /// <summary>
 645    /// Protected implementation of Dispose pattern.
 646    /// </summary>
 647    /// <param name="disposing"><c>True</c> to dispose the managed state.</param>
 648    protected virtual void Dispose(bool disposing)
 649    {
 55650        if (!_disposed)
 651        {
 55652            if (disposing)
 653            {
 55654                _configurationManager.NamedConfigurationUpdated -= ConfigurationUpdated;
 55655                NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged;
 55656                NetworkChange.NetworkAvailabilityChanged -= OnNetworkAvailabilityChanged;
 657            }
 658
 55659            _disposed = true;
 660        }
 55661    }
 662
 663    /// <inheritdoc/>
 664    public bool TryParseInterface(string intf, [NotNullWhen(true)] out IReadOnlyList<IPData>? result)
 665    {
 15666        if (string.IsNullOrEmpty(intf)
 15667            || _interfaces is null
 15668            || _interfaces.Count == 0)
 669        {
 0670            result = null;
 0671            return false;
 672        }
 673
 674        // Match all interfaces starting with names starting with token
 15675        result = _interfaces
 15676            .Where(i => i.Name.Equals(intf, StringComparison.OrdinalIgnoreCase)
 15677                        && ((IsIPv4Enabled && i.Address.AddressFamily == AddressFamily.InterNetwork)
 15678                            || (IsIPv6Enabled && i.Address.AddressFamily == AddressFamily.InterNetworkV6)))
 15679            .OrderBy(x => x.Index)
 15680            .ToArray();
 15681        return result.Count > 0;
 682    }
 683
 684    /// <inheritdoc/>
 685    public bool HasRemoteAccess(IPAddress remoteIP)
 686    {
 6687        var config = _configurationManager.GetNetworkConfiguration();
 6688        if (config.EnableRemoteAccess)
 689        {
 690            // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect r
 691            // If left blank, all remote addresses will be allowed.
 6692            if (_remoteAddressFilter.Any() && !_lanSubnets.Any(x => x.Contains(remoteIP)))
 693            {
 694                // remoteAddressFilter is a whitelist or blacklist.
 4695                var matches = _remoteAddressFilter.Count(remoteNetwork => remoteNetwork.Contains(remoteIP));
 4696                if ((!config.IsRemoteIPFilterBlacklist && matches > 0)
 4697                    || (config.IsRemoteIPFilterBlacklist && matches == 0))
 698                {
 2699                    return true;
 700                }
 701
 2702                return false;
 703            }
 704        }
 0705        else if (!_lanSubnets.Any(x => x.Contains(remoteIP)))
 706        {
 707            // Remote not enabled. So everyone should be LAN.
 0708            return false;
 709        }
 710
 2711        return true;
 712    }
 713
 714    /// <inheritdoc/>
 715    public IReadOnlyList<PhysicalAddress> GetMacAddresses()
 716    {
 717        // Populated in construction - so always has values.
 0718        return _macAddresses;
 719    }
 720
 721    /// <inheritdoc/>
 722    public IReadOnlyList<IPData> GetLoopbacks()
 723    {
 0724        if (!IsIPv4Enabled && !IsIPv6Enabled)
 725        {
 0726            return Array.Empty<IPData>();
 727        }
 728
 0729        var loopbackNetworks = new List<IPData>();
 0730        if (IsIPv4Enabled)
 731        {
 0732            loopbackNetworks.Add(new IPData(IPAddress.Loopback, NetworkConstants.IPv4RFC5735Loopback, "lo"));
 733        }
 734
 0735        if (IsIPv6Enabled)
 736        {
 0737            loopbackNetworks.Add(new IPData(IPAddress.IPv6Loopback, NetworkConstants.IPv6RFC4291Loopback, "lo"));
 738        }
 739
 0740        return loopbackNetworks;
 741    }
 742
 743    /// <inheritdoc/>
 744    public IReadOnlyList<IPData> GetAllBindInterfaces(bool individualInterfaces = false)
 745    {
 22746        var config = _configurationManager.GetNetworkConfiguration();
 22747        var localNetworkAddresses = config.LocalNetworkAddresses;
 22748        if ((localNetworkAddresses.Length > 0 && !string.IsNullOrWhiteSpace(localNetworkAddresses[0]) && _interfaces.Cou
 749        {
 0750            return _interfaces;
 751        }
 752
 753        // No bind address and no exclusions, so listen on all interfaces.
 22754        var result = new List<IPData>();
 22755        if (IsIPv4Enabled && IsIPv6Enabled)
 756        {
 757            // Kestrel source code shows it uses Sockets.DualMode - so this also covers IPAddress.Any by default
 0758            result.Add(new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any));
 759        }
 22760        else if (IsIPv4Enabled)
 761        {
 22762            result.Add(new IPData(IPAddress.Any, NetworkConstants.IPv4Any));
 763        }
 0764        else if (IsIPv6Enabled)
 765        {
 766            // Cannot use IPv6Any as Kestrel will bind to IPv4 addresses too.
 0767            foreach (var iface in _interfaces)
 768            {
 0769                if (iface.AddressFamily == AddressFamily.InterNetworkV6)
 770                {
 0771                    result.Add(iface);
 772                }
 773            }
 774        }
 775
 22776        return result;
 777    }
 778
 779    /// <inheritdoc/>
 780    public string GetBindAddress(string source, out int? port)
 781    {
 23782        if (!NetworkUtils.TryParseHost(source, out var addresses, IsIPv4Enabled, IsIPv6Enabled))
 783        {
 4784            addresses = Array.Empty<IPAddress>();
 785        }
 786
 23787        var result = GetBindAddress(addresses.FirstOrDefault(), out port);
 23788        return result;
 789    }
 790
 791    /// <inheritdoc/>
 792    public string GetBindAddress(HttpRequest source, out int? port)
 793    {
 0794        var result = GetBindAddress(source.Host.Host, out port);
 0795        port ??= source.Host.Port;
 796
 0797        return result;
 798    }
 799
 800    /// <inheritdoc/>
 801    public string GetBindAddress(IPAddress? source, out int? port, bool skipOverrides = false)
 802    {
 23803        port = null;
 804
 805        string result;
 806
 23807        if (source is not null)
 808        {
 19809            if (IsIPv4Enabled && !IsIPv6Enabled && source.AddressFamily == AddressFamily.InterNetworkV6)
 810            {
 0811                _logger.LogWarning("IPv6 is disabled in Jellyfin, but enabled in the OS. This may affect how the interfa
 812            }
 813
 19814            if (!IsIPv4Enabled && IsIPv6Enabled && source.AddressFamily == AddressFamily.InterNetwork)
 815            {
 0816                _logger.LogWarning("IPv4 is disabled in Jellyfin, but enabled in the OS. This may affect how the interfa
 817            }
 818
 19819            bool isExternal = !_lanSubnets.Any(network => network.Contains(source));
 19820            _logger.LogDebug("Trying to get bind address for source {Source} - External: {IsExternal}", source, isExtern
 821
 19822            if (!skipOverrides && MatchesPublishedServerUrl(source, isExternal, out result))
 823            {
 6824                return result;
 825            }
 826
 827            // No preference given, so move on to bind addresses.
 13828            if (MatchesBindInterface(source, isExternal, out result))
 829            {
 11830                return result;
 831            }
 832
 2833            if (isExternal && MatchesExternalInterface(source, out result))
 834            {
 0835                return result;
 836            }
 837        }
 838
 839        // Get the first LAN interface address that's not excluded and not a loopback address.
 840        // Get all available interfaces, prefer local interfaces
 6841        var availableInterfaces = _interfaces.Where(x => !IPAddress.IsLoopback(x.Address))
 6842            .OrderByDescending(x => IsInLocalNetwork(x.Address))
 6843            .ThenBy(x => x.Index)
 6844            .ToList();
 845
 6846        if (availableInterfaces.Count == 0)
 847        {
 848            // There isn't any others, so we'll use the loopback.
 0849            result = IsIPv4Enabled && !IsIPv6Enabled ? "127.0.0.1" : "::1";
 0850            _logger.LogWarning("{Source}: Only loopback {Result} returned, using that as bind address.", source, result)
 0851            return result;
 852        }
 853
 854        // If no source address is given, use the preferred (first) interface
 6855        if (source is null)
 856        {
 4857            result = NetworkUtils.FormatIPString(availableInterfaces.First().Address);
 4858            _logger.LogDebug("{Source}: Using first internal interface as bind address: {Result}", source, result);
 4859            return result;
 860        }
 861
 862        // Does the request originate in one of the interface subnets?
 863        // (For systems with multiple internal network cards, and multiple subnets)
 8864        foreach (var intf in availableInterfaces)
 865        {
 2866            if (intf.Subnet.Contains(source))
 867            {
 0868                result = NetworkUtils.FormatIPString(intf.Address);
 0869                _logger.LogDebug("{Source}: Found interface with matching subnet, using it as bind address: {Result}", s
 0870                return result;
 871            }
 872        }
 873
 874        // Fallback to first available interface
 2875        result = NetworkUtils.FormatIPString(availableInterfaces[0].Address);
 2876        _logger.LogDebug("{Source}: No matching interfaces found, using preferred interface as bind address: {Result}", 
 2877        return result;
 0878    }
 879
 880    /// <inheritdoc/>
 881    public IReadOnlyList<IPData> GetInternalBindAddresses()
 882    {
 883        // Select all local bind addresses
 6884        return _interfaces.Where(x => IsInLocalNetwork(x.Address))
 6885            .OrderBy(x => x.Index)
 6886            .ToList();
 887    }
 888
 889    /// <inheritdoc/>
 890    public bool IsInLocalNetwork(string address)
 891    {
 0892        if (NetworkUtils.TryParseToSubnet(address, out var subnet))
 893        {
 0894            return IPAddress.IsLoopback(subnet.Prefix) || (_lanSubnets.Any(x => x.Contains(subnet.Prefix)) && !_excluded
 895        }
 896
 0897        if (NetworkUtils.TryParseHost(address, out var addresses, IsIPv4Enabled, IsIPv6Enabled))
 898        {
 0899            foreach (var ept in addresses)
 900            {
 0901                if (IPAddress.IsLoopback(ept) || (_lanSubnets.Any(x => x.Contains(ept)) && !_excludedSubnets.Any(x => x.
 902                {
 0903                    return true;
 904                }
 905            }
 906        }
 907
 0908        return false;
 909    }
 910
 911    /// <summary>
 912    ///  Get if the IPAddress is Link-local.
 913    /// </summary>
 914    /// <param name="address">The IP Address.</param>
 915    /// <returns>Bool indicates if the address is link-local.</returns>
 916    public bool IsLinkLocalAddress(IPAddress address)
 917    {
 0918        ArgumentNullException.ThrowIfNull(address);
 0919        return NetworkConstants.IPv4RFC3927LinkLocal.Contains(address) || address.IsIPv6LinkLocal;
 920    }
 921
 922    /// <inheritdoc/>
 923    public bool IsInLocalNetwork(IPAddress address)
 924    {
 150925        ArgumentNullException.ThrowIfNull(address);
 926
 927        // Map IPv6 mapped IPv4 back to IPv4 (happens if Kestrel runs in dual-socket mode)
 150928        if (address.IsIPv4MappedToIPv6)
 929        {
 0930            address = address.MapToIPv4();
 931        }
 932
 150933        if ((TrustAllIPv6Interfaces && address.AddressFamily == AddressFamily.InterNetworkV6)
 150934            || IPAddress.IsLoopback(address))
 935        {
 97936            return true;
 937        }
 938
 939        // As private addresses can be redefined by Configuration.LocalNetworkAddresses
 53940        return CheckIfLanAndNotExcluded(address);
 941    }
 942
 943    private bool CheckIfLanAndNotExcluded(IPAddress address)
 944    {
 258945        foreach (var lanSubnet in _lanSubnets)
 946        {
 91947            if (lanSubnet.Contains(address))
 948            {
 66949                foreach (var excludedSubnet in _excludedSubnets)
 950                {
 4951                    if (excludedSubnet.Contains(address))
 952                    {
 2953                        return false;
 954                    }
 955                }
 956
 28957                return true;
 958            }
 959        }
 960
 23961        return false;
 30962    }
 963
 964    /// <summary>
 965    /// Attempts to match the source against the published server URL overrides.
 966    /// </summary>
 967    /// <param name="source">IP source address to use.</param>
 968    /// <param name="isInExternalSubnet">True if the source is in an external subnet.</param>
 969    /// <param name="bindPreference">The published server URL that matches the source address.</param>
 970    /// <returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
 971    private bool MatchesPublishedServerUrl(IPAddress source, bool isInExternalSubnet, out string bindPreference)
 972    {
 19973        bindPreference = string.Empty;
 19974        int? port = null;
 975
 976        // Only consider subnets including the source IP, prefering specific overrides
 977        List<PublishedServerUriOverride> validPublishedServerUrls;
 19978        if (!isInExternalSubnet)
 979        {
 980            // Only use matching internal subnets
 981            // Prefer more specific (bigger subnet prefix) overrides
 10982            validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsInternalOverride && x.Data.Subnet.Contains(so
 10983                .OrderByDescending(x => x.Data.Subnet.PrefixLength)
 10984                .ToList();
 985        }
 986        else
 987        {
 988            // Only use matching external subnets
 989            // Prefer more specific (bigger subnet prefix) overrides
 9990            validPublishedServerUrls = _publishedServerUrls.Where(x => x.IsExternalOverride && x.Data.Subnet.Contains(so
 9991                .OrderByDescending(x => x.Data.Subnet.PrefixLength)
 9992                .ToList();
 993        }
 994
 44995        foreach (var data in validPublishedServerUrls)
 996        {
 997            // Get interface matching override subnet
 6998            var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(x => data.Data.Subnet.Contains(x.Address));
 999
 61000            if (intf?.Address is not null)
 1001            {
 1002                // If matching interface is found, use override
 61003                bindPreference = data.OverrideUri;
 61004                break;
 1005            }
 1006        }
 1007
 191008        if (string.IsNullOrEmpty(bindPreference))
 1009        {
 131010            _logger.LogDebug("{Source}: No matching bind address override found", source);
 131011            return false;
 1012        }
 1013
 1014        // Handle override specifying port
 61015        var parts = bindPreference.Split(':');
 61016        if (parts.Length > 1)
 1017        {
 51018            if (int.TryParse(parts[1], out int p))
 1019            {
 01020                bindPreference = parts[0];
 01021                port = p;
 01022                _logger.LogDebug("{Source}: Matching bind address override found: {Address}:{Port}", source, bindPrefere
 01023                return true;
 1024            }
 1025        }
 1026
 61027        _logger.LogDebug("{Source}: Matching bind address override found: {Address}", source, bindPreference);
 61028        return true;
 1029    }
 1030
 1031    /// <summary>
 1032    /// Attempts to match the source against the user defined bind interfaces.
 1033    /// </summary>
 1034    /// <param name="source">IP source address to use.</param>
 1035    /// <param name="isInExternalSubnet">True if the source is in the external subnet.</param>
 1036    /// <param name="result">The result, if a match is found.</param>
 1037    /// <returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
 1038    private bool MatchesBindInterface(IPAddress source, bool isInExternalSubnet, out string result)
 1039    {
 131040        result = string.Empty;
 1041
 131042        int count = _interfaces.Count;
 131043        if (count == 1 && (_interfaces[0].Address.Equals(IPAddress.Any) || _interfaces[0].Address.Equals(IPAddress.IPv6A
 1044        {
 1045            // Ignore IPAny addresses.
 01046            count = 0;
 1047        }
 1048
 131049        if (count == 0)
 1050        {
 01051            return false;
 1052        }
 1053
 131054        IPAddress? bindAddress = null;
 131055        if (isInExternalSubnet)
 1056        {
 51057            var externalInterfaces = _interfaces.Where(x => !IsInLocalNetwork(x.Address))
 51058                .OrderBy(x => x.Index)
 51059                .ToList();
 51060            if (externalInterfaces.Count > 0)
 1061            {
 1062                // Check to see if any of the external bind interfaces are in the same subnet as the source.
 1063                // If none exists, this will select the first external interface if there is one.
 41064                bindAddress = externalInterfaces
 41065                    .OrderByDescending(x => x.Subnet.Contains(source))
 41066                    .ThenBy(x => x.Index)
 41067                    .Select(x => x.Address)
 41068                    .First();
 1069
 41070                result = NetworkUtils.FormatIPString(bindAddress);
 41071                _logger.LogDebug("{Source}: External request received, matching external bind address found: {Result}", 
 41072                return true;
 1073            }
 1074
 11075            _logger.LogDebug("{Source}: External request received, no matching external bind address found, trying inter
 1076        }
 1077        else
 1078        {
 1079            // Check to see if any of the internal bind interfaces are in the same subnet as the source.
 1080            // If none exists, this will select the first internal interface if there is one.
 81081            bindAddress = _interfaces.Where(x => IsInLocalNetwork(x.Address))
 81082                .OrderByDescending(x => x.Subnet.Contains(source))
 81083                .ThenBy(x => x.Index)
 81084                .Select(x => x.Address)
 81085                .FirstOrDefault();
 1086
 81087            if (bindAddress is not null)
 1088            {
 71089                result = NetworkUtils.FormatIPString(bindAddress);
 71090                _logger.LogDebug("{Source}: Internal request received, matching internal bind address found: {Result}", 
 71091                return true;
 1092            }
 1093        }
 1094
 21095        return false;
 1096    }
 1097
 1098    /// <summary>
 1099    /// Attempts to match the source against external interfaces.
 1100    /// </summary>
 1101    /// <param name="source">IP source address to use.</param>
 1102    /// <param name="result">The result, if a match is found.</param>
 1103    /// <returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
 1104    private bool MatchesExternalInterface(IPAddress source, out string result)
 1105    {
 1106        // Get the first external interface address that isn't a loopback.
 11107        var extResult = _interfaces
 11108            .Where(p => !IsInLocalNetwork(p.Address))
 11109            .Where(p => p.Address.AddressFamily.Equals(source.AddressFamily))
 11110            .Where(p => !IsLinkLocalAddress(p.Address))
 11111            .OrderBy(x => x.Index).ToArray();
 1112
 1113        // No external interface found
 11114        if (extResult.Length == 0)
 1115        {
 11116            result = string.Empty;
 11117            _logger.LogDebug("{Source}: External request received, but no external interface found. Need to route throug
 11118            return false;
 1119        }
 1120
 1121        // Does the request originate in one of the interface subnets?
 1122        // (For systems with multiple network cards and/or multiple subnets)
 01123        foreach (var intf in extResult)
 1124        {
 01125            if (intf.Subnet.Contains(source))
 1126            {
 01127                result = NetworkUtils.FormatIPString(intf.Address);
 01128                _logger.LogDebug("{Source}: Found external interface with matching subnet, using it as bind address: {Re
 01129                return true;
 1130            }
 1131        }
 1132
 1133        // Fallback to first external interface.
 01134        result = NetworkUtils.FormatIPString(extResult[0].Address);
 01135        _logger.LogDebug("{Source}: Using first external interface as bind address: {Result}", source, result);
 01136        return true;
 1137    }
 1138
 1139    private void PrintNetworkInformation(NetworkConfiguration config, bool debug = true)
 1140    {
 771141        var logLevel = debug ? LogLevel.Debug : LogLevel.Information;
 771142        if (_logger.IsEnabled(logLevel))
 1143        {
 221144            _logger.Log(logLevel, "Defined LAN subnets: {Subnets}", _lanSubnets.Select(s => s.Prefix + "/" + s.PrefixLen
 221145            _logger.Log(logLevel, "Defined LAN exclusions: {Subnets}", _excludedSubnets.Select(s => s.Prefix + "/" + s.P
 221146            _logger.Log(logLevel, "Used LAN subnets: {Subnets}", _lanSubnets.Where(s => !_excludedSubnets.Contains(s)).S
 221147            _logger.Log(logLevel, "Filtered interface addresses: {Addresses}", _interfaces.OrderByDescending(x => x.Addr
 221148            _logger.Log(logLevel, "Bind Addresses {Addresses}", GetAllBindInterfaces(false).OrderByDescending(x => x.Add
 221149            _logger.Log(logLevel, "Remote IP filter is {Type}", config.IsRemoteIPFilterBlacklist ? "Blocklist" : "Allowl
 221150            _logger.Log(logLevel, "Filtered subnets: {Subnets}", _remoteAddressFilter.Select(s => s.Prefix + "/" + s.Pre
 1151        }
 771152    }
 1153}

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()
InitializeLan(MediaBrowser.Common.Net.NetworkConfiguration)
EnforceBindSettings(MediaBrowser.Common.Net.NetworkConfiguration)
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)
GetMacAddresses()
GetLoopbacks()
GetAllBindInterfaces(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)