using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Connect; using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using System; using System.IO; using System.Text; using System.Threading.Tasks; using MediaBrowser.Controller.Security; using MediaBrowser.Model.IO; using MediaBrowser.Model.Threading; namespace Emby.Server.Implementations.Connect { public class ConnectEntryPoint : IServerEntryPoint { private ITimer _timer; private IpAddressInfo _cachedIpAddress; private readonly IHttpClient _httpClient; private readonly IApplicationPaths _appPaths; private readonly ILogger _logger; private readonly IConnectManager _connectManager; private readonly INetworkManager _networkManager; private readonly IApplicationHost _appHost; private readonly IFileSystem _fileSystem; private readonly ITimerFactory _timerFactory; private readonly IEncryptionManager _encryption; public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem, ITimerFactory timerFactory, IEncryptionManager encryption) { _httpClient = httpClient; _appPaths = appPaths; _logger = logger; _networkManager = networkManager; _connectManager = connectManager; _appHost = appHost; _fileSystem = fileSystem; _timerFactory = timerFactory; _encryption = encryption; } public void Run() { LoadCachedAddress(); _timer = _timerFactory.Create(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(1)); ((ConnectManager)_connectManager).Start(); } private readonly string[] _ipLookups = { "http://bot.whatismyipaddress.com", "https://connect.emby.media/service/ip" }; private async void TimerCallback(object state) { IpAddressInfo validIpAddress = null; foreach (var ipLookupUrl in _ipLookups) { try { validIpAddress = await GetIpAddress(ipLookupUrl).ConfigureAwait(false); // Try to find the ipv4 address, if present if (validIpAddress.AddressFamily != IpAddressFamily.InterNetworkV6) { break; } } catch (HttpException) { } catch (Exception ex) { _logger.ErrorException("Error getting connection info", ex); } } // If this produced an ipv6 address, try again if (validIpAddress != null && validIpAddress.AddressFamily == IpAddressFamily.InterNetworkV6) { foreach (var ipLookupUrl in _ipLookups) { try { var newAddress = await GetIpAddress(ipLookupUrl, true).ConfigureAwait(false); // Try to find the ipv4 address, if present if (newAddress.AddressFamily != IpAddressFamily.InterNetworkV6) { validIpAddress = newAddress; break; } } catch (HttpException) { } catch (Exception ex) { _logger.ErrorException("Error getting connection info", ex); } } } if (validIpAddress != null) { ((ConnectManager)_connectManager).OnWanAddressResolved(validIpAddress); CacheAddress(validIpAddress); } } private async Task GetIpAddress(string lookupUrl, bool preferIpv4 = false) { // Sometimes whatismyipaddress might fail, but it won't do us any good having users raise alarms over it. var logErrors = false; #if DEBUG logErrors = true; #endif using (var stream = await _httpClient.Get(new HttpRequestOptions { Url = lookupUrl, UserAgent = "Emby/" + _appHost.ApplicationVersion, LogErrors = logErrors, // Seeing block length errors with our server EnableHttpCompression = false, PreferIpv4 = preferIpv4, BufferContent = false }).ConfigureAwait(false)) { using (var reader = new StreamReader(stream)) { var addressString = await reader.ReadToEndAsync().ConfigureAwait(false); return _networkManager.ParseIpAddress(addressString); } } } private string CacheFilePath { get { return Path.Combine(_appPaths.DataPath, "wan.dat"); } } private void CacheAddress(IpAddressInfo address) { if (_cachedIpAddress != null && _cachedIpAddress.Equals(address)) { // no need to update the file if the address has not changed return; } var path = CacheFilePath; try { _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); } catch (Exception ex) { } try { _fileSystem.WriteAllText(path, _encryption.EncryptString(address.ToString()), Encoding.UTF8); _cachedIpAddress = address; } catch (Exception ex) { _logger.ErrorException("Error saving data", ex); } } private void LoadCachedAddress() { var path = CacheFilePath; _logger.Info("Loading data from {0}", path); try { var endpoint = _encryption.DecryptString(_fileSystem.ReadAllText(path, Encoding.UTF8)); IpAddressInfo ipAddress; if (_networkManager.TryParseIpAddress(endpoint, out ipAddress)) { _cachedIpAddress = ipAddress; ((ConnectManager)_connectManager).OnWanAddressResolved(ipAddress); } } catch (IOException) { // File isn't there. no biggie } catch (Exception ex) { _logger.ErrorException("Error loading data", ex); } } public void Dispose() { if (_timer != null) { _timer.Dispose(); _timer = null; } } } }