From 80fac82c2c68ec09b82d213370a3a3e0f1029df2 Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 18 Apr 2024 00:44:20 +0800 Subject: [PATCH] fix: bind auto-discovery to multicast ip on macOS (#11368) --- src/Jellyfin.Networking/AutoDiscoveryHost.cs | 40 +++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/Jellyfin.Networking/AutoDiscoveryHost.cs b/src/Jellyfin.Networking/AutoDiscoveryHost.cs index 2be57d7a1e..f1b79b4b4f 100644 --- a/src/Jellyfin.Networking/AutoDiscoveryHost.cs +++ b/src/Jellyfin.Networking/AutoDiscoveryHost.cs @@ -11,6 +11,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Model.ApiClient; +using MediaBrowser.Model.Net; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -50,6 +51,25 @@ public sealed class AutoDiscoveryHost : BackgroundService _networkManager = networkManager; } + private static IPAddress GetBindAddress(IPData intf) + { + if (intf.AddressFamily == AddressFamily.InterNetwork) + { + if (OperatingSystem.IsLinux()) + { + return NetworkUtils.GetBroadcastAddress(intf.Subnet); + } + + if (OperatingSystem.IsMacOS()) + { + // macOS kernel does not allow bind to 127:255:255:255 + return IPAddress.IsLoopback(intf.Address) ? intf.Address : NetworkUtils.GetBroadcastAddress(intf.Subnet); + } + } + + return intf.Address; + } + /// protected override async Task ExecuteAsync(CancellationToken stoppingToken) { @@ -63,24 +83,23 @@ public sealed class AutoDiscoveryHost : BackgroundService // Linux needs to bind to the broadcast addresses to receive broadcast traffic if (OperatingSystem.IsLinux() && networkConfig.EnableIPv4) { - udpServers.Add(ListenForAutoDiscoveryMessage(IPAddress.Broadcast, stoppingToken)); + udpServers.Add(ListenForAutoDiscoveryMessage(IPAddress.Broadcast, IPAddress.Broadcast, stoppingToken)); } udpServers.AddRange(_networkManager.GetInternalBindAddresses() .Select(intf => ListenForAutoDiscoveryMessage( - OperatingSystem.IsLinux() && intf.AddressFamily == AddressFamily.InterNetwork - ? NetworkUtils.GetBroadcastAddress(intf.Subnet) - : intf.Address, + GetBindAddress(intf), + intf.Address, stoppingToken))); await Task.WhenAll(udpServers).ConfigureAwait(false); } - private async Task ListenForAutoDiscoveryMessage(IPAddress address, CancellationToken cancellationToken) + private async Task ListenForAutoDiscoveryMessage(IPAddress listenAddress, IPAddress respondAddress, CancellationToken cancellationToken) { try { - using var udpClient = new UdpClient(new IPEndPoint(address, PortNumber)); + using var udpClient = new UdpClient(new IPEndPoint(listenAddress, PortNumber)); udpClient.MulticastLoopback = false; while (!cancellationToken.IsCancellationRequested) @@ -91,7 +110,7 @@ public sealed class AutoDiscoveryHost : BackgroundService var text = Encoding.UTF8.GetString(result.Buffer); if (text.Contains("who is JellyfinServer?", StringComparison.OrdinalIgnoreCase)) { - await RespondToV2Message(udpClient, result.RemoteEndPoint, cancellationToken).ConfigureAwait(false); + await RespondToV2Message(respondAddress, result.RemoteEndPoint, cancellationToken).ConfigureAwait(false); } } catch (SocketException ex) @@ -107,11 +126,11 @@ public sealed class AutoDiscoveryHost : BackgroundService catch (Exception ex) { // Exception in this function will prevent the background service from restarting in-process. - _logger.LogError(ex, "Unable to bind to {Address}:{Port}", address, PortNumber); + _logger.LogError(ex, "Unable to bind to {Address}:{Port}", listenAddress, PortNumber); } } - private async Task RespondToV2Message(UdpClient udpClient, IPEndPoint endpoint, CancellationToken cancellationToken) + private async Task RespondToV2Message(IPAddress responderIp, IPEndPoint endpoint, CancellationToken cancellationToken) { var localUrl = _appHost.GetSmartApiUrl(endpoint.Address); if (string.IsNullOrEmpty(localUrl)) @@ -122,10 +141,11 @@ public sealed class AutoDiscoveryHost : BackgroundService var response = new ServerDiscoveryInfo(localUrl, _appHost.SystemId, _appHost.FriendlyName); + using var responder = new UdpClient(new IPEndPoint(responderIp, PortNumber)); try { _logger.LogDebug("Sending AutoDiscovery response"); - await udpClient + await responder .SendAsync(JsonSerializer.SerializeToUtf8Bytes(response).AsMemory(), endpoint, cancellationToken) .ConfigureAwait(false); }