NetworkManager.cs 47 KB

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