NetworkManager.cs 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Net.NetworkInformation;
  7. using System.Net.Sockets;
  8. using System.Threading.Tasks;
  9. using Jellyfin.Networking.Configuration;
  10. using MediaBrowser.Common.Configuration;
  11. using MediaBrowser.Common.Net;
  12. using Microsoft.AspNetCore.Http;
  13. using Microsoft.AspNetCore.HttpOverrides;
  14. using Microsoft.Extensions.Logging;
  15. namespace Jellyfin.Networking.Manager
  16. {
  17. /// <summary>
  18. /// Class to take care of network interface management.
  19. /// </summary>
  20. public class NetworkManager : INetworkManager, IDisposable
  21. {
  22. /// <summary>
  23. /// Threading lock for network properties.
  24. /// </summary>
  25. private readonly object _initLock;
  26. /// <summary>
  27. /// List of all interface MAC addresses.
  28. /// </summary>
  29. private readonly List<PhysicalAddress> _macAddresses;
  30. private readonly ILogger<NetworkManager> _logger;
  31. private readonly IConfigurationManager _configurationManager;
  32. private readonly object _eventFireLock;
  33. /// <summary>
  34. /// Holds the published server URLs and the IPs to use them on.
  35. /// </summary>
  36. private readonly Dictionary<IPData, string> _publishedServerUrls;
  37. private List<IPNetwork> _remoteAddressFilter;
  38. /// <summary>
  39. /// Used to stop "event-racing conditions".
  40. /// </summary>
  41. private bool _eventfire;
  42. /// <summary>
  43. /// Dictionary containing interface addresses and their subnets.
  44. /// </summary>
  45. private List<IPData> _interfaces;
  46. /// <summary>
  47. /// Unfiltered user defined LAN subnets (<see cref="NetworkConfiguration.LocalNetworkSubnets"/>)
  48. /// or internal interface network subnets if undefined by user.
  49. /// </summary>
  50. private List<IPNetwork> _lanSubnets;
  51. /// <summary>
  52. /// User defined list of subnets to excluded from the LAN.
  53. /// </summary>
  54. private List<IPNetwork> _excludedSubnets;
  55. /// <summary>
  56. /// True if this object is disposed.
  57. /// </summary>
  58. private bool _disposed;
  59. /// <summary>
  60. /// Initializes a new instance of the <see cref="NetworkManager"/> class.
  61. /// </summary>
  62. /// <param name="configurationManager">IServerConfigurationManager instance.</param>
  63. /// <param name="logger">Logger to use for messages.</param>
  64. #pragma warning disable CS8618 // Non-nullable field is uninitialized. : Values are set in UpdateSettings function. Compiler doesn't yet recognise this.
  65. public NetworkManager(IConfigurationManager configurationManager, ILogger<NetworkManager> logger)
  66. {
  67. ArgumentNullException.ThrowIfNull(logger);
  68. ArgumentNullException.ThrowIfNull(configurationManager);
  69. _logger = logger;
  70. _configurationManager = configurationManager;
  71. _initLock = new();
  72. _interfaces = new List<IPData>();
  73. _macAddresses = new List<PhysicalAddress>();
  74. _publishedServerUrls = new Dictionary<IPData, string>();
  75. _eventFireLock = new object();
  76. _remoteAddressFilter = new List<IPNetwork>();
  77. UpdateSettings(_configurationManager.GetNetworkConfiguration());
  78. NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;
  79. NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
  80. _configurationManager.NamedConfigurationUpdated += ConfigurationUpdated;
  81. }
  82. #pragma warning restore CS8618 // Non-nullable field is uninitialized.
  83. /// <summary>
  84. /// Event triggered on network changes.
  85. /// </summary>
  86. public event EventHandler? NetworkChanged;
  87. /// <summary>
  88. /// Gets or sets a value indicating whether testing is taking place.
  89. /// </summary>
  90. public static string MockNetworkSettings { get; set; } = string.Empty;
  91. /// <summary>
  92. /// Gets a value indicating whether IP4 is enabled.
  93. /// </summary>
  94. public bool IsIpv4Enabled => _configurationManager.GetNetworkConfiguration().EnableIPV4;
  95. /// <summary>
  96. /// Gets a value indicating whether IP6 is enabled.
  97. /// </summary>
  98. public bool IsIpv6Enabled => _configurationManager.GetNetworkConfiguration().EnableIPV6;
  99. /// <summary>
  100. /// Gets a value indicating whether is all IPv6 interfaces are trusted as internal.
  101. /// </summary>
  102. public bool TrustAllIpv6Interfaces { get; private set; }
  103. /// <summary>
  104. /// Gets the Published server override list.
  105. /// </summary>
  106. public Dictionary<IPData, string> PublishedServerUrls => _publishedServerUrls;
  107. /// <inheritdoc/>
  108. public void Dispose()
  109. {
  110. Dispose(true);
  111. GC.SuppressFinalize(this);
  112. }
  113. /// <summary>
  114. /// Handler for network change events.
  115. /// </summary>
  116. /// <param name="sender">Sender.</param>
  117. /// <param name="e">A <see cref="NetworkAvailabilityEventArgs"/> containing network availability information.</param>
  118. private void OnNetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs e)
  119. {
  120. _logger.LogDebug("Network availability changed.");
  121. OnNetworkChanged();
  122. }
  123. /// <summary>
  124. /// Handler for network change events.
  125. /// </summary>
  126. /// <param name="sender">Sender.</param>
  127. /// <param name="e">An <see cref="EventArgs"/>.</param>
  128. private void OnNetworkAddressChanged(object? sender, EventArgs e)
  129. {
  130. _logger.LogDebug("Network address change detected.");
  131. OnNetworkChanged();
  132. }
  133. /// <summary>
  134. /// Triggers our event, and re-loads interface information.
  135. /// </summary>
  136. private void OnNetworkChanged()
  137. {
  138. lock (_eventFireLock)
  139. {
  140. if (!_eventfire)
  141. {
  142. _logger.LogDebug("Network Address Change Event.");
  143. // As network events tend to fire one after the other only fire once every second.
  144. _eventfire = true;
  145. OnNetworkChangeAsync().GetAwaiter().GetResult();
  146. }
  147. }
  148. }
  149. /// <summary>
  150. /// Async task that waits for 2 seconds before re-initialising the settings, as typically these events fire multiple times in succession.
  151. /// </summary>
  152. /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
  153. private async Task OnNetworkChangeAsync()
  154. {
  155. try
  156. {
  157. await Task.Delay(2000).ConfigureAwait(false);
  158. var networkConfig = _configurationManager.GetNetworkConfiguration();
  159. InitialiseLan(networkConfig);
  160. InitialiseInterfaces();
  161. EnforceBindSettings(networkConfig);
  162. NetworkChanged?.Invoke(this, EventArgs.Empty);
  163. }
  164. finally
  165. {
  166. _eventfire = false;
  167. }
  168. }
  169. /// <summary>
  170. /// Generate a list of all the interface ip addresses and submasks where that are in the active/unknown state.
  171. /// Generate a list of all active mac addresses that aren't loopback addresses.
  172. /// </summary>
  173. private void InitialiseInterfaces()
  174. {
  175. lock (_initLock)
  176. {
  177. _logger.LogDebug("Refreshing interfaces.");
  178. _interfaces.Clear();
  179. _macAddresses.Clear();
  180. try
  181. {
  182. var nics = NetworkInterface.GetAllNetworkInterfaces()
  183. .Where(i => i.SupportsMulticast && i.OperationalStatus == OperationalStatus.Up);
  184. foreach (NetworkInterface adapter in nics)
  185. {
  186. try
  187. {
  188. var ipProperties = adapter.GetIPProperties();
  189. var mac = adapter.GetPhysicalAddress();
  190. // Populate MAC list
  191. if (adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && PhysicalAddress.None.Equals(mac))
  192. {
  193. _macAddresses.Add(mac);
  194. }
  195. // Populate interface list
  196. foreach (var info in ipProperties.UnicastAddresses)
  197. {
  198. if (IsIpv4Enabled && info.Address.AddressFamily == AddressFamily.InterNetwork)
  199. {
  200. var interfaceObject = new IPData(info.Address, new IPNetwork(info.Address, info.PrefixLength), adapter.Name);
  201. interfaceObject.Index = ipProperties.GetIPv4Properties().Index;
  202. interfaceObject.Name = adapter.Name.ToLowerInvariant();
  203. _interfaces.Add(interfaceObject);
  204. }
  205. else if (IsIpv6Enabled && info.Address.AddressFamily == AddressFamily.InterNetworkV6)
  206. {
  207. var interfaceObject = new IPData(info.Address, new IPNetwork(info.Address, info.PrefixLength), adapter.Name);
  208. interfaceObject.Index = ipProperties.GetIPv6Properties().Index;
  209. interfaceObject.Name = adapter.Name.ToLowerInvariant();
  210. _interfaces.Add(interfaceObject);
  211. }
  212. }
  213. }
  214. #pragma warning disable CA1031 // Do not catch general exception types
  215. catch (Exception ex)
  216. #pragma warning restore CA1031 // Do not catch general exception types
  217. {
  218. // Ignore error, and attempt to continue.
  219. _logger.LogError(ex, "Error encountered parsing interfaces.");
  220. }
  221. }
  222. }
  223. #pragma warning disable CA1031 // Do not catch general exception types
  224. catch (Exception ex)
  225. #pragma warning restore CA1031 // Do not catch general exception types
  226. {
  227. _logger.LogError(ex, "Error obtaining interfaces.");
  228. }
  229. if (_interfaces.Count == 0)
  230. {
  231. _logger.LogWarning("No interface information available. Using loopback interface(s).");
  232. if (IsIpv4Enabled && !IsIpv6Enabled)
  233. {
  234. _interfaces.Add(new IPData(IPAddress.Loopback, new IPNetwork(IPAddress.Loopback, 8), "lo"));
  235. }
  236. if (!IsIpv4Enabled && IsIpv6Enabled)
  237. {
  238. _interfaces.Add(new IPData(IPAddress.IPv6Loopback, new IPNetwork(IPAddress.IPv6Loopback, 128), "lo"));
  239. }
  240. }
  241. _logger.LogDebug("Discovered {0} interfaces.", _interfaces.Count);
  242. _logger.LogDebug("Interfaces addresses : {0}", _interfaces.Select(s => s.Address).ToString());
  243. }
  244. }
  245. /// <summary>
  246. /// Initialises internal LAN cache.
  247. /// </summary>
  248. private void InitialiseLan(NetworkConfiguration config)
  249. {
  250. lock (_initLock)
  251. {
  252. _logger.LogDebug("Refreshing LAN information.");
  253. // Get configuration options
  254. string[] subnets = config.LocalNetworkSubnets;
  255. _ = NetworkExtensions.TryParseSubnets(subnets, out _lanSubnets, false);
  256. _ = NetworkExtensions.TryParseSubnets(subnets, out _excludedSubnets, true);
  257. if (_lanSubnets.Count == 0)
  258. {
  259. // If no LAN addresses are specified, all private subnets and Loopback are deemed to be the LAN
  260. _logger.LogDebug("Using LAN interface addresses as user provided no LAN details.");
  261. if (IsIpv6Enabled)
  262. {
  263. _lanSubnets.Add(new IPNetwork(IPAddress.IPv6Loopback, 128)); // RFC 4291 (Loopback)
  264. _lanSubnets.Add(new IPNetwork(IPAddress.Parse("fe80::"), 10)); // RFC 4291 (Site local)
  265. _lanSubnets.Add(new IPNetwork(IPAddress.Parse("fc00::"), 7)); // RFC 4193 (Unique local)
  266. }
  267. if (IsIpv4Enabled)
  268. {
  269. _lanSubnets.Add(new IPNetwork(IPAddress.Loopback, 8)); // RFC 5735 (Loopback)
  270. _lanSubnets.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8)); // RFC 1918 (private)
  271. _lanSubnets.Add(new IPNetwork(IPAddress.Parse("172.16.0.0"), 12)); // RFC 1918 (private)
  272. _lanSubnets.Add(new IPNetwork(IPAddress.Parse("192.168.0.0"), 16)); // RFC 1918 (private)
  273. }
  274. }
  275. _logger.LogInformation("Defined LAN addresses : {0}", _lanSubnets.Select(s => s.Prefix + "/" + s.PrefixLength));
  276. _logger.LogInformation("Defined LAN exclusions : {0}", _excludedSubnets.Select(s => s.Prefix + "/" + s.PrefixLength));
  277. _logger.LogInformation("Using LAN addresses: {0}", _lanSubnets.Where(s => !_excludedSubnets.Contains(s)).Select(s => s.Prefix + "/" + s.PrefixLength));
  278. }
  279. }
  280. /// <summary>
  281. /// Enforce bind addresses and exclusions on available interfaces.
  282. /// </summary>
  283. private void EnforceBindSettings(NetworkConfiguration config)
  284. {
  285. lock (_initLock)
  286. {
  287. // Respect explicit bind addresses
  288. var localNetworkAddresses = config.LocalNetworkAddresses;
  289. if (localNetworkAddresses.Length > 0 && !string.IsNullOrWhiteSpace(localNetworkAddresses.First()))
  290. {
  291. var bindAddresses = localNetworkAddresses.Select(p => IPAddress.TryParse(p, out var addresses)
  292. ? addresses
  293. : (_interfaces.Where(x => x.Name.Equals(p, StringComparison.OrdinalIgnoreCase))
  294. .Select(x => x.Address)
  295. .FirstOrDefault() ?? IPAddress.None))
  296. .ToList();
  297. bindAddresses.RemoveAll(x => x == IPAddress.None);
  298. _interfaces = _interfaces.Where(x => bindAddresses.Contains(x.Address)).ToList();
  299. if (bindAddresses.Contains(IPAddress.Loopback))
  300. {
  301. _interfaces.Add(new IPData(IPAddress.Loopback, new IPNetwork(IPAddress.Loopback, 8), "lo"));
  302. }
  303. if (bindAddresses.Contains(IPAddress.IPv6Loopback))
  304. {
  305. _interfaces.Add(new IPData(IPAddress.IPv6Loopback, new IPNetwork(IPAddress.IPv6Loopback, 128), "lo"));
  306. }
  307. }
  308. // Remove all interfaces matching any virtual machine interface prefix
  309. if (config.IgnoreVirtualInterfaces)
  310. {
  311. // Remove potentially exisiting * and split config string into prefixes
  312. var virtualInterfacePrefixes = config.VirtualInterfaceNames
  313. .Select(i => i.ToLowerInvariant()
  314. .Replace("*", string.Empty, StringComparison.OrdinalIgnoreCase));
  315. // Check all interfaces for matches against the prefixes and remove them
  316. if (_interfaces.Count > 0 && virtualInterfacePrefixes.Any())
  317. {
  318. foreach (var virtualInterfacePrefix in virtualInterfacePrefixes)
  319. {
  320. _interfaces.RemoveAll(x => x.Name.StartsWith(virtualInterfacePrefix, StringComparison.OrdinalIgnoreCase));
  321. }
  322. }
  323. }
  324. // Remove all IPv4 interfaces if IPv4 is disabled
  325. if (!IsIpv4Enabled)
  326. {
  327. _interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetwork);
  328. }
  329. // Remove all IPv6 interfaces if IPv6 is disabled
  330. if (!IsIpv6Enabled)
  331. {
  332. _interfaces.RemoveAll(x => x.AddressFamily == AddressFamily.InterNetworkV6);
  333. }
  334. _logger.LogInformation("Using bind addresses: {0}", _interfaces.Select(x => x.Address));
  335. }
  336. }
  337. /// <summary>
  338. /// Initialises the remote address values.
  339. /// </summary>
  340. private void InitialiseRemote(NetworkConfiguration config)
  341. {
  342. lock (_initLock)
  343. {
  344. // Parse config values into filter collection
  345. var remoteIPFilter = config.RemoteIPFilter;
  346. if (remoteIPFilter.Any() && !string.IsNullOrWhiteSpace(remoteIPFilter.First()))
  347. {
  348. // Parse all IPs with netmask to a subnet
  349. _ = NetworkExtensions.TryParseSubnets(remoteIPFilter.Where(x => x.Contains('/', StringComparison.OrdinalIgnoreCase)).ToArray(), out _remoteAddressFilter, false);
  350. // Parse everything else as an IP and construct subnet with a single IP
  351. var ips = remoteIPFilter.Where(x => !x.Contains('/', StringComparison.OrdinalIgnoreCase));
  352. foreach (var ip in ips)
  353. {
  354. if (IPAddress.TryParse(ip, out var ipp))
  355. {
  356. _remoteAddressFilter.Add(new IPNetwork(ipp, ipp.AddressFamily == AddressFamily.InterNetwork ? 32 : 128));
  357. }
  358. }
  359. }
  360. }
  361. }
  362. /// <summary>
  363. /// Parses the user defined overrides into the dictionary object.
  364. /// Overrides are the equivalent of localised publishedServerUrl, enabling
  365. /// different addresses to be advertised over different subnets.
  366. /// format is subnet=ipaddress|host|uri
  367. /// when subnet = 0.0.0.0, any external address matches.
  368. /// </summary>
  369. private void InitialiseOverrides(NetworkConfiguration config)
  370. {
  371. lock (_initLock)
  372. {
  373. _publishedServerUrls.Clear();
  374. string[] overrides = config.PublishedServerUriBySubnet;
  375. foreach (var entry in overrides)
  376. {
  377. var parts = entry.Split('=');
  378. if (parts.Length != 2)
  379. {
  380. _logger.LogError("Unable to parse bind override: {Entry}", entry);
  381. }
  382. else
  383. {
  384. var replacement = parts[1].Trim();
  385. var ipParts = parts[0].Split("/");
  386. if (string.Equals(parts[0], "all", StringComparison.OrdinalIgnoreCase))
  387. {
  388. _publishedServerUrls[new IPData(IPAddress.Broadcast, null)] = replacement;
  389. }
  390. else if (string.Equals(parts[0], "external", StringComparison.OrdinalIgnoreCase))
  391. {
  392. _publishedServerUrls[new IPData(IPAddress.Any, new IPNetwork(IPAddress.Any, 0))] = replacement;
  393. _publishedServerUrls[new IPData(IPAddress.IPv6Any, new IPNetwork(IPAddress.IPv6Any, 0))] = replacement;
  394. }
  395. else if (string.Equals(parts[0], "internal", StringComparison.OrdinalIgnoreCase))
  396. {
  397. foreach (var lan in _lanSubnets)
  398. {
  399. var lanPrefix = lan.Prefix;
  400. _publishedServerUrls[new IPData(lanPrefix, new IPNetwork(lanPrefix, lan.PrefixLength))] = replacement;
  401. }
  402. }
  403. else if (IPAddress.TryParse(ipParts[0], out IPAddress? result))
  404. {
  405. var data = new IPData(result, null);
  406. if (ipParts.Length > 1 && int.TryParse(ipParts[1], out var netmask))
  407. {
  408. data.Subnet = new IPNetwork(result, netmask);
  409. }
  410. _publishedServerUrls[data] = replacement;
  411. }
  412. else if (TryParseInterface(ipParts[0], out var ifaces))
  413. {
  414. foreach (var iface in ifaces)
  415. {
  416. _publishedServerUrls[iface] = replacement;
  417. }
  418. }
  419. else
  420. {
  421. _logger.LogError("Unable to parse bind override: {Entry}", entry);
  422. }
  423. }
  424. }
  425. }
  426. }
  427. private void ConfigurationUpdated(object? sender, ConfigurationUpdateEventArgs evt)
  428. {
  429. if (evt.Key.Equals("network", StringComparison.Ordinal))
  430. {
  431. UpdateSettings((NetworkConfiguration)evt.NewConfiguration);
  432. }
  433. }
  434. /// <summary>
  435. /// Reloads all settings and re-initialises the instance.
  436. /// </summary>
  437. /// <param name="configuration">The <see cref="NetworkConfiguration"/> to use.</param>
  438. public void UpdateSettings(object configuration)
  439. {
  440. ArgumentNullException.ThrowIfNull(configuration);
  441. var config = (NetworkConfiguration)configuration;
  442. InitialiseLan(config);
  443. InitialiseRemote(config);
  444. if (string.IsNullOrEmpty(MockNetworkSettings))
  445. {
  446. InitialiseInterfaces();
  447. }
  448. else // Used in testing only.
  449. {
  450. // Format is <IPAddress>,<Index>,<Name>: <next interface>. Set index to -ve to simulate a gateway.
  451. var interfaceList = MockNetworkSettings.Split('|');
  452. foreach (var details in interfaceList)
  453. {
  454. var parts = details.Split(',');
  455. var split = parts[0].Split("/");
  456. var address = IPAddress.Parse(split[0]);
  457. var network = new IPNetwork(address, int.Parse(split[1], CultureInfo.InvariantCulture));
  458. var index = int.Parse(parts[1], CultureInfo.InvariantCulture);
  459. if (address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.InterNetworkV6)
  460. {
  461. var data = new IPData(address, network, parts[2]);
  462. data.Index = index;
  463. _interfaces.Add(data);
  464. }
  465. }
  466. }
  467. EnforceBindSettings(config);
  468. InitialiseOverrides(config);
  469. }
  470. /// <summary>
  471. /// Protected implementation of Dispose pattern.
  472. /// </summary>
  473. /// <param name="disposing"><c>True</c> to dispose the managed state.</param>
  474. protected virtual void Dispose(bool disposing)
  475. {
  476. if (!_disposed)
  477. {
  478. if (disposing)
  479. {
  480. _configurationManager.NamedConfigurationUpdated -= ConfigurationUpdated;
  481. NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged;
  482. NetworkChange.NetworkAvailabilityChanged -= OnNetworkAvailabilityChanged;
  483. }
  484. _disposed = true;
  485. }
  486. }
  487. /// <inheritdoc/>
  488. public bool TryParseInterface(string intf, out List<IPData> result)
  489. {
  490. result = new List<IPData>();
  491. if (string.IsNullOrEmpty(intf))
  492. {
  493. return false;
  494. }
  495. if (_interfaces != null)
  496. {
  497. // Match all interfaces starting with names starting with token
  498. var matchedInterfaces = _interfaces.Where(s => s.Name.Equals(intf.ToLowerInvariant(), StringComparison.OrdinalIgnoreCase)).OrderBy(x => x.Index);
  499. if (matchedInterfaces.Any())
  500. {
  501. _logger.LogInformation("Interface {Token} used in settings. Using its interface addresses.", intf);
  502. // Use interface IP instead of name
  503. foreach (IPData iface in matchedInterfaces)
  504. {
  505. if ((IsIpv4Enabled && iface.Address.AddressFamily == AddressFamily.InterNetwork)
  506. || (IsIpv6Enabled && iface.Address.AddressFamily == AddressFamily.InterNetworkV6))
  507. {
  508. result.Add(iface);
  509. }
  510. }
  511. return true;
  512. }
  513. }
  514. return false;
  515. }
  516. /// <inheritdoc/>
  517. public bool HasRemoteAccess(IPAddress remoteIp)
  518. {
  519. var config = _configurationManager.GetNetworkConfiguration();
  520. if (config.EnableRemoteAccess)
  521. {
  522. // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely.
  523. // If left blank, all remote addresses will be allowed.
  524. if (_remoteAddressFilter.Any() && !_lanSubnets.Any(x => x.Contains(remoteIp)))
  525. {
  526. // remoteAddressFilter is a whitelist or blacklist.
  527. var matches = _remoteAddressFilter.Count(remoteNetwork => remoteNetwork.Contains(remoteIp));
  528. if ((!config.IsRemoteIPFilterBlacklist && matches > 0)
  529. || (config.IsRemoteIPFilterBlacklist && matches == 0))
  530. {
  531. return true;
  532. }
  533. return false;
  534. }
  535. }
  536. else if (!_lanSubnets.Where(x => x.Contains(remoteIp)).Any())
  537. {
  538. // Remote not enabled. So everyone should be LAN.
  539. return false;
  540. }
  541. return true;
  542. }
  543. /// <inheritdoc/>
  544. public IReadOnlyList<PhysicalAddress> GetMacAddresses()
  545. {
  546. // Populated in construction - so always has values.
  547. return _macAddresses;
  548. }
  549. /// <inheritdoc/>
  550. public IReadOnlyList<IPData> GetLoopbacks()
  551. {
  552. var loopbackNetworks = new List<IPData>();
  553. if (IsIpv4Enabled)
  554. {
  555. loopbackNetworks.Add(new IPData(IPAddress.Loopback, new IPNetwork(IPAddress.Loopback, 8), "lo"));
  556. }
  557. if (IsIpv6Enabled)
  558. {
  559. loopbackNetworks.Add(new IPData(IPAddress.IPv6Loopback, new IPNetwork(IPAddress.IPv6Loopback, 128), "lo"));
  560. }
  561. return loopbackNetworks;
  562. }
  563. /// <inheritdoc/>
  564. public IReadOnlyList<IPData> GetAllBindInterfaces(bool individualInterfaces = false)
  565. {
  566. if (_interfaces.Count == 0)
  567. {
  568. // No bind address and no exclusions, so listen on all interfaces.
  569. var result = new List<IPData>();
  570. if (individualInterfaces)
  571. {
  572. foreach (var iface in _interfaces)
  573. {
  574. result.Add(iface);
  575. }
  576. return result;
  577. }
  578. if (IsIpv4Enabled && IsIpv6Enabled)
  579. {
  580. // Kestrel source code shows it uses Sockets.DualMode - so this also covers IPAddress.Any by default
  581. result.Add(new IPData(IPAddress.IPv6Any, new IPNetwork(IPAddress.IPv6Any, 0)));
  582. }
  583. else if (IsIpv4Enabled)
  584. {
  585. result.Add(new IPData(IPAddress.Any, new IPNetwork(IPAddress.Any, 0)));
  586. }
  587. else if (IsIpv6Enabled)
  588. {
  589. // Cannot use IPv6Any as Kestrel will bind to IPv4 addresses too.
  590. foreach (var iface in _interfaces)
  591. {
  592. if (iface.AddressFamily == AddressFamily.InterNetworkV6)
  593. {
  594. result.Add(iface);
  595. }
  596. }
  597. }
  598. return result;
  599. }
  600. return _interfaces;
  601. }
  602. /// <inheritdoc/>
  603. public string GetBindInterface(string source, out int? port)
  604. {
  605. _ = NetworkExtensions.TryParseHost(source, out var address, IsIpv4Enabled, IsIpv6Enabled);
  606. var result = GetBindInterface(address.FirstOrDefault(), out port);
  607. return result;
  608. }
  609. /// <inheritdoc/>
  610. public string GetBindInterface(HttpRequest source, out int? port)
  611. {
  612. string result;
  613. _ = NetworkExtensions.TryParseHost(source.Host.Host, out var addresses, IsIpv4Enabled, IsIpv6Enabled);
  614. result = GetBindInterface(addresses.FirstOrDefault(), out port);
  615. port ??= source.Host.Port;
  616. return result;
  617. }
  618. /// <inheritdoc/>
  619. public string GetBindInterface(IPAddress? source, out int? port)
  620. {
  621. port = null;
  622. string result;
  623. if (source != null)
  624. {
  625. if (IsIpv4Enabled && !IsIpv6Enabled && source.AddressFamily == AddressFamily.InterNetworkV6)
  626. {
  627. _logger.LogWarning("IPv6 is disabled in Jellyfin, but enabled in the OS. This may affect how the interface is selected.");
  628. }
  629. if (!IsIpv4Enabled && IsIpv6Enabled && source.AddressFamily == AddressFamily.InterNetwork)
  630. {
  631. _logger.LogWarning("IPv4 is disabled in Jellyfin, but enabled in the OS. This may affect how the interface is selected.");
  632. }
  633. bool isExternal = !_lanSubnets.Any(network => network.Contains(source));
  634. _logger.LogDebug("GetBindInterface with source. External: {IsExternal}:", isExternal);
  635. if (MatchesPublishedServerUrl(source, isExternal, out string res, out port))
  636. {
  637. if (port != null)
  638. {
  639. _logger.LogInformation("{Source}: Using BindAddress {Address}:{Port}", source, res, port);
  640. }
  641. else
  642. {
  643. _logger.LogInformation("{Source}: Using BindAddress {Address}", source, res);
  644. }
  645. return res;
  646. }
  647. // No preference given, so move on to bind addresses.
  648. if (MatchesBindInterface(source, isExternal, out result))
  649. {
  650. return result;
  651. }
  652. if (isExternal && MatchesExternalInterface(source, out result))
  653. {
  654. return result;
  655. }
  656. }
  657. // Get the first LAN interface address that's not excluded and not a loopback address.
  658. var availableInterfaces = _interfaces.Where(x => !IPAddress.IsLoopback(x.Address))
  659. .OrderByDescending(x => IsInLocalNetwork(x.Address))
  660. .ThenBy(x => x.Index);
  661. if (availableInterfaces.Any())
  662. {
  663. if (source != null)
  664. {
  665. foreach (var intf in availableInterfaces)
  666. {
  667. if (intf.Address.Equals(source))
  668. {
  669. result = NetworkExtensions.FormatIpString(intf.Address);
  670. _logger.LogDebug("{Source}: GetBindInterface: Has found matching interface: {Result}", source, result);
  671. return result;
  672. }
  673. }
  674. // Does the request originate in one of the interface subnets?
  675. // (For systems with multiple internal network cards, and multiple subnets)
  676. foreach (var intf in availableInterfaces)
  677. {
  678. if (intf.Subnet.Contains(source))
  679. {
  680. result = NetworkExtensions.FormatIpString(intf.Address);
  681. _logger.LogDebug("{Source}: GetBindInterface: Has source, matched best internal interface on range: {Result}", source, result);
  682. return result;
  683. }
  684. }
  685. }
  686. result = NetworkExtensions.FormatIpString(availableInterfaces.First().Address);
  687. _logger.LogDebug("{Source}: GetBindInterface: Matched first internal interface: {Result}", source, result);
  688. return result;
  689. }
  690. // There isn't any others, so we'll use the loopback.
  691. result = IsIpv4Enabled && !IsIpv6Enabled ? "127.0.0.1" : "::1";
  692. _logger.LogWarning("{Source}: GetBindInterface: Loopback {Result} returned.", source, result);
  693. return result;
  694. }
  695. /// <inheritdoc/>
  696. public IReadOnlyList<IPData> GetInternalBindAddresses()
  697. {
  698. // Select all local bind addresses
  699. return _interfaces.Where(x => IsInLocalNetwork(x.Address))
  700. .OrderBy(x => x.Index)
  701. .ToList();
  702. }
  703. /// <inheritdoc/>
  704. public bool IsInLocalNetwork(string address)
  705. {
  706. if (IPAddress.TryParse(address, out var ep))
  707. {
  708. return IPAddress.IsLoopback(ep) || (_lanSubnets.Any(x => x.Contains(ep)) && !_excludedSubnets.Any(x => x.Contains(ep)));
  709. }
  710. if (NetworkExtensions.TryParseHost(address, out var addresses, IsIpv4Enabled, IsIpv6Enabled))
  711. {
  712. bool match = false;
  713. foreach (var ept in addresses)
  714. {
  715. match |= IPAddress.IsLoopback(ept) || (_lanSubnets.Any(x => x.Contains(ept)) && !_excludedSubnets.Any(x => x.Contains(ept)));
  716. }
  717. return match;
  718. }
  719. return false;
  720. }
  721. /// <inheritdoc/>
  722. public bool IsInLocalNetwork(IPAddress address)
  723. {
  724. ArgumentNullException.ThrowIfNull(address);
  725. // See conversation at https://github.com/jellyfin/jellyfin/pull/3515.
  726. if (TrustAllIpv6Interfaces && address.AddressFamily == AddressFamily.InterNetworkV6)
  727. {
  728. return true;
  729. }
  730. // As private addresses can be redefined by Configuration.LocalNetworkAddresses
  731. var match = CheckIfLanAndNotExcluded(address);
  732. return address.Equals(IPAddress.Loopback) || address.Equals(IPAddress.IPv6Loopback) || match;
  733. }
  734. private bool CheckIfLanAndNotExcluded(IPAddress address)
  735. {
  736. bool match = false;
  737. foreach (var lanSubnet in _lanSubnets)
  738. {
  739. match |= lanSubnet.Contains(address);
  740. }
  741. foreach (var excludedSubnet in _excludedSubnets)
  742. {
  743. match &= !excludedSubnet.Contains(address);
  744. }
  745. NetworkExtensions.IsIPv6LinkLocal(address);
  746. return match;
  747. }
  748. /// <summary>
  749. /// Attempts to match the source against the published server URL overrides.
  750. /// </summary>
  751. /// <param name="source">IP source address to use.</param>
  752. /// <param name="isInExternalSubnet">True if the source is in an external subnet.</param>
  753. /// <param name="bindPreference">The published server URL that matches the source address.</param>
  754. /// <param name="port">The resultant port, if one exists.</param>
  755. /// <returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
  756. private bool MatchesPublishedServerUrl(IPAddress source, bool isInExternalSubnet, out string bindPreference, out int? port)
  757. {
  758. bindPreference = string.Empty;
  759. port = null;
  760. var validPublishedServerUrls = _publishedServerUrls.Where(x => x.Key.Address.Equals(IPAddress.Any)).ToList();
  761. validPublishedServerUrls.AddRange(_publishedServerUrls.Where(x => x.Key.Address.Equals(IPAddress.IPv6Any)));
  762. validPublishedServerUrls.AddRange(_publishedServerUrls.Where(x => x.Key.Subnet.Contains(source)));
  763. validPublishedServerUrls = validPublishedServerUrls.GroupBy(x => x.Key).Select(y => y.First()).ToList();
  764. // Check for user override.
  765. foreach (var data in validPublishedServerUrls)
  766. {
  767. // Get address interface
  768. var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(s => s.Subnet.Contains(data.Key.Address));
  769. // Remaining. Match anything.
  770. if (data.Key.Address.Equals(IPAddress.Broadcast))
  771. {
  772. bindPreference = data.Value;
  773. break;
  774. }
  775. else if ((data.Key.Address.Equals(IPAddress.Any) || data.Key.Address.Equals(IPAddress.IPv6Any)) && isInExternalSubnet)
  776. {
  777. // External.
  778. bindPreference = data.Value;
  779. break;
  780. }
  781. else if (intf?.Address != null)
  782. {
  783. // Match ip address.
  784. bindPreference = data.Value;
  785. break;
  786. }
  787. }
  788. if (string.IsNullOrEmpty(bindPreference))
  789. {
  790. return false;
  791. }
  792. // Has it got a port defined?
  793. var parts = bindPreference.Split(':');
  794. if (parts.Length > 1)
  795. {
  796. if (int.TryParse(parts[1], out int p))
  797. {
  798. bindPreference = parts[0];
  799. port = p;
  800. }
  801. }
  802. return true;
  803. }
  804. /// <summary>
  805. /// Attempts to match the source against a user defined bind interface.
  806. /// </summary>
  807. /// <param name="source">IP source address to use.</param>
  808. /// <param name="isInExternalSubnet">True if the source is in the external subnet.</param>
  809. /// <param name="result">The result, if a match is found.</param>
  810. /// <returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
  811. private bool MatchesBindInterface(IPAddress source, bool isInExternalSubnet, out string result)
  812. {
  813. result = string.Empty;
  814. int count = _interfaces.Count;
  815. if (count == 1 && (_interfaces[0].Equals(IPAddress.Any) || _interfaces[0].Equals(IPAddress.IPv6Any)))
  816. {
  817. // Ignore IPAny addresses.
  818. count = 0;
  819. }
  820. if (count > 0)
  821. {
  822. IPAddress? bindAddress = null;
  823. var externalInterfaces = _interfaces.Where(x => !IsInLocalNetwork(x.Address))
  824. .OrderBy(x => x.Index)
  825. .ToList();
  826. if (isInExternalSubnet)
  827. {
  828. if (externalInterfaces.Any())
  829. {
  830. // Check to see if any of the external bind interfaces are in the same subnet as the source.
  831. // If none exists, this will select the first external interface if there is one.
  832. bindAddress = externalInterfaces
  833. .OrderByDescending(x => x.Subnet.Contains(source))
  834. .ThenBy(x => x.Index)
  835. .Select(x => x.Address)
  836. .FirstOrDefault();
  837. if (bindAddress != null)
  838. {
  839. result = NetworkExtensions.FormatIpString(bindAddress);
  840. _logger.LogDebug("{Source}: GetBindInterface: Has source, found a matching external bind interface: {Result}", source, result);
  841. return true;
  842. }
  843. }
  844. _logger.LogWarning("{Source}: External request received, no external interface bind found, trying internal interfaces.", source);
  845. }
  846. else
  847. {
  848. // Check to see if any of the internal bind interfaces are in the same subnet as the source.
  849. // If none exists, this will select the first internal interface if there is one.
  850. bindAddress = _interfaces.Where(x => IsInLocalNetwork(x.Address))
  851. .OrderByDescending(x => x.Subnet.Contains(source))
  852. .ThenBy(x => x.Index)
  853. .Select(x => x.Address)
  854. .FirstOrDefault();
  855. if (bindAddress != null)
  856. {
  857. result = NetworkExtensions.FormatIpString(bindAddress);
  858. _logger.LogWarning("{Source}: Request received, matching internal interface bind found: {Result}", source, result);
  859. return true;
  860. }
  861. }
  862. }
  863. return false;
  864. }
  865. /// <summary>
  866. /// Attempts to match the source against an external interface.
  867. /// </summary>
  868. /// <param name="source">IP source address to use.</param>
  869. /// <param name="result">The result, if a match is found.</param>
  870. /// <returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
  871. private bool MatchesExternalInterface(IPAddress source, out string result)
  872. {
  873. result = string.Empty;
  874. // Get the first WAN interface address that isn't a loopback.
  875. var extResult = _interfaces.Where(p => !IsInLocalNetwork(p.Address)).OrderBy(x => x.Index);
  876. IPAddress? hasResult = null;
  877. // Does the request originate in one of the interface subnets?
  878. // (For systems with multiple internal network cards, and multiple subnets)
  879. foreach (var intf in extResult)
  880. {
  881. hasResult ??= intf.Address;
  882. if (!IsInLocalNetwork(intf.Address) && intf.Subnet.Contains(source))
  883. {
  884. result = NetworkExtensions.FormatIpString(intf.Address);
  885. _logger.LogDebug("{Source}: GetBindInterface: Selected best external on interface on range: {Result}", source, result);
  886. return true;
  887. }
  888. }
  889. if (hasResult != null)
  890. {
  891. result = NetworkExtensions.FormatIpString(hasResult);
  892. _logger.LogDebug("{Source}: GetBindInterface: Selected first external interface: {Result}", source, result);
  893. return true;
  894. }
  895. _logger.LogDebug("{Source}: External request received, but no WAN interface found. Need to route through internal network.", source);
  896. return false;
  897. }
  898. }
  899. }