diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
index 82c6abb8c8..01a987b6ab 100644
--- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
@@ -79,6 +79,10 @@ namespace Emby.Server.Implementations.EntryPoints
{
if (_enableMultiSocketBinding)
{
+ // Add global broadcast socket
+ _udpServers.Add(new UdpServer(_logger, _appHost, _config, System.Net.IPAddress.Broadcast, PortNumber));
+
+ // Add bind address specific broadcast sockets
foreach (var bindAddress in _networkManager.GetInternalBindAddresses())
{
if (bindAddress.AddressFamily == AddressFamily.InterNetworkV6)
@@ -87,7 +91,10 @@ namespace Emby.Server.Implementations.EntryPoints
continue;
}
- _udpServers.Add(new UdpServer(_logger, _appHost, _config, bindAddress.Address, PortNumber));
+ var broadcastAddress = NetworkExtensions.GetBroadcastAddress(bindAddress.Subnet);
+ _logger.LogDebug("Binding UDP server to {Address}", broadcastAddress.ToString());
+
+ _udpServers.Add(new UdpServer(_logger, _appHost, _config, broadcastAddress, PortNumber));
}
}
else
diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs
index 42b66bbed3..3c347461a7 100644
--- a/Jellyfin.Networking/Manager/NetworkManager.cs
+++ b/Jellyfin.Networking/Manager/NetworkManager.cs
@@ -280,7 +280,7 @@ namespace Jellyfin.Networking.Manager
}
_logger.LogDebug("Discovered {0} interfaces.", _interfaces.Count);
- _logger.LogDebug("Interfaces addresses : {0}", _interfaces.Select(s => s.Address).ToString());
+ _logger.LogDebug("Interfaces addresses : {0}", _interfaces.Select(s => s.Address.ToString()));
}
}
@@ -726,20 +726,11 @@ namespace Jellyfin.Networking.Manager
}
bool isExternal = !_lanSubnets.Any(network => network.Contains(source));
- _logger.LogDebug("GetBindInterface with source. External: {IsExternal}:", isExternal);
+ _logger.LogDebug("GetBindInterface with source {Source}. External: {IsExternal}:", source, isExternal);
- if (MatchesPublishedServerUrl(source, isExternal, out string res, out port))
+ if (MatchesPublishedServerUrl(source, isExternal, out result))
{
- if (port != null)
- {
- _logger.LogInformation("{Source}: Using BindAddress {Address}:{Port}", source, res, port);
- }
- else
- {
- _logger.LogInformation("{Source}: Using BindAddress {Address}", source, res);
- }
-
- return res;
+ return result;
}
// No preference given, so move on to bind addresses.
@@ -868,41 +859,37 @@ namespace Jellyfin.Networking.Manager
/// IP source address to use.
/// True if the source is in an external subnet.
/// The published server URL that matches the source address.
- /// The resultant port, if one exists.
/// true if a match is found, false otherwise.
- private bool MatchesPublishedServerUrl(IPAddress source, bool isInExternalSubnet, out string bindPreference, out int? port)
+ private bool MatchesPublishedServerUrl(IPAddress source, bool isInExternalSubnet, out string bindPreference)
{
bindPreference = string.Empty;
- port = null;
+ int? port = null;
var validPublishedServerUrls = _publishedServerUrls.Where(x => x.Key.Address.Equals(IPAddress.Any)
|| x.Key.Address.Equals(IPAddress.IPv6Any)
|| x.Key.Subnet.Contains(source))
.GroupBy(x => x.Key)
- .Select(y => y.First())
+ .Select(x => x.First())
+ .OrderBy(x => x.Key.Address.Equals(IPAddress.Any)
+ || x.Key.Address.Equals(IPAddress.IPv6Any))
.ToList();
// Check for user override.
foreach (var data in validPublishedServerUrls)
{
- // Get address interface
- var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(s => s.Subnet.Contains(data.Key.Address));
+ // Get address interface.
+ var intf = _interfaces.OrderBy(x => x.Index).FirstOrDefault(x => data.Key.Subnet.Contains(x.Address));
- // Remaining. Match anything.
- if (data.Key.Address.Equals(IPAddress.Broadcast))
- {
- bindPreference = data.Value;
- break;
- }
- else if ((data.Key.Address.Equals(IPAddress.Any) || data.Key.Address.Equals(IPAddress.IPv6Any)) && isInExternalSubnet)
+ if (isInExternalSubnet && (data.Key.Address.Equals(IPAddress.Any) || data.Key.Address.Equals(IPAddress.IPv6Any)))
{
// External.
bindPreference = data.Value;
break;
}
- else if (intf?.Address != null)
+
+ if (intf?.Address != null)
{
- // Match ip address.
+ // Match IP address.
bindPreference = data.Value;
break;
}
@@ -910,6 +897,7 @@ namespace Jellyfin.Networking.Manager
if (string.IsNullOrEmpty(bindPreference))
{
+ _logger.LogInformation("{Source}: No matching bind address override found", source);
return false;
}
@@ -924,6 +912,15 @@ namespace Jellyfin.Networking.Manager
}
}
+ if (port != null)
+ {
+ _logger.LogInformation("{Source}: Matching bind address override found {Address}:{Port}", source, bindPreference, port);
+ }
+ else
+ {
+ _logger.LogInformation("{Source}: Matching bind address override found {Address}", source, bindPreference);
+ }
+
return true;
}
@@ -967,12 +964,12 @@ namespace Jellyfin.Networking.Manager
if (bindAddress != null)
{
result = NetworkExtensions.FormatIpString(bindAddress);
- _logger.LogDebug("{Source}: GetBindInterface: Has source, found a matching external bind interface: {Result}", source, result);
+ _logger.LogDebug("{Source}: External request received, matching external bind interface found: {Result}", source, result);
return true;
}
}
- _logger.LogWarning("{Source}: External request received, no external interface bind found, trying internal interfaces.", source);
+ _logger.LogWarning("{Source}: External request received, no matching external bind interface found, trying internal interfaces.", source);
}
else
{
@@ -987,7 +984,7 @@ namespace Jellyfin.Networking.Manager
if (bindAddress != null)
{
result = NetworkExtensions.FormatIpString(bindAddress);
- _logger.LogWarning("{Source}: Request received, matching internal interface bind found: {Result}", source, result);
+ _logger.LogDebug("{Source}: Internal request received, matching internal bind interface found: {Result}", source, result);
return true;
}
}
diff --git a/MediaBrowser.Common/Net/NetworkExtensions.cs b/MediaBrowser.Common/Net/NetworkExtensions.cs
index d7632c0deb..1c2b65346c 100644
--- a/MediaBrowser.Common/Net/NetworkExtensions.cs
+++ b/MediaBrowser.Common/Net/NetworkExtensions.cs
@@ -56,7 +56,23 @@ namespace MediaBrowser.Common.Net
/// String value of the subnet mask in dotted decimal notation.
public static IPAddress CidrToMask(byte cidr, AddressFamily family)
{
- uint addr = 0xFFFFFFFF << (family == AddressFamily.InterNetwork ? 32 : 128 - cidr);
+ uint addr = 0xFFFFFFFF << ((family == AddressFamily.InterNetwork ? 32 : 128) - cidr);
+ addr = ((addr & 0xff000000) >> 24)
+ | ((addr & 0x00ff0000) >> 8)
+ | ((addr & 0x0000ff00) << 8)
+ | ((addr & 0x000000ff) << 24);
+ return new IPAddress(addr);
+ }
+
+ ///
+ /// Convert a subnet mask in CIDR notation to a dotted decimal string value. IPv4 only.
+ ///
+ /// Subnet mask in CIDR notation.
+ /// IPv4 or IPv6 family.
+ /// String value of the subnet mask in dotted decimal notation.
+ public static IPAddress CidrToMask(int cidr, AddressFamily family)
+ {
+ uint addr = 0xFFFFFFFF << ((family == AddressFamily.InterNetwork ? 32 : 128) - cidr);
addr = ((addr & 0xff000000) >> 24)
| ((addr & 0x00ff0000) >> 8)
| ((addr & 0x0000ff00) << 8)
@@ -319,5 +335,19 @@ namespace MediaBrowser.Common.Net
addresses = Array.Empty();
return false;
}
+
+ ///
+ /// Gets the broadcast address for a .
+ ///
+ /// The .
+ /// The broadcast address.
+ public static IPAddress GetBroadcastAddress(IPNetwork network)
+ {
+ uint ipAddress = BitConverter.ToUInt32(network.Prefix.GetAddressBytes(), 0);
+ uint ipMaskV4 = BitConverter.ToUInt32(CidrToMask(network.PrefixLength, AddressFamily.InterNetwork).GetAddressBytes(), 0);
+ uint broadCastIpAddress = ipAddress | ~ipMaskV4;
+
+ return new IPAddress(BitConverter.GetBytes(broadCastIpAddress));
+ }
}
}