NetworkManager.cs 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295
  1. #pragma warning disable CA1021 // Avoid out parameters
  2. using System;
  3. using System.Collections.Generic;
  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.Tasks;
  10. using Jellyfin.Networking.Configuration;
  11. using MediaBrowser.Common.Configuration;
  12. using MediaBrowser.Common.Net;
  13. using Microsoft.AspNetCore.Http;
  14. using Microsoft.Extensions.Logging;
  15. using NetCollection = System.Collections.ObjectModel.Collection<MediaBrowser.Common.Net.IPObject>;
  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. /// Contains the description of the interface along with its index.
  25. /// </summary>
  26. private readonly Dictionary<string, int> _interfaceNames;
  27. /// <summary>
  28. /// Threading lock for network properties.
  29. /// </summary>
  30. private readonly object _intLock = new object();
  31. /// <summary>
  32. /// List of all interface addresses and masks.
  33. /// </summary>
  34. private readonly NetCollection _interfaceAddresses;
  35. /// <summary>
  36. /// List of all interface MAC addresses.
  37. /// </summary>
  38. private readonly List<PhysicalAddress> _macAddresses;
  39. private readonly ILogger<NetworkManager> _logger;
  40. private readonly IConfigurationManager _configurationManager;
  41. private readonly object _eventFireLock;
  42. /// <summary>
  43. /// Holds the bind address overrides.
  44. /// </summary>
  45. private readonly Dictionary<IPNetAddress, string> _publishedServerUrls;
  46. /// <summary>
  47. /// Used to stop "event-racing conditions".
  48. /// </summary>
  49. private bool _eventfire;
  50. /// <summary>
  51. /// Unfiltered user defined LAN subnets. (Configuration.LocalNetworkSubnets).
  52. /// or internal interface network subnets if undefined by user.
  53. /// </summary>
  54. private NetCollection _lanSubnets;
  55. /// <summary>
  56. /// User defined list of subnets to excluded from the LAN.
  57. /// </summary>
  58. private NetCollection _excludedSubnets;
  59. /// <summary>
  60. /// List of interface addresses to bind the WS.
  61. /// </summary>
  62. private NetCollection _bindAddresses;
  63. /// <summary>
  64. /// List of interface addresses to exclude from bind.
  65. /// </summary>
  66. private NetCollection _bindExclusions;
  67. /// <summary>
  68. /// Caches list of all internal filtered interface addresses and masks.
  69. /// </summary>
  70. private NetCollection _internalInterfaces;
  71. /// <summary>
  72. /// Flag set when no custom LAN has been defined in the config.
  73. /// </summary>
  74. private bool _usingPrivateAddresses;
  75. /// <summary>
  76. /// True if this object is disposed.
  77. /// </summary>
  78. private bool _disposed;
  79. /// <summary>
  80. /// Initializes a new instance of the <see cref="NetworkManager"/> class.
  81. /// </summary>
  82. /// <param name="configurationManager">IServerConfigurationManager instance.</param>
  83. /// <param name="logger">Logger to use for messages.</param>
  84. #pragma warning disable CS8618 // Non-nullable field is uninitialized. : Values are set in UpdateSettings function. Compiler doesn't yet recognise this.
  85. public NetworkManager(IConfigurationManager configurationManager, ILogger<NetworkManager> logger)
  86. {
  87. _logger = logger ?? throw new ArgumentNullException(nameof(logger));
  88. _configurationManager = configurationManager ?? throw new ArgumentNullException(nameof(configurationManager));
  89. _interfaceAddresses = new NetCollection();
  90. _macAddresses = new List<PhysicalAddress>();
  91. _interfaceNames = new Dictionary<string, int>();
  92. _publishedServerUrls = new Dictionary<IPNetAddress, string>();
  93. _eventFireLock = new object();
  94. UpdateSettings(_configurationManager.GetNetworkConfiguration());
  95. NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;
  96. NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
  97. _configurationManager.NamedConfigurationUpdated += ConfigurationUpdated;
  98. }
  99. #pragma warning restore CS8618 // Non-nullable field is uninitialized.
  100. /// <summary>
  101. /// Event triggered on network changes.
  102. /// </summary>
  103. public event EventHandler? NetworkChanged;
  104. /// <summary>
  105. /// Gets or sets a value indicating whether testing is taking place.
  106. /// </summary>
  107. public static string MockNetworkSettings { get; set; } = string.Empty;
  108. /// <summary>
  109. /// Gets or sets a value indicating whether IP6 is enabled.
  110. /// </summary>
  111. public bool IsIP6Enabled { get; set; }
  112. /// <summary>
  113. /// Gets or sets a value indicating whether IP4 is enabled.
  114. /// </summary>
  115. public bool IsIP4Enabled { get; set; }
  116. /// <inheritdoc/>
  117. public NetCollection RemoteAddressFilter { get; private set; }
  118. /// <summary>
  119. /// Gets a value indicating whether is all IPv6 interfaces are trusted as internal.
  120. /// </summary>
  121. public bool TrustAllIP6Interfaces { get; internal set; }
  122. /// <summary>
  123. /// Gets the Published server override list.
  124. /// </summary>
  125. public Dictionary<IPNetAddress, string> PublishedServerUrls => _publishedServerUrls;
  126. /// <summary>
  127. /// Creates a new network collection.
  128. /// </summary>
  129. /// <param name="source">Items to assign the collection, or null.</param>
  130. /// <returns>The collection created.</returns>
  131. public static NetCollection CreateCollection(IEnumerable<IPObject>? source)
  132. {
  133. var result = new NetCollection();
  134. if (source != null)
  135. {
  136. return result.AddRange(source);
  137. }
  138. return result;
  139. }
  140. /// <inheritdoc/>
  141. public void Dispose()
  142. {
  143. Dispose(true);
  144. GC.SuppressFinalize(this);
  145. }
  146. /// <inheritdoc/>
  147. public IReadOnlyCollection<PhysicalAddress> GetMacAddresses()
  148. {
  149. // Populated in construction - so always has values.
  150. return _macAddresses.AsReadOnly();
  151. }
  152. /// <inheritdoc/>
  153. public bool IsGatewayInterface(object? addressObj)
  154. {
  155. var address = addressObj switch
  156. {
  157. IPAddress addressIp => addressIp,
  158. IPObject addressIpObj => addressIpObj.Address,
  159. _ => IPAddress.None
  160. };
  161. return _internalInterfaces.Any(i => i.Address.Equals(address) && i.Tag < 0);
  162. }
  163. /// <inheritdoc/>
  164. public NetCollection GetLoopbacks()
  165. {
  166. NetCollection nc = new NetCollection();
  167. if (IsIP4Enabled)
  168. {
  169. nc.AddItem(IPAddress.Loopback);
  170. }
  171. if (IsIP6Enabled)
  172. {
  173. nc.AddItem(IPAddress.IPv6Loopback);
  174. }
  175. return nc;
  176. }
  177. /// <inheritdoc/>
  178. public bool IsExcluded(IPAddress ip)
  179. {
  180. return _excludedSubnets.ContainsAddress(ip);
  181. }
  182. /// <inheritdoc/>
  183. public bool IsExcluded(EndPoint ip)
  184. {
  185. return ip != null && IsExcluded(((IPEndPoint)ip).Address);
  186. }
  187. /// <inheritdoc/>
  188. public NetCollection CreateIPCollection(string[] values, bool bracketed = false)
  189. {
  190. NetCollection col = new NetCollection();
  191. if (values == null)
  192. {
  193. return col;
  194. }
  195. for (int a = 0; a < values.Length; a++)
  196. {
  197. string v = values[a].Trim();
  198. try
  199. {
  200. if (v.StartsWith('[') && v.EndsWith(']'))
  201. {
  202. if (bracketed)
  203. {
  204. AddToCollection(col, v[1..^1]);
  205. }
  206. }
  207. else if (v.StartsWith('!'))
  208. {
  209. if (bracketed)
  210. {
  211. AddToCollection(col, v.Substring(1));
  212. }
  213. }
  214. else if (!bracketed)
  215. {
  216. AddToCollection(col, v);
  217. }
  218. }
  219. catch (ArgumentException e)
  220. {
  221. _logger.LogInformation("Ignoring LAN value {value}. Reason : {reason}", v, e.Message);
  222. }
  223. }
  224. return col;
  225. }
  226. /// <inheritdoc/>
  227. public NetCollection GetAllBindInterfaces(bool individualInterfaces = false)
  228. {
  229. int count = _bindAddresses.Count;
  230. if (count == 0)
  231. {
  232. if (_bindExclusions.Count > 0)
  233. {
  234. // Return all the interfaces except the ones specifically excluded.
  235. return _interfaceAddresses.Exclude(_bindExclusions);
  236. }
  237. if (individualInterfaces)
  238. {
  239. return new NetCollection(_interfaceAddresses);
  240. }
  241. // No bind address and no exclusions, so listen on all interfaces.
  242. NetCollection result = new NetCollection();
  243. if (IsIP4Enabled)
  244. {
  245. result.AddItem(IPAddress.Any);
  246. }
  247. if (IsIP6Enabled)
  248. {
  249. result.AddItem(IPAddress.IPv6Any);
  250. }
  251. return result;
  252. }
  253. // Remove any excluded bind interfaces.
  254. return _bindAddresses.Exclude(_bindExclusions);
  255. }
  256. /// <inheritdoc/>
  257. public string GetBindInterface(string source, out int? port)
  258. {
  259. if (!string.IsNullOrEmpty(source) && IPHost.TryParse(source, out IPHost host))
  260. {
  261. return GetBindInterface(host, out port);
  262. }
  263. return GetBindInterface(IPHost.None, out port);
  264. }
  265. /// <inheritdoc/>
  266. public string GetBindInterface(IPAddress source, out int? port)
  267. {
  268. return GetBindInterface(new IPNetAddress(source), out port);
  269. }
  270. /// <inheritdoc/>
  271. public string GetBindInterface(HttpRequest source, out int? port)
  272. {
  273. string result;
  274. if (source != null && IPHost.TryParse(source.Host.Host, out IPHost host))
  275. {
  276. result = GetBindInterface(host, out port);
  277. port ??= source.Host.Port;
  278. }
  279. else
  280. {
  281. result = GetBindInterface(IPNetAddress.None, out port);
  282. port ??= source?.Host.Port;
  283. }
  284. return result;
  285. }
  286. /// <inheritdoc/>
  287. public string GetBindInterface(IPObject source, out int? port)
  288. {
  289. port = null;
  290. if (source == null)
  291. {
  292. throw new ArgumentNullException(nameof(source));
  293. }
  294. // Do we have a source?
  295. bool haveSource = !source.Address.Equals(IPAddress.None);
  296. bool isExternal = false;
  297. if (haveSource)
  298. {
  299. if (!IsIP6Enabled && source.AddressFamily == AddressFamily.InterNetworkV6)
  300. {
  301. _logger.LogWarning("IPv6 is disabled in Jellyfin, but enabled in the OS. This may affect how the interface is selected.");
  302. }
  303. if (!IsIP4Enabled && source.AddressFamily == AddressFamily.InterNetwork)
  304. {
  305. _logger.LogWarning("IPv4 is disabled in Jellyfin, but enabled in the OS. This may affect how the interface is selected.");
  306. }
  307. isExternal = !IsInLocalNetwork(source);
  308. if (MatchesPublishedServerUrl(source, isExternal, out string res, out port))
  309. {
  310. _logger.LogInformation("{0}: Using BindAddress {1}:{2}", source, res, port);
  311. return res;
  312. }
  313. }
  314. _logger.LogDebug("GetBindInterface: Source: {0}, External: {1}:", haveSource, isExternal);
  315. // No preference given, so move on to bind addresses.
  316. if (MatchesBindInterface(source, isExternal, out string result))
  317. {
  318. return result;
  319. }
  320. if (isExternal && MatchesExternalInterface(source, out result))
  321. {
  322. return result;
  323. }
  324. // Get the first LAN interface address that isn't a loopback.
  325. var interfaces = CreateCollection(_interfaceAddresses
  326. .Exclude(_bindExclusions)
  327. .Where(p => IsInLocalNetwork(p))
  328. .OrderBy(p => p.Tag));
  329. if (interfaces.Count > 0)
  330. {
  331. if (haveSource)
  332. {
  333. // Does the request originate in one of the interface subnets?
  334. // (For systems with multiple internal network cards, and multiple subnets)
  335. foreach (var intf in interfaces)
  336. {
  337. if (intf.Contains(source))
  338. {
  339. result = FormatIP6String(intf.Address);
  340. _logger.LogDebug("{0}: GetBindInterface: Has source, matched best internal interface on range. {1}", source, result);
  341. return result;
  342. }
  343. }
  344. }
  345. result = FormatIP6String(interfaces.First().Address);
  346. _logger.LogDebug("{0}: GetBindInterface: Matched first internal interface. {1}", source, result);
  347. return result;
  348. }
  349. // There isn't any others, so we'll use the loopback.
  350. result = IsIP6Enabled ? "::" : "127.0.0.1";
  351. _logger.LogWarning("{0}: GetBindInterface: Loopback return.", source, result);
  352. return result;
  353. }
  354. /// <inheritdoc/>
  355. public NetCollection GetInternalBindAddresses()
  356. {
  357. int count = _bindAddresses.Count;
  358. if (count == 0)
  359. {
  360. if (_bindExclusions.Count > 0)
  361. {
  362. // Return all the internal interfaces except the ones excluded.
  363. return CreateCollection(_internalInterfaces.Where(p => !_bindExclusions.Contains(p)));
  364. }
  365. // No bind address, so return all internal interfaces.
  366. return CreateCollection(_internalInterfaces.Where(p => !p.IsLoopback()));
  367. }
  368. return new NetCollection(_bindAddresses);
  369. }
  370. /// <inheritdoc/>
  371. public bool IsInLocalNetwork(IPObject address)
  372. {
  373. if (address == null)
  374. {
  375. throw new ArgumentNullException(nameof(address));
  376. }
  377. if (address.Equals(IPAddress.None))
  378. {
  379. return false;
  380. }
  381. // See conversation at https://github.com/jellyfin/jellyfin/pull/3515.
  382. if (TrustAllIP6Interfaces && address.AddressFamily == AddressFamily.InterNetworkV6)
  383. {
  384. return true;
  385. }
  386. // As private addresses can be redefined by Configuration.LocalNetworkAddresses
  387. return _lanSubnets.Contains(address) && !_excludedSubnets.Contains(address);
  388. }
  389. /// <inheritdoc/>
  390. public bool IsInLocalNetwork(string address)
  391. {
  392. if (IPHost.TryParse(address, out IPHost ep))
  393. {
  394. return _lanSubnets.Contains(ep) && !_excludedSubnets.Contains(ep);
  395. }
  396. return false;
  397. }
  398. /// <inheritdoc/>
  399. public bool IsInLocalNetwork(IPAddress address)
  400. {
  401. if (address == null)
  402. {
  403. throw new ArgumentNullException(nameof(address));
  404. }
  405. // See conversation at https://github.com/jellyfin/jellyfin/pull/3515.
  406. if (TrustAllIP6Interfaces && address.AddressFamily == AddressFamily.InterNetworkV6)
  407. {
  408. return true;
  409. }
  410. // As private addresses can be redefined by Configuration.LocalNetworkAddresses
  411. return _lanSubnets.ContainsAddress(address) && !_excludedSubnets.ContainsAddress(address);
  412. }
  413. /// <inheritdoc/>
  414. public bool IsPrivateAddressRange(IPObject address)
  415. {
  416. if (address == null)
  417. {
  418. throw new ArgumentNullException(nameof(address));
  419. }
  420. // See conversation at https://github.com/jellyfin/jellyfin/pull/3515.
  421. if (TrustAllIP6Interfaces && address.AddressFamily == AddressFamily.InterNetworkV6)
  422. {
  423. return true;
  424. }
  425. else
  426. {
  427. return address.IsPrivateAddressRange();
  428. }
  429. }
  430. /// <inheritdoc/>
  431. public bool IsExcludedInterface(IPAddress address)
  432. {
  433. return _bindExclusions.ContainsAddress(address);
  434. }
  435. /// <inheritdoc/>
  436. public NetCollection GetFilteredLANSubnets(NetCollection? filter = null)
  437. {
  438. if (filter == null)
  439. {
  440. return _lanSubnets.Exclude(_excludedSubnets).AsNetworks();
  441. }
  442. return _lanSubnets.Exclude(filter);
  443. }
  444. /// <inheritdoc/>
  445. public bool IsValidInterfaceAddress(IPAddress address)
  446. {
  447. return _interfaceAddresses.ContainsAddress(address);
  448. }
  449. /// <inheritdoc/>
  450. public bool TryParseInterface(string token, out NetCollection? result)
  451. {
  452. result = null;
  453. if (string.IsNullOrEmpty(token))
  454. {
  455. return false;
  456. }
  457. if (_interfaceNames != null && _interfaceNames.TryGetValue(token.ToLower(CultureInfo.InvariantCulture), out int index))
  458. {
  459. result = new NetCollection();
  460. _logger.LogInformation("Interface {0} used in settings. Using its interface addresses.", token);
  461. // Replace interface tags with the interface IP's.
  462. foreach (IPNetAddress iface in _interfaceAddresses)
  463. {
  464. if (Math.Abs(iface.Tag) == index &&
  465. ((IsIP4Enabled && iface.Address.AddressFamily == AddressFamily.InterNetwork) ||
  466. (IsIP6Enabled && iface.Address.AddressFamily == AddressFamily.InterNetworkV6)))
  467. {
  468. result.Add(iface);
  469. }
  470. }
  471. return true;
  472. }
  473. return false;
  474. }
  475. /// <summary>
  476. /// Reloads all settings and re-initialises the instance.
  477. /// </summary>
  478. /// <param name="configuration">The configuration to use.</param>
  479. public void UpdateSettings(object configuration)
  480. {
  481. NetworkConfiguration config = (NetworkConfiguration)configuration ?? throw new ArgumentNullException(nameof(configuration));
  482. IsIP4Enabled = Socket.OSSupportsIPv4 && config.EnableIPV4;
  483. IsIP6Enabled = Socket.OSSupportsIPv6 && config.EnableIPV6;
  484. if (!IsIP6Enabled && !IsIP4Enabled)
  485. {
  486. _logger.LogError("IPv4 and IPv6 cannot both be disabled.");
  487. IsIP4Enabled = true;
  488. }
  489. TrustAllIP6Interfaces = config.TrustAllIP6Interfaces;
  490. // UdpHelper.EnableMultiSocketBinding = config.EnableMultiSocketBinding;
  491. if (string.IsNullOrEmpty(MockNetworkSettings))
  492. {
  493. InitialiseInterfaces();
  494. }
  495. else // Used in testing only.
  496. {
  497. // Format is <IPAddress>,<Index>,<Name>: <next interface>. Set index to -ve to simulate a gateway.
  498. var interfaceList = MockNetworkSettings.Split(':');
  499. foreach (var details in interfaceList)
  500. {
  501. var parts = details.Split(',');
  502. var address = IPNetAddress.Parse(parts[0]);
  503. var index = int.Parse(parts[1], CultureInfo.InvariantCulture);
  504. address.Tag = index;
  505. _interfaceAddresses.Add(address);
  506. _interfaceNames.Add(parts[2], Math.Abs(index));
  507. }
  508. }
  509. InitialiseLAN(config);
  510. InitialiseBind(config);
  511. InitialiseRemote(config);
  512. InitialiseOverrides(config);
  513. }
  514. /// <summary>
  515. /// Protected implementation of Dispose pattern.
  516. /// </summary>
  517. /// <param name="disposing">True to dispose the managed state.</param>
  518. protected virtual void Dispose(bool disposing)
  519. {
  520. if (!_disposed)
  521. {
  522. if (disposing)
  523. {
  524. _configurationManager.NamedConfigurationUpdated -= ConfigurationUpdated;
  525. NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged;
  526. NetworkChange.NetworkAvailabilityChanged -= OnNetworkAvailabilityChanged;
  527. }
  528. _disposed = true;
  529. }
  530. }
  531. /// <summary>
  532. /// Trys to identify the string and return an object of that class.
  533. /// </summary>
  534. /// <param name="addr">String to parse.</param>
  535. /// <param name="result">IPObject to return.</param>
  536. /// <returns>True if the value parsed successfully.</returns>
  537. private static bool TryParse(string addr, out IPObject result)
  538. {
  539. if (!string.IsNullOrEmpty(addr))
  540. {
  541. // Is it an IP address
  542. if (IPNetAddress.TryParse(addr, out IPNetAddress nw))
  543. {
  544. result = nw;
  545. return true;
  546. }
  547. if (IPHost.TryParse(addr, out IPHost h))
  548. {
  549. result = h;
  550. return true;
  551. }
  552. }
  553. result = IPNetAddress.None;
  554. return false;
  555. }
  556. /// <summary>
  557. /// Converts an IPAddress into a string.
  558. /// Ipv6 addresses are returned in [ ], with their scope removed.
  559. /// </summary>
  560. /// <param name="address">Address to convert.</param>
  561. /// <returns>URI save conversion of the address.</returns>
  562. private static string FormatIP6String(IPAddress address)
  563. {
  564. var str = address.ToString();
  565. if (address.AddressFamily == AddressFamily.InterNetworkV6)
  566. {
  567. int i = str.IndexOf("%", StringComparison.OrdinalIgnoreCase);
  568. if (i != -1)
  569. {
  570. str = str.Substring(0, i);
  571. }
  572. return $"[{str}]";
  573. }
  574. return str;
  575. }
  576. private void ConfigurationUpdated(object? sender, ConfigurationUpdateEventArgs evt)
  577. {
  578. if (evt.Key.Equals("network", StringComparison.Ordinal))
  579. {
  580. UpdateSettings(evt.NewConfiguration);
  581. }
  582. }
  583. /// <summary>
  584. /// Checks the string to see if it matches any interface names.
  585. /// </summary>
  586. /// <param name="token">String to check.</param>
  587. /// <param name="index">Interface index number.</param>
  588. /// <returns>True if an interface name matches the token.</returns>
  589. private bool IsInterface(string token, out int index)
  590. {
  591. index = -1;
  592. // Is it the name of an interface (windows) eg, Wireless LAN adapter Wireless Network Connection 1.
  593. // Null check required here for automated testing.
  594. if (_interfaceNames != null && token.Length > 1)
  595. {
  596. bool partial = token[^1] == '*';
  597. if (partial)
  598. {
  599. token = token[0..^1];
  600. }
  601. foreach ((string interfc, int interfcIndex) in _interfaceNames)
  602. {
  603. if ((!partial && string.Equals(interfc, token, StringComparison.OrdinalIgnoreCase)) ||
  604. (partial && interfc.StartsWith(token, true, CultureInfo.InvariantCulture)))
  605. {
  606. index = interfcIndex;
  607. return true;
  608. }
  609. }
  610. }
  611. return false;
  612. }
  613. /// <summary>
  614. /// Parses strings into the collection, replacing any interface references.
  615. /// </summary>
  616. /// <param name="col">Collection.</param>
  617. /// <param name="token">String to parse.</param>
  618. private void AddToCollection(NetCollection col, string token)
  619. {
  620. // Is it the name of an interface (windows) eg, Wireless LAN adapter Wireless Network Connection 1.
  621. // Null check required here for automated testing.
  622. if (IsInterface(token, out int index))
  623. {
  624. _logger.LogInformation("Interface {0} used in settings. Using its interface addresses.", token);
  625. // Replace interface tags with the interface IP's.
  626. foreach (IPNetAddress iface in _interfaceAddresses)
  627. {
  628. if (Math.Abs(iface.Tag) == index &&
  629. ((IsIP4Enabled && iface.Address.AddressFamily == AddressFamily.InterNetwork) ||
  630. (IsIP6Enabled && iface.Address.AddressFamily == AddressFamily.InterNetworkV6)))
  631. {
  632. col.Add(iface);
  633. }
  634. }
  635. }
  636. else if (TryParse(token, out IPObject obj))
  637. {
  638. if (!IsIP6Enabled)
  639. {
  640. // Remove IP6 addresses from multi-homed IPHosts.
  641. obj.Remove(AddressFamily.InterNetworkV6);
  642. if (!obj.IsIP6())
  643. {
  644. col.Add(obj);
  645. }
  646. }
  647. else if (!IsIP4Enabled)
  648. {
  649. // Remove IP4 addresses from multi-homed IPHosts.
  650. obj.Remove(AddressFamily.InterNetwork);
  651. if (obj.IsIP6())
  652. {
  653. col.Add(obj);
  654. }
  655. }
  656. else
  657. {
  658. col.Add(obj);
  659. }
  660. }
  661. else
  662. {
  663. _logger.LogDebug("Invalid or unknown network {0}.", token);
  664. }
  665. }
  666. /// <summary>
  667. /// Handler for network change events.
  668. /// </summary>
  669. /// <param name="sender">Sender.</param>
  670. /// <param name="e">Network availability information.</param>
  671. private void OnNetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs e)
  672. {
  673. _logger.LogDebug("Network availability changed.");
  674. OnNetworkChanged();
  675. }
  676. /// <summary>
  677. /// Handler for network change events.
  678. /// </summary>
  679. /// <param name="sender">Sender.</param>
  680. /// <param name="e">Event arguments.</param>
  681. private void OnNetworkAddressChanged(object? sender, EventArgs e)
  682. {
  683. _logger.LogDebug("Network address change detected.");
  684. OnNetworkChanged();
  685. }
  686. /// <summary>
  687. /// Async task that waits for 2 seconds before re-initialising the settings, as typically these events fire multiple times in succession.
  688. /// </summary>
  689. /// <returns>The network change async.</returns>
  690. private async Task OnNetworkChangeAsync()
  691. {
  692. try
  693. {
  694. await Task.Delay(2000).ConfigureAwait(false);
  695. InitialiseInterfaces();
  696. // Recalculate LAN caches.
  697. InitialiseLAN(_configurationManager.GetNetworkConfiguration());
  698. NetworkChanged?.Invoke(this, EventArgs.Empty);
  699. }
  700. finally
  701. {
  702. _eventfire = false;
  703. }
  704. }
  705. /// <summary>
  706. /// Triggers our event, and re-loads interface information.
  707. /// </summary>
  708. private void OnNetworkChanged()
  709. {
  710. lock (_eventFireLock)
  711. {
  712. if (!_eventfire)
  713. {
  714. _logger.LogDebug("Network Address Change Event.");
  715. // As network events tend to fire one after the other only fire once every second.
  716. _eventfire = true;
  717. OnNetworkChangeAsync().GetAwaiter().GetResult();
  718. }
  719. }
  720. }
  721. /// <summary>
  722. /// Parses the user defined overrides into the dictionary object.
  723. /// Overrides are the equivalent of localised publishedServerUrl, enabling
  724. /// different addresses to be advertised over different subnets.
  725. /// format is subnet=ipaddress|host|uri
  726. /// when subnet = 0.0.0.0, any external address matches.
  727. /// </summary>
  728. private void InitialiseOverrides(NetworkConfiguration config)
  729. {
  730. lock (_intLock)
  731. {
  732. _publishedServerUrls.Clear();
  733. string[] overrides = config.PublishedServerUriBySubnet;
  734. if (overrides == null)
  735. {
  736. return;
  737. }
  738. foreach (var entry in overrides)
  739. {
  740. var parts = entry.Split('=');
  741. if (parts.Length != 2)
  742. {
  743. _logger.LogError("Unable to parse bind override. {0}", entry);
  744. }
  745. else
  746. {
  747. var replacement = parts[1].Trim();
  748. if (string.Equals(parts[0], "remaining", StringComparison.OrdinalIgnoreCase))
  749. {
  750. _publishedServerUrls[new IPNetAddress(IPAddress.Broadcast)] = replacement;
  751. }
  752. else if (string.Equals(parts[0], "external", StringComparison.OrdinalIgnoreCase))
  753. {
  754. _publishedServerUrls[new IPNetAddress(IPAddress.Any)] = replacement;
  755. }
  756. else if (TryParseInterface(parts[0], out NetCollection? addresses) && addresses != null)
  757. {
  758. foreach (IPNetAddress na in addresses)
  759. {
  760. _publishedServerUrls[na] = replacement;
  761. }
  762. }
  763. else if (IPNetAddress.TryParse(parts[0], out IPNetAddress result))
  764. {
  765. _publishedServerUrls[result] = replacement;
  766. }
  767. else
  768. {
  769. _logger.LogError("Unable to parse bind ip address. {0}", parts[1]);
  770. }
  771. }
  772. }
  773. }
  774. }
  775. private void InitialiseBind(NetworkConfiguration config)
  776. {
  777. string[] lanAddresses = config.LocalNetworkAddresses;
  778. // TODO: remove when bug fixed: https://github.com/jellyfin/jellyfin-web/issues/1334
  779. if (lanAddresses.Length == 1 && lanAddresses[0].IndexOf(',', StringComparison.OrdinalIgnoreCase) != -1)
  780. {
  781. lanAddresses = lanAddresses[0].Split(',');
  782. }
  783. // TODO: end fix: https://github.com/jellyfin/jellyfin-web/issues/1334
  784. // Add virtual machine interface names to the list of bind exclusions, so that they are auto-excluded.
  785. if (config.IgnoreVirtualInterfaces)
  786. {
  787. var newList = lanAddresses.ToList();
  788. newList.AddRange(config.VirtualInterfaceNames.Split(',').ToList());
  789. lanAddresses = newList.ToArray();
  790. }
  791. // Read and parse bind addresses and exclusions, removing ones that don't exist.
  792. _bindAddresses = CreateIPCollection(lanAddresses).Union(_interfaceAddresses);
  793. _bindExclusions = CreateIPCollection(lanAddresses, true).Union(_interfaceAddresses);
  794. _logger.LogInformation("Using bind addresses: {0}", _bindAddresses);
  795. _logger.LogInformation("Using bind exclusions: {0}", _bindExclusions);
  796. }
  797. private void InitialiseRemote(NetworkConfiguration config)
  798. {
  799. RemoteAddressFilter = CreateIPCollection(config.RemoteIPFilter);
  800. }
  801. /// <summary>
  802. /// Initialises internal LAN cache settings.
  803. /// </summary>
  804. private void InitialiseLAN(NetworkConfiguration config)
  805. {
  806. lock (_intLock)
  807. {
  808. _logger.LogDebug("Refreshing LAN information.");
  809. // Get config options.
  810. string[] subnets = config.LocalNetworkSubnets;
  811. // Create lists from user settings.
  812. _lanSubnets = CreateIPCollection(subnets);
  813. _excludedSubnets = CreateIPCollection(subnets, true).AsNetworks();
  814. // If no LAN addresses are specified - all private subnets are deemed to be the LAN
  815. _usingPrivateAddresses = _lanSubnets.Count == 0;
  816. // NOTE: The order of the commands in this statement matters.
  817. if (_usingPrivateAddresses)
  818. {
  819. _logger.LogDebug("Using LAN interface addresses as user provided no LAN details.");
  820. // Internal interfaces must be private and not excluded.
  821. _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsPrivateAddressRange(i) && !_excludedSubnets.Contains(i)));
  822. // Subnets are the same as the calculated internal interface.
  823. _lanSubnets = new NetCollection();
  824. // We must listen on loopback for LiveTV to function regardless of the settings.
  825. if (IsIP6Enabled)
  826. {
  827. _lanSubnets.Add(IPNetAddress.IP6Loopback);
  828. _lanSubnets.Add(IPNetAddress.Parse("fc00::/7")); // ULA
  829. _lanSubnets.Add(IPNetAddress.Parse("fe80::/10")); // Site local
  830. }
  831. if (IsIP4Enabled)
  832. {
  833. _lanSubnets.Add(IPNetAddress.IP4Loopback);
  834. _lanSubnets.Add(IPNetAddress.Parse("10.0.0.0/8"));
  835. _lanSubnets.Add(IPNetAddress.Parse("172.16.0.0/12"));
  836. _lanSubnets.Add(IPNetAddress.Parse("192.168.0.0/16"));
  837. }
  838. }
  839. else
  840. {
  841. // We must listen on loopback for LiveTV to function regardless of the settings.
  842. if (IsIP6Enabled)
  843. {
  844. _lanSubnets.Add(IPNetAddress.IP6Loopback);
  845. }
  846. if (IsIP4Enabled)
  847. {
  848. _lanSubnets.Add(IPNetAddress.IP4Loopback);
  849. }
  850. // Internal interfaces must be private, not excluded and part of the LocalNetworkSubnet.
  851. _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsInLocalNetwork(i) && !_excludedSubnets.Contains(i) && _lanSubnets.Contains(i)));
  852. }
  853. _logger.LogInformation("Defined LAN addresses : {0}", _lanSubnets);
  854. _logger.LogInformation("Defined LAN exclusions : {0}", _excludedSubnets);
  855. _logger.LogInformation("Using LAN addresses: {0}", _lanSubnets.Exclude(_excludedSubnets).AsNetworks());
  856. }
  857. }
  858. /// <summary>
  859. /// Generate a list of all the interface ip addresses and submasks where that are in the active/unknown state.
  860. /// Generate a list of all active mac addresses that aren't loopback addresses.
  861. /// </summary>
  862. private void InitialiseInterfaces()
  863. {
  864. lock (_intLock)
  865. {
  866. _logger.LogDebug("Refreshing interfaces.");
  867. _interfaceNames.Clear();
  868. _interfaceAddresses.Clear();
  869. try
  870. {
  871. IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces()
  872. .Where(i => i.SupportsMulticast && i.OperationalStatus == OperationalStatus.Up);
  873. foreach (NetworkInterface adapter in nics)
  874. {
  875. try
  876. {
  877. IPInterfaceProperties ipProperties = adapter.GetIPProperties();
  878. PhysicalAddress mac = adapter.GetPhysicalAddress();
  879. // populate mac list
  880. if (adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && mac != null && mac != PhysicalAddress.None)
  881. {
  882. _macAddresses.Add(mac);
  883. }
  884. // populate interface address list
  885. foreach (UnicastIPAddressInformation info in ipProperties.UnicastAddresses)
  886. {
  887. if (IsIP4Enabled && info.Address.AddressFamily == AddressFamily.InterNetwork)
  888. {
  889. IPNetAddress nw = new IPNetAddress(info.Address, IPObject.MaskToCidr(info.IPv4Mask))
  890. {
  891. // Keep the number of gateways on this interface, along with its index.
  892. Tag = ipProperties.GetIPv4Properties().Index
  893. };
  894. int tag = nw.Tag;
  895. if ((ipProperties.GatewayAddresses.Count > 0) && !nw.IsLoopback())
  896. {
  897. // -ve Tags signify the interface has a gateway.
  898. nw.Tag *= -1;
  899. }
  900. _interfaceAddresses.Add(nw);
  901. // Store interface name so we can use the name in Collections.
  902. _interfaceNames[adapter.Description.ToLower(CultureInfo.InvariantCulture)] = tag;
  903. _interfaceNames["eth" + tag.ToString(CultureInfo.InvariantCulture)] = tag;
  904. }
  905. else if (IsIP6Enabled && info.Address.AddressFamily == AddressFamily.InterNetworkV6)
  906. {
  907. IPNetAddress nw = new IPNetAddress(info.Address, (byte)info.PrefixLength)
  908. {
  909. // Keep the number of gateways on this interface, along with its index.
  910. Tag = ipProperties.GetIPv6Properties().Index
  911. };
  912. int tag = nw.Tag;
  913. if ((ipProperties.GatewayAddresses.Count > 0) && !nw.IsLoopback())
  914. {
  915. // -ve Tags signify the interface has a gateway.
  916. nw.Tag *= -1;
  917. }
  918. _interfaceAddresses.Add(nw);
  919. // Store interface name so we can use the name in Collections.
  920. _interfaceNames[adapter.Description.ToLower(CultureInfo.InvariantCulture)] = tag;
  921. _interfaceNames["eth" + tag.ToString(CultureInfo.InvariantCulture)] = tag;
  922. }
  923. }
  924. }
  925. #pragma warning disable CA1031 // Do not catch general exception types
  926. catch
  927. {
  928. // Ignore error, and attempt to continue.
  929. }
  930. #pragma warning restore CA1031 // Do not catch general exception types
  931. }
  932. _logger.LogDebug("Discovered {0} interfaces.", _interfaceAddresses.Count);
  933. _logger.LogDebug("Interfaces addresses : {0}", _interfaceAddresses);
  934. // If for some reason we don't have an interface info, resolve our DNS name.
  935. if (_interfaceAddresses.Count == 0)
  936. {
  937. _logger.LogWarning("No interfaces information available. Using loopback.");
  938. IPHost host = new IPHost(Dns.GetHostName());
  939. foreach (var a in host.GetAddresses())
  940. {
  941. _interfaceAddresses.AddItem(a);
  942. }
  943. if (_interfaceAddresses.Count == 0)
  944. {
  945. _logger.LogError("No interfaces information available. Resolving DNS name.");
  946. // Last ditch attempt - use loopback address.
  947. _interfaceAddresses.Add(IPNetAddress.IP4Loopback);
  948. if (IsIP6Enabled)
  949. {
  950. _interfaceAddresses.Add(IPNetAddress.IP6Loopback);
  951. }
  952. }
  953. }
  954. }
  955. catch (NetworkInformationException ex)
  956. {
  957. _logger.LogError(ex, "Error in InitialiseInterfaces.");
  958. }
  959. }
  960. }
  961. /// <summary>
  962. /// Attempts to match the source against a user defined bind interface.
  963. /// </summary>
  964. /// <param name="source">IP source address to use.</param>
  965. /// <param name="isExternal">True if the source is in the external subnet.</param>
  966. /// <param name="bindPreference">The published server url that matches the source address.</param>
  967. /// <param name="port">The resultant port, if one exists.</param>
  968. /// <returns>True if a match is found.</returns>
  969. private bool MatchesPublishedServerUrl(IPObject source, bool isExternal, out string bindPreference, out int? port)
  970. {
  971. bindPreference = string.Empty;
  972. port = null;
  973. // Check for user override.
  974. foreach (var addr in _publishedServerUrls)
  975. {
  976. // Remaining. Match anything.
  977. if (addr.Key.Equals(IPAddress.Broadcast))
  978. {
  979. bindPreference = addr.Value;
  980. break;
  981. }
  982. else if ((addr.Key.Equals(IPAddress.Any) || addr.Key.Equals(IPAddress.IPv6Any)) && isExternal)
  983. {
  984. // External.
  985. bindPreference = addr.Value;
  986. break;
  987. }
  988. else if (addr.Key.Contains(source))
  989. {
  990. // Match ip address.
  991. bindPreference = addr.Value;
  992. break;
  993. }
  994. }
  995. if (!string.IsNullOrEmpty(bindPreference))
  996. {
  997. // Has it got a port defined?
  998. var parts = bindPreference.Split(':');
  999. if (parts.Length > 1)
  1000. {
  1001. if (int.TryParse(parts[1], out int p))
  1002. {
  1003. bindPreference = parts[0];
  1004. port = p;
  1005. }
  1006. }
  1007. return true;
  1008. }
  1009. return false;
  1010. }
  1011. /// <summary>
  1012. /// Attempts to match the source against a user defined bind interface.
  1013. /// </summary>
  1014. /// <param name="source">IP source address to use.</param>
  1015. /// <param name="isExternal">True if the source is in the external subnet.</param>
  1016. /// <param name="result">The result, if a match is found.</param>
  1017. /// <returns>True if a match is found.</returns>
  1018. private bool MatchesBindInterface(IPObject source, bool isExternal, out string result)
  1019. {
  1020. result = string.Empty;
  1021. var nc = _bindAddresses.Exclude(_bindExclusions);
  1022. int count = nc.Count;
  1023. if (count == 1 && (_bindAddresses[0].Equals(IPAddress.Any) || _bindAddresses[0].Equals(IPAddress.IPv6Any)))
  1024. {
  1025. // Ignore IPAny addresses.
  1026. count = 0;
  1027. }
  1028. if (count != 0)
  1029. {
  1030. // Check to see if any of the bind interfaces are in the same subnet.
  1031. NetCollection bindResult;
  1032. IPAddress? defaultGateway = null;
  1033. IPAddress? bindAddress;
  1034. if (isExternal)
  1035. {
  1036. // Find all external bind addresses. Store the default gateway, but check to see if there is a better match first.
  1037. bindResult = CreateCollection(nc
  1038. .Where(p => !IsInLocalNetwork(p))
  1039. .OrderBy(p => p.Tag));
  1040. defaultGateway = bindResult.FirstOrDefault()?.Address;
  1041. bindAddress = bindResult
  1042. .Where(p => p.Contains(source))
  1043. .OrderBy(p => p.Tag)
  1044. .FirstOrDefault()?.Address;
  1045. }
  1046. else
  1047. {
  1048. // Look for the best internal address.
  1049. bindAddress = nc
  1050. .Where(p => IsInLocalNetwork(p) && (p.Contains(source) || p.Equals(IPAddress.None)))
  1051. .OrderBy(p => p.Tag)
  1052. .FirstOrDefault()?.Address;
  1053. }
  1054. if (bindAddress != null)
  1055. {
  1056. result = FormatIP6String(bindAddress);
  1057. _logger.LogDebug("{0}: GetBindInterface: Has source, found a match bind interface subnets. {1}", source, result);
  1058. return true;
  1059. }
  1060. if (isExternal && defaultGateway != null)
  1061. {
  1062. result = FormatIP6String(defaultGateway);
  1063. _logger.LogDebug("{0}: GetBindInterface: Using first user defined external interface. {1}", source, result);
  1064. return true;
  1065. }
  1066. result = FormatIP6String(nc.First().Address);
  1067. _logger.LogDebug("{0}: GetBindInterface: Selected first user defined interface. {1}", source, result);
  1068. if (isExternal)
  1069. {
  1070. // TODO: remove this after testing.
  1071. _logger.LogWarning("{0}: External request received, however, only an internal interface bind found.", source);
  1072. }
  1073. return true;
  1074. }
  1075. return false;
  1076. }
  1077. /// <summary>
  1078. /// Attempts to match the source against an external interface.
  1079. /// </summary>
  1080. /// <param name="source">IP source address to use.</param>
  1081. /// <param name="result">The result, if a match is found.</param>
  1082. /// <returns>True if a match is found.</returns>
  1083. private bool MatchesExternalInterface(IPObject source, out string result)
  1084. {
  1085. result = string.Empty;
  1086. // Get the first WAN interface address that isn't a loopback.
  1087. var extResult = CreateCollection(_interfaceAddresses
  1088. .Exclude(_bindExclusions)
  1089. .Where(p => !IsInLocalNetwork(p))
  1090. .OrderBy(p => p.Tag));
  1091. if (extResult.Count > 0)
  1092. {
  1093. // Does the request originate in one of the interface subnets?
  1094. // (For systems with multiple internal network cards, and multiple subnets)
  1095. foreach (var intf in extResult)
  1096. {
  1097. if (!IsInLocalNetwork(intf) && intf.Contains(source))
  1098. {
  1099. result = FormatIP6String(intf.Address);
  1100. _logger.LogDebug("{0}: GetBindInterface: Selected best external on interface on range. {1}", source, result);
  1101. return true;
  1102. }
  1103. }
  1104. result = FormatIP6String(extResult.First().Address);
  1105. _logger.LogDebug("{0}: GetBindInterface: Selected first external interface. {0}", source, result);
  1106. return true;
  1107. }
  1108. // Have to return something, so return an internal address
  1109. // TODO: remove this after testing.
  1110. _logger.LogWarning("{0}: External request received, however, no WAN interface found.", source);
  1111. return false;
  1112. }
  1113. }
  1114. }