NetworkManager.cs 43 KB

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