From 085e597a2e0be1aa7341946201de774684031dd4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 27 May 2013 14:34:03 -0400 Subject: [PATCH] improve accuracy of local ip address discovery --- .../NetworkManagement/NetworkManager.cs | 22 ++++--- MediaBrowser.Common/Net/IHttpServer.cs | 6 ++ MediaBrowser.Common/Net/INetworkManager.cs | 2 +- .../HttpServer/HttpServer.cs | 29 +++++++-- .../Udp/UdpServer.cs | 60 +++++++++++++++++-- .../EntryPoints/UdpServerEntryPoint.cs | 40 ++++++++++++- 6 files changed, 133 insertions(+), 26 deletions(-) diff --git a/MediaBrowser.Common.Implementations/NetworkManagement/NetworkManager.cs b/MediaBrowser.Common.Implementations/NetworkManagement/NetworkManager.cs index 3c42d340b8..6db00064cf 100644 --- a/MediaBrowser.Common.Implementations/NetworkManagement/NetworkManager.cs +++ b/MediaBrowser.Common.Implementations/NetworkManagement/NetworkManager.cs @@ -1,11 +1,11 @@ -using System.Globalization; -using System.Management; -using MediaBrowser.Common.Net; +using MediaBrowser.Common.Net; using MediaBrowser.Model.Net; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; +using System.Management; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; @@ -21,18 +21,16 @@ namespace MediaBrowser.Common.Implementations.NetworkManagement /// Gets the machine's local ip address /// /// IPAddress. - public string GetLocalIpAddress() + public IEnumerable GetLocalIpAddresses() { var host = Dns.GetHostEntry(Dns.GetHostName()); - var ip = host.AddressList.LastOrDefault(i => i.AddressFamily == AddressFamily.InterNetwork); - - if (ip == null) - { - return null; - } - - return ip.ToString(); + // Reverse them because the last one is usually the correct one + // It's not fool-proof so ultimately the consumer will have to examine them and decide + return host.AddressList + .Where(i => i.AddressFamily == AddressFamily.InterNetwork) + .Select(i => i.ToString()) + .Reverse(); } /// diff --git a/MediaBrowser.Common/Net/IHttpServer.cs b/MediaBrowser.Common/Net/IHttpServer.cs index 45da18e1b8..8bbaac0391 100644 --- a/MediaBrowser.Common/Net/IHttpServer.cs +++ b/MediaBrowser.Common/Net/IHttpServer.cs @@ -26,6 +26,12 @@ namespace MediaBrowser.Common.Net /// true if [supports web sockets]; otherwise, false. bool SupportsWebSockets { get; } + /// + /// Gets the local end points. + /// + /// The local end points. + IEnumerable LocalEndPoints { get; } + /// /// Stops this instance. /// diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index 782855cd14..8265cfeb7b 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Common.Net /// Gets the machine's local ip address /// /// IPAddress. - string GetLocalIpAddress(); + IEnumerable GetLocalIpAddresses(); /// /// Gets a random port number that is currently available diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpServer.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpServer.cs index 01b867bdb7..893715b733 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpServer.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpServer.cs @@ -14,6 +14,7 @@ using ServiceStack.WebHost.Endpoints; using ServiceStack.WebHost.Endpoints.Extensions; using ServiceStack.WebHost.Endpoints.Support; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -76,6 +77,17 @@ namespace MediaBrowser.Server.Implementations.HttpServer /// private readonly ContainerAdapter _containerAdapter; + private readonly ConcurrentDictionary _localEndPoints = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Gets the local end points. + /// + /// The local end points. + public IEnumerable LocalEndPoints + { + get { return _localEndPoints.Keys.ToList(); } + } + /// /// Initializes a new instance of the class. /// @@ -340,15 +352,24 @@ namespace MediaBrowser.Server.Implementations.HttpServer /// The CTX. private void LogHttpRequest(HttpListenerContext ctx) { - var log = new StringBuilder(); + var endpoint = ctx.Request.LocalEndPoint; - log.AppendLine("Url: " + ctx.Request.Url); - log.AppendLine("Headers: " + string.Join(",", ctx.Request.Headers.AllKeys.Select(k => k + "=" + ctx.Request.Headers[k]))); + if (endpoint != null) + { + var address = endpoint.ToString(); - var type = ctx.Request.IsWebSocketRequest ? "Web Socket" : "HTTP " + ctx.Request.HttpMethod; + _localEndPoints.AddOrUpdate(address, address, (key, existing) => address); + } if (EnableHttpRequestLogging) { + var log = new StringBuilder(); + + log.AppendLine("Url: " + ctx.Request.Url); + log.AppendLine("Headers: " + string.Join(",", ctx.Request.Headers.AllKeys.Select(k => k + "=" + ctx.Request.Headers[k]))); + + var type = ctx.Request.IsWebSocketRequest ? "Web Socket" : "HTTP " + ctx.Request.HttpMethod; + _logger.LogMultiline(type + " request received from " + ctx.Request.RemoteEndPoint, LogSeverity.Debug, log); } } diff --git a/MediaBrowser.Server.Implementations/Udp/UdpServer.cs b/MediaBrowser.Server.Implementations/Udp/UdpServer.cs index 17f5b7b15c..af32f08de1 100644 --- a/MediaBrowser.Server.Implementations/Udp/UdpServer.cs +++ b/MediaBrowser.Server.Implementations/Udp/UdpServer.cs @@ -1,13 +1,14 @@ -using System.Linq; -using MediaBrowser.Common.Implementations.NetworkManagement; +using MediaBrowser.Common.Implementations.NetworkManagement; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Logging; using System; +using System.Linq; using System.Net; using System.Net.Sockets; using System.Reactive.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.Udp @@ -22,8 +23,18 @@ namespace MediaBrowser.Server.Implementations.Udp /// private readonly ILogger _logger; + /// + /// The _network manager + /// private readonly INetworkManager _networkManager; + /// + /// The _HTTP server + /// + private readonly IHttpServer _httpServer; + /// + /// The _server configuration manager + /// private readonly IServerConfigurationManager _serverConfigurationManager; /// @@ -32,11 +43,15 @@ namespace MediaBrowser.Server.Implementations.Udp /// The logger. /// The network manager. /// The server configuration manager. - public UdpServer(ILogger logger, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager) + /// The HTTP server. + public UdpServer(ILogger logger, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager, IHttpServer httpServer) { _logger = logger; _networkManager = networkManager; _serverConfigurationManager = serverConfigurationManager; + _httpServer = httpServer; + + new Timer(state => _logger.Info("Internal address {0}", GetLocalIpAddress()), null, 0, 1000); } /// @@ -54,13 +69,41 @@ namespace MediaBrowser.Server.Implementations.Udp { _logger.Info("Received UDP server request from " + e.RemoteEndPoint); - // Send a response back with our ip address and port - var response = String.Format("MediaBrowser{0}|{1}:{2}", context, _networkManager.GetLocalIpAddress(), _serverConfigurationManager.Configuration.HttpServerPortNumber); + var localAddress = GetLocalIpAddress(); - await SendAsync(Encoding.UTF8.GetBytes(response), e.RemoteEndPoint); + if (!string.IsNullOrEmpty(localAddress)) + { + // Send a response back with our ip address and port + var response = String.Format("MediaBrowser{0}|{1}:{2}", context, GetLocalIpAddress(), _serverConfigurationManager.Configuration.HttpServerPortNumber); + + await SendAsync(Encoding.UTF8.GetBytes(response), e.RemoteEndPoint); + } + else + { + _logger.Warn("Unable to respond to udp request because the local ip address could not be determined."); + } } } + /// + /// Gets the local ip address. + /// + /// 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 + return matchedAddress ?? localAddresses.FirstOrDefault(); + } + /// /// The _udp client /// @@ -203,6 +246,11 @@ namespace MediaBrowser.Server.Implementations.Udp /// The bytes. /// The remote end point. /// Task. + /// + /// bytes + /// or + /// remoteEndPoint + /// public async Task SendAsync(byte[] bytes, string remoteEndPoint) { if (bytes == null) diff --git a/MediaBrowser.ServerApplication/EntryPoints/UdpServerEntryPoint.cs b/MediaBrowser.ServerApplication/EntryPoints/UdpServerEntryPoint.cs index af4ce84e3e..595d5c89fc 100644 --- a/MediaBrowser.ServerApplication/EntryPoints/UdpServerEntryPoint.cs +++ b/MediaBrowser.ServerApplication/EntryPoints/UdpServerEntryPoint.cs @@ -7,6 +7,9 @@ using System.Net.Sockets; namespace MediaBrowser.ServerApplication.EntryPoints { + /// + /// Class UdpServerEntryPoint + /// public class UdpServerEntryPoint : IServerEntryPoint { /// @@ -15,20 +18,44 @@ namespace MediaBrowser.ServerApplication.EntryPoints /// The UDP server. private UdpServer UdpServer { get; set; } + /// + /// The _logger + /// private readonly ILogger _logger; + /// + /// The _network manager + /// private readonly INetworkManager _networkManager; + /// + /// The _server configuration manager + /// private readonly IServerConfigurationManager _serverConfigurationManager; + /// + /// The _HTTP server + /// + private readonly IHttpServer _httpServer; - public UdpServerEntryPoint(ILogger logger, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager) + /// + /// Initializes a new instance of the class. + /// + /// The logger. + /// The network manager. + /// The server configuration manager. + /// The HTTP server. + public UdpServerEntryPoint(ILogger logger, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager, IHttpServer httpServer) { _logger = logger; _networkManager = networkManager; _serverConfigurationManager = serverConfigurationManager; + _httpServer = httpServer; } - + + /// + /// Runs this instance. + /// public void Run() { - var udpServer = new UdpServer(_logger, _networkManager, _serverConfigurationManager); + var udpServer = new UdpServer(_logger, _networkManager, _serverConfigurationManager, _httpServer); try { @@ -42,11 +69,18 @@ namespace MediaBrowser.ServerApplication.EntryPoints } } + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// public void Dispose() { Dispose(true); } + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool dispose) { if (dispose)