From 6130cb2403662596bba0474494372446d9caa3c3 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 16 Sep 2014 23:04:10 -0400 Subject: [PATCH] adjust discovery of local ip addresses --- .../Networking/BaseNetworkManager.cs | 28 ++++++++++--- .../IServerApplicationHost.cs | 9 +++- MediaBrowser.Dlna/Main/DlnaEntryPoint.cs | 2 +- MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs | 40 +++++++++--------- .../HttpServer/HttpListenerHost.cs | 28 ++++++++++--- .../ApplicationHost.cs | 42 ++++++++++++++----- 6 files changed, 107 insertions(+), 42 deletions(-) diff --git a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs index 8159be6340..46f8806573 100644 --- a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs +++ b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs @@ -24,14 +24,32 @@ namespace MediaBrowser.Common.Implementations.Networking /// IPAddress. public IEnumerable GetLocalIpAddresses() { - var list = GetIPsDefault().Where(i => !IPAddress.IsLoopback(i)).Select(i => i.ToString()).ToList(); + var list = GetIPsDefault() + .Where(i => !IPAddress.IsLoopback(i)) + .Select(i => i.ToString()) + .ToList(); - if (list.Count > 0) + try { - return list; - } + var listFromDns = Dns.GetHostAddresses(Dns.GetHostName()) + .Where(i => i.AddressFamily == AddressFamily.InterNetwork) + .Where(i => !IPAddress.IsLoopback(i)) + .Select(i => i.ToString()) + .ToList(); - return GetLocalIpAddressesFallback(); + if (listFromDns.Count > 0) + { + return listFromDns + .OrderBy(i => (list.Contains(i, StringComparer.OrdinalIgnoreCase) ? 0 : 1)) + .ToList(); + } + } + catch + { + + } + + return list; } public bool IsInLocalNetwork(string endpoint) diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index a48860b7eb..d1a9b386cc 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common; +using System.Collections.Generic; +using MediaBrowser.Common; using MediaBrowser.Model.System; using System; @@ -46,5 +47,11 @@ namespace MediaBrowser.Controller /// /// The name of the friendly. string FriendlyName { get; } + + /// + /// Gets the HTTP server ip addresses. + /// + /// The HTTP server ip addresses. + IEnumerable HttpServerIpAddresses { get; } } } diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs index 4d3e2272a9..1d88eca032 100644 --- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs +++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs @@ -111,7 +111,7 @@ namespace MediaBrowser.Dlna.Main _ssdpHandler.Start(); - _deviceDiscovery = new DeviceDiscovery(_logger, _config, _httpClient, _ssdpHandler, _network); + _deviceDiscovery = new DeviceDiscovery(_logger, _config, _ssdpHandler, _appHost); _deviceDiscovery.Start(); } diff --git a/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs b/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs index 56877d82d4..adc2d731ac 100644 --- a/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs +++ b/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Events; using MediaBrowser.Common.Net; +using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Logging; using System; @@ -21,21 +22,19 @@ namespace MediaBrowser.Dlna.Ssdp private readonly IServerConfigurationManager _config; private readonly SsdpHandler _ssdpHandler; private readonly CancellationTokenSource _tokenSource; - private readonly IHttpClient _httpClient; - private readonly INetworkManager _networkManager; + private readonly IServerApplicationHost _appHost; public event EventHandler DeviceDiscovered; public event EventHandler DeviceLeft; - public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, IHttpClient httpClient, SsdpHandler ssdpHandler, INetworkManager networkManager) + public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, SsdpHandler ssdpHandler, IServerApplicationHost appHost) { _tokenSource = new CancellationTokenSource(); _logger = logger; _config = config; - _httpClient = httpClient; _ssdpHandler = ssdpHandler; - _networkManager = networkManager; + _appHost = appHost; } public void Start() @@ -48,21 +47,21 @@ namespace MediaBrowser.Dlna.Ssdp if (!network.SupportsMulticast || OperationalStatus.Up != network.OperationalStatus || !network.GetIPProperties().MulticastAddresses.Any()) continue; - + var ipV4 = network.GetIPProperties().GetIPv4Properties(); if (null == ipV4) continue; - var localIp = network.GetIPProperties().UnicastAddresses + var localIps = network.GetIPProperties().UnicastAddresses .Where(i => i.Address.AddressFamily == AddressFamily.InterNetwork) .Select(i => i.Address) - .FirstOrDefault(); + .ToList(); - if (localIp != null) + foreach (var localIp in localIps) { try { - CreateListener(localIp, ipV4.Index); + CreateListener(localIp); } catch (Exception e) { @@ -71,7 +70,7 @@ namespace MediaBrowser.Dlna.Ssdp } } } - + void _ssdpHandler_MessageReceived(object sender, SsdpMessageEventArgs e) { string nts; @@ -87,9 +86,9 @@ namespace MediaBrowser.Dlna.Ssdp try { - var ip = _networkManager.GetLocalIpAddresses().FirstOrDefault(); + var ip = _appHost.HttpServerIpAddresses.FirstOrDefault(); - if (ip != null) + if (!string.IsNullOrWhiteSpace(ip)) { e.LocalIp = IPAddress.Parse(ip); TryCreateDevice(e); @@ -116,19 +115,17 @@ namespace MediaBrowser.Dlna.Ssdp return new List(); } } - private void CreateListener(IPAddress localIp, int networkInterfaceIndex) + private void CreateListener(IPAddress localIp) { Task.Factory.StartNew(async (o) => { try { - var socket = GetMulticastSocket(networkInterfaceIndex); - var endPoint = new IPEndPoint(localIp, 1900); - socket.Bind(endPoint); + var socket = GetMulticastSocket(localIp, endPoint); - _logger.Info("Creating SSDP listener on {0}, network interface index {1}", localIp, networkInterfaceIndex); + _logger.Info("Creating SSDP listener on {0}", localIp); var receiveBuffer = new byte[64000]; @@ -188,12 +185,15 @@ namespace MediaBrowser.Dlna.Ssdp } - private Socket GetMulticastSocket(int networkInterfaceIndex) + private Socket GetMulticastSocket(IPAddress localIpAddress, EndPoint localEndpoint) { var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), networkInterfaceIndex)); + socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIpAddress)); socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4); + + socket.Bind(localEndpoint); + return socket; } diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index 15d1eea6d9..d88a04063a 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -14,7 +14,6 @@ using ServiceStack.Host.HttpListener; using ServiceStack.Logging; using ServiceStack.Web; using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -40,15 +39,26 @@ namespace MediaBrowser.Server.Implementations.HttpServer public event EventHandler WebSocketConnected; - private readonly ConcurrentDictionary _localEndPoints = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - + private readonly List _localEndpoints = new List(); + + private readonly ReaderWriterLockSlim _localEndpointLock = new ReaderWriterLockSlim(); + /// /// Gets the local end points. /// /// The local end points. public IEnumerable LocalEndPoints { - get { return _listener == null ? new List() : _localEndPoints.Keys.ToList(); } + get + { + _localEndpointLock.EnterReadLock(); + + var list = _localEndpoints.ToList(); + + _localEndpointLock.ExitReadLock(); + + return list; + } } public HttpListenerHost(IApplicationHost applicationHost, ILogManager logManager, string serviceName, string handlerPath, string defaultRedirectPath, params Assembly[] assembliesWithServices) @@ -156,7 +166,15 @@ namespace MediaBrowser.Server.Implementations.HttpServer private void OnRequestReceived(string localEndPoint) { - _localEndPoints.GetOrAdd(localEndPoint, localEndPoint); + if (_localEndpointLock.TryEnterWriteLock(100)) + { + var list = _localEndpoints.ToList(); + + list.Remove(localEndPoint); + list.Insert(0, localEndPoint); + + _localEndpointLock.ExitWriteLock(); + } } /// diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 25d410bfc7..e05ae9fa16 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -950,17 +950,8 @@ namespace MediaBrowser.ServerApplication /// System.String. private string GetLocalIpAddress() { - var localAddresses = NetworkManager.GetLocalIpAddresses().ToList(); - - // Cross-check the local ip addresses with addresses that have been received on with the http server - var matchedAddress = HttpServer.LocalEndPoints - .ToList() - .Select(i => i.Split(':').FirstOrDefault()) - .Where(i => !string.IsNullOrEmpty(i)) - .FirstOrDefault(i => localAddresses.Contains(i, StringComparer.OrdinalIgnoreCase)); - // Return the first matched address, if found, or the first known local address - var address = matchedAddress ?? localAddresses.FirstOrDefault(); + var address = HttpServerIpAddresses.FirstOrDefault(); if (!string.IsNullOrWhiteSpace(address)) { @@ -972,6 +963,37 @@ namespace MediaBrowser.ServerApplication return address; } + public IEnumerable HttpServerIpAddresses + { + get + { + var localAddresses = NetworkManager.GetLocalIpAddresses() + .ToList(); + + if (localAddresses.Count < 2) + { + return localAddresses; + } + + var httpServerAddresses = HttpServer.LocalEndPoints + .Select(i => i.Split(':').FirstOrDefault()) + .Where(i => !string.IsNullOrEmpty(i)) + .ToList(); + + // Cross-check the local ip addresses with addresses that have been received on with the http server + var matchedAddresses = httpServerAddresses + .Where(i => localAddresses.Contains(i, StringComparer.OrdinalIgnoreCase)) + .ToList(); + + if (matchedAddresses.Count == 0) + { + return localAddresses.Take(1); + } + + return matchedAddresses; + } + } + public string FriendlyName { get