2
0
Эх сурвалжийг харах

Use Mono.Nat Nuget package

Bond_009 5 жил өмнө
parent
commit
b0a25c4237

+ 9 - 14
Emby.Dlna/Ssdp/DeviceDiscovery.cs

@@ -4,8 +4,6 @@ using System.Linq;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Net;
-using Microsoft.Extensions.Logging;
 using Rssdp;
 using Rssdp.Infrastructure;
 
@@ -15,13 +13,14 @@ namespace Emby.Dlna.Ssdp
     {
         private bool _disposed;
 
-        private readonly ILogger _logger;
         private readonly IServerConfigurationManager _config;
 
         private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
 
         private int _listenerCount;
         private object _syncLock = new object();
+
+        /// <inheritdoc />
         public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered
         {
             add
@@ -31,6 +30,7 @@ namespace Emby.Dlna.Ssdp
                     _listenerCount++;
                     DeviceDiscoveredInternal += value;
                 }
+
                 StartInternal();
             }
             remove
@@ -43,21 +43,16 @@ namespace Emby.Dlna.Ssdp
             }
         }
 
+        /// <inheritdoc />
         public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
 
         private SsdpDeviceLocator _deviceLocator;
 
-        private readonly ISocketFactory _socketFactory;
         private ISsdpCommunicationsServer _commsServer;
 
-        public DeviceDiscovery(
-            ILoggerFactory loggerFactory,
-            IServerConfigurationManager config,
-            ISocketFactory socketFactory)
+        public DeviceDiscovery(IServerConfigurationManager config)
         {
-            _logger = loggerFactory.CreateLogger(nameof(DeviceDiscovery));
             _config = config;
-            _socketFactory = socketFactory;
         }
 
         // Call this method from somewhere in your code to start the search.
@@ -82,8 +77,8 @@ namespace Emby.Dlna.Ssdp
                     //_DeviceLocator.NotificationFilter = "upnp:rootdevice";
 
                     // Connect our event handler so we process devices as they are found
-                    _deviceLocator.DeviceAvailable += deviceLocator_DeviceAvailable;
-                    _deviceLocator.DeviceUnavailable += _DeviceLocator_DeviceUnavailable;
+                    _deviceLocator.DeviceAvailable += OnDeviceLocatorDeviceAvailable;
+                    _deviceLocator.DeviceUnavailable += OnDeviceLocatorDeviceUnavailable;
 
                     var dueTime = TimeSpan.FromSeconds(5);
                     var interval = TimeSpan.FromSeconds(_config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds);
@@ -94,7 +89,7 @@ namespace Emby.Dlna.Ssdp
         }
 
         // Process each found device in the event handler
-        void deviceLocator_DeviceAvailable(object sender, DeviceAvailableEventArgs e)
+        private void OnDeviceLocatorDeviceAvailable(object sender, DeviceAvailableEventArgs e)
         {
             var originalHeaders = e.DiscoveredDevice.ResponseHeaders;
 
@@ -115,7 +110,7 @@ namespace Emby.Dlna.Ssdp
             DeviceDiscoveredInternal?.Invoke(this, args);
         }
 
-        private void _DeviceLocator_DeviceUnavailable(object sender, DeviceUnavailableEventArgs e)
+        private void OnDeviceLocatorDeviceUnavailable(object sender, DeviceUnavailableEventArgs e)
         {
             var originalHeaders = e.DiscoveredDevice.ResponseHeaders;
 

+ 2 - 3
Emby.Server.Implementations/ApplicationHost.cs

@@ -866,8 +866,7 @@ namespace Emby.Server.Implementations
             NotificationManager = new NotificationManager(LoggerFactory, UserManager, ServerConfigurationManager);
             serviceCollection.AddSingleton(NotificationManager);
 
-            serviceCollection.AddSingleton<IDeviceDiscovery>(
-                new DeviceDiscovery(LoggerFactory, ServerConfigurationManager, SocketFactory));
+            serviceCollection.AddSingleton<IDeviceDiscovery>(new DeviceDiscovery(ServerConfigurationManager));
 
             ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository);
             serviceCollection.AddSingleton(ChapterManager);
@@ -1730,7 +1729,7 @@ namespace Emby.Server.Implementations
         /// dns is prefixed with a valid Uri prefix.
         /// </summary>
         /// <param name="externalDns">The external dns prefix to get the hostname of.</param>
-        /// <returns>The hostname in <paramref name="externalDns"/></returns>
+        /// <returns>The hostname in <paramref name="externalDns"/>.</returns>
         private static string GetHostnameFromExternalDns(string externalDns)
         {
             if (string.IsNullOrEmpty(externalDns))

+ 1 - 1
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -10,7 +10,6 @@
     <ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj" />
     <ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj" />
     <ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
-    <ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj" />
     <ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj" />
     <ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj" />
     <ProjectReference Include="..\Emby.Photos\Emby.Photos.csproj" />
@@ -32,6 +31,7 @@
     <PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.0" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.0" />
+    <PackageReference Include="Mono.Nat" Version="2.0.0" />
     <PackageReference Include="ServiceStack.Text.Core" Version="5.6.0" />
     <PackageReference Include="sharpcompress" Version="0.24.0" />
     <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.0.1" />

+ 66 - 173
Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs

@@ -1,10 +1,9 @@
 using System;
 using System.Collections.Generic;
-using System.Globalization;
 using System.Net;
+using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Plugins;
@@ -19,57 +18,51 @@ namespace Emby.Server.Implementations.EntryPoints
     {
         private readonly IServerApplicationHost _appHost;
         private readonly ILogger _logger;
-        private readonly IHttpClient _httpClient;
         private readonly IServerConfigurationManager _config;
         private readonly IDeviceDiscovery _deviceDiscovery;
 
         private Timer _timer;
 
-        private NatManager _natManager;
-
         private readonly object _createdRulesLock = new object();
-        private List<string> _createdRules = new List<string>();
-        private readonly object _usnsHandledLock = new object();
-        private List<string> _usnsHandled = new List<string>();
+        private List<IPEndPoint> _createdRules = new List<IPEndPoint>();
+        private string _lastConfigIdentifier;
+
+        private bool _disposed = false;
 
-        public ExternalPortForwarding(ILoggerFactory loggerFactory, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient)
+        public ExternalPortForwarding(
+            ILogger<ExternalPortForwarding> logger,
+            IServerApplicationHost appHost,
+            IServerConfigurationManager config,
+            IDeviceDiscovery deviceDiscovery)
         {
-            _logger = loggerFactory.CreateLogger("PortMapper");
+            _logger = logger;
             _appHost = appHost;
             _config = config;
             _deviceDiscovery = deviceDiscovery;
-            _httpClient = httpClient;
-            _config.ConfigurationUpdated += _config_ConfigurationUpdated1;
         }
 
-        private void _config_ConfigurationUpdated1(object sender, EventArgs e)
-        {
-            _config_ConfigurationUpdated(sender, e);
-        }
-
-        private string _lastConfigIdentifier;
         private string GetConfigIdentifier()
         {
-            var values = new List<string>();
+            const char Separator = '|';
             var config = _config.Configuration;
 
-            values.Add(config.EnableUPnP.ToString());
-            values.Add(config.PublicPort.ToString(CultureInfo.InvariantCulture));
-            values.Add(_appHost.HttpPort.ToString(CultureInfo.InvariantCulture));
-            values.Add(_appHost.HttpsPort.ToString(CultureInfo.InvariantCulture));
-            values.Add(_appHost.EnableHttps.ToString());
-            values.Add((config.EnableRemoteAccess).ToString());
-
-            return string.Join("|", values.ToArray());
+            return new StringBuilder(32)
+                .Append(config.EnableUPnP).Append(Separator)
+                .Append(config.PublicPort).Append(Separator)
+                .Append(_appHost.HttpPort).Append(Separator)
+                .Append(_appHost.HttpsPort).Append(Separator)
+                .Append(_appHost.EnableHttps).Append(Separator)
+                .Append(config.EnableRemoteAccess).Append(Separator)
+                .ToString();
         }
 
-        private async void _config_ConfigurationUpdated(object sender, EventArgs e)
+        private async void OnConfigurationUpdated(object sender, EventArgs e)
         {
             if (!string.Equals(_lastConfigIdentifier, GetConfigIdentifier(), StringComparison.OrdinalIgnoreCase))
             {
-                DisposeNat();
+                Stop();
 
-                await RunAsync();
+                await RunAsync().ConfigureAwait(false);
             }
         }
 
@@ -80,8 +73,7 @@ namespace Emby.Server.Implementations.EntryPoints
                 Start();
             }
 
-            _config.ConfigurationUpdated -= _config_ConfigurationUpdated;
-            _config.ConfigurationUpdated += _config_ConfigurationUpdated;
+            _config.ConfigurationUpdated += OnConfigurationUpdated;
 
             return Task.CompletedTask;
         }
@@ -89,105 +81,27 @@ namespace Emby.Server.Implementations.EntryPoints
         private void Start()
         {
             _logger.LogDebug("Starting NAT discovery");
-            if (_natManager == null)
-            {
-                _natManager = new NatManager(_logger, _httpClient);
-                _natManager.DeviceFound += NatUtility_DeviceFound;
-                _natManager.StartDiscovery();
-            }
+
+            NatUtility.DeviceFound += OnNatUtilityDeviceFound;
+            NatUtility.StartDiscovery();
 
             _timer = new Timer(ClearCreatedRules, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
 
-            _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
+            _deviceDiscovery.DeviceDiscovered += OnDeviceDiscoveryDeviceDiscovered;
 
             _lastConfigIdentifier = GetConfigIdentifier();
         }
 
-        private async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
+        private void Stop()
         {
-            if (_disposed)
-            {
-                return;
-            }
-
-            var info = e.Argument;
-
-            if (!info.Headers.TryGetValue("USN", out string usn)) usn = string.Empty;
-
-            if (!info.Headers.TryGetValue("NT", out string nt)) nt = string.Empty;
-
-            // Filter device type
-            if (usn.IndexOf("WANIPConnection:", StringComparison.OrdinalIgnoreCase) == -1 &&
-                     nt.IndexOf("WANIPConnection:", StringComparison.OrdinalIgnoreCase) == -1 &&
-                     usn.IndexOf("WANPPPConnection:", StringComparison.OrdinalIgnoreCase) == -1 &&
-                     nt.IndexOf("WANPPPConnection:", StringComparison.OrdinalIgnoreCase) == -1)
-            {
-                return;
-            }
-
-            var identifier = string.IsNullOrWhiteSpace(usn) ? nt : usn;
-
-            if (info.Location == null)
-            {
-                return;
-            }
-
-            lock (_usnsHandledLock)
-            {
-                if (_usnsHandled.Contains(identifier))
-                {
-                    return;
-                }
-
-                _usnsHandled.Add(identifier);
-            }
-
-            _logger.LogDebug("Found NAT device: " + identifier);
-
-            if (IPAddress.TryParse(info.Location.Host, out var address))
-            {
-                // The Handle method doesn't need the port
-                var endpoint = new IPEndPoint(address, info.Location.Port);
-
-                IPAddress localAddress = null;
-
-                try
-                {
-                    var localAddressString = await _appHost.GetLocalApiUrl(CancellationToken.None).ConfigureAwait(false);
+            _logger.LogDebug("Stopping NAT discovery");
 
-                    if (Uri.TryCreate(localAddressString, UriKind.Absolute, out var uri))
-                    {
-                        localAddressString = uri.Host;
+            NatUtility.StopDiscovery();
+            NatUtility.DeviceFound -= OnNatUtilityDeviceFound;
 
-                        if (!IPAddress.TryParse(localAddressString, out localAddress))
-                        {
-                            return;
-                        }
-                    }
-                }
-                catch (Exception ex)
-                {
-                    _logger.LogError(ex, "Error");
-                    return;
-                }
+            _timer?.Dispose();
 
-                if (_disposed)
-                {
-                    return;
-                }
-
-                // This should never happen, but the Handle method will throw ArgumentNullException if it does
-                if (localAddress == null)
-                {
-                    return;
-                }
-
-                var natManager = _natManager;
-                if (natManager != null)
-                {
-                    await natManager.Handle(localAddress, info, endpoint, NatProtocol.Upnp).ConfigureAwait(false);
-                }
-            }
+            _deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered;
         }
 
         private void ClearCreatedRules(object state)
@@ -196,30 +110,24 @@ namespace Emby.Server.Implementations.EntryPoints
             {
                 _createdRules.Clear();
             }
-
-            lock (_usnsHandledLock)
-            {
-                _usnsHandled.Clear();
-            }
         }
 
-        void NatUtility_DeviceFound(object sender, DeviceEventArgs e)
+        private void OnDeviceDiscoveryDeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
         {
-            if (_disposed)
-            {
-                return;
-            }
+            NatUtility.Search(e.Argument.LocalIpAddress, NatProtocol.Upnp);
+        }
 
+        private void OnNatUtilityDeviceFound(object sender, DeviceEventArgs e)
+        {
             try
             {
                 var device = e.Device;
 
                 CreateRules(device);
             }
-            catch
+            catch (Exception ex)
             {
-                // Commenting out because users are reporting problems out of our control
-                //_logger.LogError(ex, "Error creating port forwarding rules");
+                _logger.LogError(ex, "Error creating port forwarding rules");
             }
         }
 
@@ -232,15 +140,13 @@ namespace Emby.Server.Implementations.EntryPoints
 
             // On some systems the device discovered event seems to fire repeatedly
             // This check will help ensure we're not trying to port map the same device over and over
-            var address = device.LocalAddress;
-
-            var addressString = address.ToString();
+            var address = device.DeviceEndpoint;
 
             lock (_createdRulesLock)
             {
-                if (!_createdRules.Contains(addressString))
+                if (!_createdRules.Contains(address))
                 {
-                    _createdRules.Add(addressString);
+                    _createdRules.Add(address);
                 }
                 else
                 {
@@ -268,54 +174,41 @@ namespace Emby.Server.Implementations.EntryPoints
             }
         }
 
-        private Task CreatePortMap(INatDevice device, int privatePort, int publicPort)
+        private Task<Mapping> CreatePortMap(INatDevice device, int privatePort, int publicPort)
         {
-            _logger.LogDebug("Creating port map on local port {0} to public port {1} with device {2}", privatePort, publicPort, device.LocalAddress.ToString());
-
-            return device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
-            {
-                Description = _appHost.Name
-            });
+            _logger.LogDebug(
+                "Creating port map on local port {0} to public port {1} with device {2}",
+                privatePort,
+                publicPort,
+                device.DeviceEndpoint);
+
+            return device.CreatePortMapAsync(
+                new Mapping(Protocol.Tcp, privatePort, publicPort, 0, _appHost.Name));
         }
 
-        private bool _disposed = false;
+        /// <inheritdoc />
         public void Dispose()
         {
-            _disposed = true;
-            DisposeNat();
+            Dispose(true);
+            GC.SuppressFinalize(this);
         }
 
-        private void DisposeNat()
+        /// <summary>
+        /// Releases unmanaged and - optionally - managed resources.
+        /// </summary>
+        /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+        protected virtual void Dispose(bool dispose)
         {
-            _logger.LogDebug("Stopping NAT discovery");
-
-            if (_timer != null)
+            if (_disposed)
             {
-                _timer.Dispose();
-                _timer = null;
+                return;
             }
 
-            _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
-
-            var natManager = _natManager;
+            Stop();
 
-            if (natManager != null)
-            {
-                _natManager = null;
+            _timer = null;
 
-                using (natManager)
-                {
-                    try
-                    {
-                        natManager.StopDiscovery();
-                        natManager.DeviceFound -= NatUtility_DeviceFound;
-                    }
-                    catch (Exception ex)
-                    {
-                        _logger.LogError(ex, "Error stopping NAT Discovery");
-                    }
-                }
-            }
+            _disposed = true;
         }
     }
 }

+ 0 - 6
MediaBrowser.sln

@@ -33,8 +33,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RSSDP", "RSSDP\RSSDP.csproj
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Nat", "Mono.Nat\Mono.Nat.csproj", "{CB7F2326-6497-4A3D-BA03-48513B17A7BE}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Notifications", "Emby.Notifications\Emby.Notifications.csproj", "{2E030C33-6923-4530-9E54-FA29FA6AD1A9}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Naming", "Emby.Naming\Emby.Naming.csproj", "{E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}"
@@ -129,10 +127,6 @@ Global
 		{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Any CPU.Build.0 = Release|Any CPU
-		{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release|Any CPU.Build.0 = Release|Any CPU
 		{2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|Any CPU.ActiveCfg = Release|Any CPU

+ 0 - 55
Mono.Nat/AbstractNatDevice.cs

@@ -1,55 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//   Ben Motmans <ben.motmans@gmail.com>
-//
-// Copyright (C) 2006 Alan McGovern
-// Copyright (C) 2007 Ben Motmans
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Net;
-using System.Threading.Tasks;
-
-namespace Mono.Nat
-{
-    public abstract class AbstractNatDevice : INatDevice
-    {
-        private DateTime lastSeen;
-
-        protected AbstractNatDevice()
-        {
-        }
-
-        public abstract IPAddress LocalAddress { get; }
-
-        public DateTime LastSeen
-        {
-            get { return lastSeen; }
-            set { lastSeen = value; }
-        }
-
-        public abstract Task CreatePortMap(Mapping mapping);
-    }
-}

+ 0 - 36
Mono.Nat/Enums/ProtocolType.cs

@@ -1,36 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//
-// Copyright (C) 2006 Alan McGovern
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-
-namespace Mono.Nat
-{
-    public enum Protocol
-    {
-        Tcp,
-        Udp
-    }
-}

+ 0 - 45
Mono.Nat/EventArgs/DeviceEventArgs.cs

@@ -1,45 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//
-// Copyright (C) 2006 Alan McGovern
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-
-namespace Mono.Nat
-{
-    public class DeviceEventArgs : EventArgs
-    {
-        private INatDevice device;
-
-        public DeviceEventArgs(INatDevice device)
-        {
-            this.device = device;
-        }
-
-        public INatDevice Device
-        {
-            get { return this.device; }
-        }
-    }
-}

+ 0 - 45
Mono.Nat/INatDevice.cs

@@ -1,45 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//   Ben Motmans <ben.motmans@gmail.com>
-//
-// Copyright (C) 2006 Alan McGovern
-// Copyright (C) 2007 Ben Motmans
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Net;
-using System.Threading.Tasks;
-
-namespace Mono.Nat
-{
-    public interface INatDevice
-    {
-        Task CreatePortMap (Mapping mapping);
-
-        IPAddress LocalAddress { get; }
-
-        DateTime LastSeen { get; set; }
-    }
-}

+ 0 - 46
Mono.Nat/ISearcher.cs

@@ -1,46 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//   Ben Motmans <ben.motmans@gmail.com>
-//   Nicholas Terry <nick.i.terry@gmail.com>
-//
-// Copyright (C) 2006 Alan McGovern
-// Copyright (C) 2007 Ben Motmans
-// Copyright (C) 2014 Nicholas Terry
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Net.Sockets;
-using System.Net;
-
-namespace Mono.Nat
-{
-    internal interface ISearcher
-    {
-        event EventHandler<DeviceEventArgs> DeviceFound;
-
-        void Search();
-        void Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint);
-    }
-}

+ 0 - 121
Mono.Nat/Mapping.cs

@@ -1,121 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//   Ben Motmans <ben.motmans@gmail.com>
-//
-// Copyright (C) 2006 Alan McGovern
-// Copyright (C) 2007 Ben Motmans
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-
-namespace Mono.Nat
-{
-    public class Mapping
-    {
-        private string description;
-        private DateTime expiration;
-        private int lifetime;
-        private int privatePort;
-        private Protocol protocol;
-        private int publicPort;
-
-        public Mapping(Protocol protocol, int privatePort, int publicPort)
-            : this (protocol, privatePort, publicPort, 0)
-        {
-        }
-
-        public Mapping(Protocol protocol, int privatePort, int publicPort, int lifetime)
-        {
-            this.protocol = protocol;
-            this.privatePort = privatePort;
-            this.publicPort = publicPort;
-            this.lifetime = lifetime;
-
-            if (lifetime == int.MaxValue)
-                this.expiration = DateTime.MaxValue;
-            else if (lifetime == 0)
-                this.expiration = DateTime.Now;
-            else
-                this.expiration = DateTime.Now.AddSeconds (lifetime);
-        }
-
-        public string Description
-        {
-            get { return description; }
-            set { description = value; }
-        }
-
-        public Protocol Protocol
-        {
-            get { return protocol; }
-            internal set { protocol = value; }
-        }
-
-        public int PrivatePort
-        {
-            get { return privatePort; }
-            internal set { privatePort = value; }
-        }
-
-        public int PublicPort
-        {
-            get { return publicPort; }
-            internal set { publicPort = value; }
-        }
-
-        public int Lifetime
-        {
-            get { return lifetime; }
-            internal set { lifetime = value; }
-        }
-
-        public DateTime Expiration
-        {
-            get { return expiration; }
-            internal set { expiration = value; }
-        }
-
-        public bool IsExpired()
-        {
-            return expiration < DateTime.Now;
-        }
-
-        public override bool Equals(object obj)
-        {
-            var other = obj as Mapping;
-            return other == null ? false : this.protocol == other.protocol &&
-            this.privatePort == other.privatePort && this.publicPort == other.publicPort;
-        }
-
-        public override int GetHashCode()
-        {
-            return this.protocol.GetHashCode() ^ this.privatePort.GetHashCode() ^ this.publicPort.GetHashCode();
-        }
-
-        public override string ToString()
-        {
-            return String.Format( "Protocol: {0}, Public Port: {1}, Private Port: {2}, Description: {3}, Expiration: {4}, Lifetime: {5}",
-            this.protocol, this.publicPort, this.privatePort, this.description, this.expiration, this.lifetime );
-        }
-    }
-}

+ 0 - 17
Mono.Nat/Mono.Nat.csproj

@@ -1,17 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <ItemGroup>
-    <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
-    <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Compile Include="..\SharedVersion.cs" />
-  </ItemGroup>
-
-  <PropertyGroup>
-    <TargetFramework>netstandard2.1</TargetFramework>
-    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
-  </PropertyGroup>
-
-</Project>

+ 0 - 86
Mono.Nat/NatManager.cs

@@ -1,86 +0,0 @@
-using System;
-using System.Net;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Dlna;
-using Microsoft.Extensions.Logging;
-using System.Linq;
-
-namespace Mono.Nat
-{
-    public class NatManager : IDisposable
-    {
-        public event EventHandler<DeviceEventArgs> DeviceFound;
-
-        private List<ISearcher> controllers = new List<ISearcher>();
-
-        private ILogger Logger;
-        private IHttpClient HttpClient;
-
-        public NatManager(ILogger logger, IHttpClient httpClient)
-        {
-            Logger = logger;
-            HttpClient = httpClient;
-        }
-
-        private object _runSyncLock = new object();
-        public void StartDiscovery()
-        {
-            lock (_runSyncLock)
-            {
-                if (controllers.Count > 0)
-                {
-                    return;
-                }
-
-                controllers.Add(new PmpSearcher(Logger));
-
-                foreach (var searcher in controllers)
-                {
-                    searcher.DeviceFound += Searcher_DeviceFound;
-                }
-            }
-        }
-
-        public void StopDiscovery()
-        {
-            lock (_runSyncLock)
-            {
-                var disposables = controllers.OfType<IDisposable>().ToList();
-                controllers.Clear();
-
-                foreach (var disposable in disposables)
-                {
-                    disposable.Dispose();
-                }
-            }
-        }
-
-        public void Dispose()
-        {
-            StopDiscovery();
-        }
-
-        public Task Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint, NatProtocol protocol)
-        {
-            switch (protocol)
-            {
-                case NatProtocol.Upnp:
-                    var searcher = new UpnpSearcher(Logger, HttpClient);
-                    searcher.DeviceFound += Searcher_DeviceFound;
-                    return searcher.Handle(localAddress, deviceInfo, endpoint);
-                default:
-                    throw new ArgumentException("Unexpected protocol: " + protocol);
-            }
-        }
-
-        private void Searcher_DeviceFound(object sender, DeviceEventArgs e)
-        {
-            if (DeviceFound != null)
-            {
-                DeviceFound(sender, e);
-            }
-        }
-    }
-}

+ 0 - 8
Mono.Nat/NatProtocol.cs

@@ -1,8 +0,0 @@
-namespace Mono.Nat
-{
-    public enum NatProtocol
-    {
-        Upnp = 0,
-        Pmp = 1
-    }
-}

+ 0 - 56
Mono.Nat/Pmp/PmpConstants.cs

@@ -1,56 +0,0 @@
-//
-// Authors:
-//   Ben Motmans <ben.motmans@gmail.com>
-//
-// Copyright (C) 2007 Ben Motmans
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-
-namespace Mono.Nat.Pmp
-{
-    internal static class PmpConstants
-    {
-        public const byte Version = (byte)0;
-
-        public const byte OperationCode = (byte)0;
-        public const byte OperationCodeUdp = (byte)1;
-        public const byte OperationCodeTcp = (byte)2;
-        public const byte ServerNoop = (byte)128;
-
-        public const int ClientPort = 5350;
-        public const int ServerPort = 5351;
-
-        public const int RetryDelay = 250;
-        public const int RetryAttempts = 9;
-
-        public const int RecommendedLeaseTime = 60 * 60;
-        public const int DefaultLeaseTime = RecommendedLeaseTime;
-
-        public const short ResultCodeSuccess = 0;
-        public const short ResultCodeUnsupportedVersion = 1;
-        public const short ResultCodeNotAuthorized = 2;
-        public const short ResultCodeNetworkFailure = 3;
-        public const short ResultCodeOutOfResources = 4;
-        public const short ResultCodeUnsupportedOperationCode = 5;
-    }
-}

+ 0 - 217
Mono.Nat/Pmp/PmpNatDevice.cs

@@ -1,217 +0,0 @@
-//
-// Authors:
-//   Ben Motmans <ben.motmans@gmail.com>
-//
-// Copyright (C) 2007 Ben Motmans
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.IO;
-using System.Net;
-using System.Net.Sockets;
-using System.Threading;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Extensions;
-using Microsoft.Extensions.Logging;
-
-namespace Mono.Nat.Pmp
-{
-    internal sealed class PmpNatDevice : AbstractNatDevice, IEquatable<PmpNatDevice>
-    {
-        private IPAddress localAddress;
-        private IPAddress publicAddress;
-        private ILogger _logger;
-
-        internal PmpNatDevice(IPAddress localAddress, IPAddress publicAddress, ILogger logger)
-        {
-            if (localAddress == null)
-            {
-                throw new ArgumentNullException(nameof(localAddress));
-            }
-
-            this.localAddress = localAddress;
-            this.publicAddress = publicAddress;
-            _logger = logger;
-        }
-
-        public override IPAddress LocalAddress
-        {
-            get { return localAddress; }
-        }
-
-        public override Task CreatePortMap(Mapping mapping)
-        {
-            return InternalCreatePortMapAsync(mapping, true);
-        }
-
-        public override bool Equals(object obj)
-        {
-            var device = obj as PmpNatDevice;
-            return (device == null) ? false : this.Equals(device);
-        }
-
-        public override int GetHashCode()
-        {
-            return this.publicAddress.GetHashCode();
-        }
-
-        public bool Equals(PmpNatDevice other)
-        {
-            return (other == null) ? false : this.publicAddress.Equals(other.publicAddress);
-        }
-
-        private async Task<Mapping> InternalCreatePortMapAsync(Mapping mapping, bool create)
-        {
-            var package = new List<byte>();
-
-            package.Add(PmpConstants.Version);
-            package.Add(mapping.Protocol == Protocol.Tcp ? PmpConstants.OperationCodeTcp : PmpConstants.OperationCodeUdp);
-            package.Add(0); //reserved
-            package.Add(0); //reserved
-            package.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)mapping.PrivatePort)));
-            package.AddRange(
-                BitConverter.GetBytes(create ? IPAddress.HostToNetworkOrder((short)mapping.PublicPort) : (short)0));
-            package.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(mapping.Lifetime)));
-
-            try
-            {
-                byte[] buffer = package.ToArray();
-                int attempt = 0;
-                int delay = PmpConstants.RetryDelay;
-
-                using (var udpClient = new UdpClient())
-                {
-                    var cancellationTokenSource = new CancellationTokenSource();
-
-                    while (attempt < PmpConstants.RetryAttempts)
-                    {
-                        await udpClient.SendAsync(buffer, buffer.Length, new IPEndPoint(LocalAddress, PmpConstants.ServerPort));
-
-                        if (attempt == 0)
-                        {
-                            await Task.Run(() => CreatePortMapListen(udpClient, mapping, cancellationTokenSource.Token));
-                        }
-
-                        attempt++;
-                        delay *= 2;
-                        await Task.Delay(delay).ConfigureAwait(false);
-                    }
-
-                    cancellationTokenSource.Cancel();
-                }
-            }
-            catch (OperationCanceledException)
-            {
-
-            }
-            catch (Exception e)
-            {
-                string type = create ? "create" : "delete";
-                string message = String.Format("Failed to {0} portmap (protocol={1}, private port={2}) {3}",
-                                               type,
-                                               mapping.Protocol,
-                                               mapping.PrivatePort,
-                                               e.Message);
-                _logger.LogDebug(message);
-                throw e;
-            }
-
-            return mapping;
-        }
-
-        private async void CreatePortMapListen(UdpClient udpClient, Mapping mapping, CancellationToken cancellationToken)
-        {
-            while (!cancellationToken.IsCancellationRequested)
-            {
-                try
-                {
-                    var result = await udpClient.ReceiveAsync().ConfigureAwait(false);
-                    var endPoint = result.RemoteEndPoint;
-                    byte[] data = data = result.Buffer;
-
-                    if (data.Length < 16)
-                        continue;
-
-                    if (data[0] != PmpConstants.Version)
-                        continue;
-
-                    var opCode = (byte)(data[1] & 127);
-
-                    var protocol = Protocol.Tcp;
-                    if (opCode == PmpConstants.OperationCodeUdp)
-                        protocol = Protocol.Udp;
-
-                    short resultCode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 2));
-                    int epoch = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 4));
-
-                    short privatePort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 8));
-                    short publicPort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 10));
-
-                    var lifetime = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 12));
-
-                    if (privatePort < 0 || publicPort < 0 || resultCode != PmpConstants.ResultCodeSuccess)
-                    {
-                        var errors = new[]
-                                         {
-                                         "Success",
-                                         "Unsupported Version",
-                                         "Not Authorized/Refused (e.g. box supports mapping, but user has turned feature off)"
-                                         ,
-                                         "Network Failure (e.g. NAT box itself has not obtained a DHCP lease)",
-                                         "Out of resources (NAT box cannot create any more mappings at this time)",
-                                         "Unsupported opcode"
-                                     };
-
-                        var errorMsg = errors[resultCode];
-                        _logger.LogDebug("Error in CreatePortMapListen: " + errorMsg);
-                        return;
-                    }
-
-                    if (lifetime == 0) return; //mapping was deleted
-
-                    //mapping was created
-                    //TODO: verify that the private port+protocol are a match
-                    mapping.PublicPort = publicPort;
-                    mapping.Protocol = protocol;
-                    mapping.Expiration = DateTime.Now.AddSeconds(lifetime);
-                    return;
-                }
-                catch (Exception ex)
-                {
-                    _logger.LogError(ex, "Error in CreatePortMapListen");
-                    return;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Overridden.
-        /// </summary>
-        /// <returns></returns>
-        public override string ToString()
-        {
-            return String.Format("PmpNatDevice - Local Address: {0}, Public IP: {1}, Last Seen: {2}",
-                this.localAddress, this.publicAddress, this.LastSeen);
-        }
-    }
-}

+ 0 - 235
Mono.Nat/Pmp/PmpSearcher.cs

@@ -1,235 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//   Ben Motmans <ben.motmans@gmail.com>
-//   Nicholas Terry <nick.i.terry@gmail.com>
-//
-// Copyright (C) 2006 Alan McGovern
-// Copyright (C) 2007 Ben Motmans
-// Copyright (C) 2014 Nicholas Terry
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Net;
-using Mono.Nat.Pmp;
-using System.Net.NetworkInformation;
-using System.Net.Sockets;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using System.Linq;
-
-namespace Mono.Nat
-{
-    internal class PmpSearcher : ISearcher, IDisposable
-    {
-        private ILogger _logger;
-
-        private int timeout = 250;
-        private DateTime nextSearch;
-        public event EventHandler<DeviceEventArgs> DeviceFound;
-
-        public PmpSearcher(ILogger logger)
-        {
-            _logger = logger;
-
-            CreateSocketsAndAddGateways();
-        }
-
-        public void Dispose()
-        {
-            var list = sockets.ToList();
-            sockets.Clear();
-
-            foreach (var s in list)
-            {
-                using (s)
-                {
-                    s.Close();
-                }
-            }
-        }
-
-        private List<UdpClient> sockets;
-        private Dictionary<UdpClient, List<IPEndPoint>> gatewayLists;
-
-        private void CreateSocketsAndAddGateways()
-        {
-            sockets = new List<UdpClient>();
-            gatewayLists = new Dictionary<UdpClient, List<IPEndPoint>>();
-
-            try
-            {
-                foreach (var n in NetworkInterface.GetAllNetworkInterfaces())
-                {
-                    if (n.OperationalStatus != OperationalStatus.Up && n.OperationalStatus != OperationalStatus.Unknown)
-                        continue;
-                    IPInterfaceProperties properties = n.GetIPProperties();
-                    var gatewayList = new List<IPEndPoint>();
-
-                    foreach (GatewayIPAddressInformation gateway in properties.GatewayAddresses)
-                    {
-                        if (gateway.Address.AddressFamily == AddressFamily.InterNetwork)
-                        {
-                            gatewayList.Add(new IPEndPoint(gateway.Address, PmpConstants.ServerPort));
-                        }
-                    }
-                    if (gatewayList.Count == 0)
-                    {
-                        /* Mono on OSX doesn't give any gateway addresses, so check DNS entries */
-                        foreach (var gw2 in properties.DnsAddresses)
-                        {
-                            if (gw2.AddressFamily == AddressFamily.InterNetwork)
-                            {
-                                gatewayList.Add(new IPEndPoint(gw2, PmpConstants.ServerPort));
-                            }
-                        }
-                        foreach (UnicastIPAddressInformation unicast in properties.UnicastAddresses)
-                        {
-                            if (/*unicast.DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred
-                                && unicast.AddressPreferredLifetime != UInt32.MaxValue
-                                && */unicast.Address.AddressFamily == AddressFamily.InterNetwork)
-                            {
-                                var bytes = unicast.Address.GetAddressBytes();
-                                bytes[3] = 1;
-                                gatewayList.Add(new IPEndPoint(new IPAddress(bytes), PmpConstants.ServerPort));
-                            }
-                        }
-                    }
-
-                    if (gatewayList.Count > 0)
-                    {
-                        foreach (var address in properties.UnicastAddresses)
-                        {
-                            if (address.Address.AddressFamily == AddressFamily.InterNetwork)
-                            {
-                                UdpClient client;
-
-                                try
-                                {
-                                    client = new UdpClient(new IPEndPoint(address.Address, 0));
-                                }
-                                catch (SocketException)
-                                {
-                                    continue; // Move on to the next address.
-                                }
-
-                                gatewayLists.Add(client, gatewayList);
-                                sockets.Add(client);
-                            }
-                        }
-                    }
-                }
-            }
-            catch (Exception)
-            {
-                // NAT-PMP does not use multicast, so there isn't really a good fallback.
-            }
-        }
-
-        public async void Search()
-        {
-            foreach (UdpClient s in sockets)
-            {
-                try
-                {
-                    await Search(s).ConfigureAwait(false);
-                }
-                catch
-                {
-                    // Ignore any search errors
-                }
-            }
-        }
-
-        async Task Search(UdpClient client)
-        {
-            // Sort out the time for the next search first. The spec says the
-            // timeout should double after each attempt. Once it reaches 64 seconds
-            // (and that attempt fails), assume no devices available
-            nextSearch = DateTime.Now.AddMilliseconds(timeout);
-            timeout *= 2;
-
-            // We've tried 9 times as per spec, try searching again in 5 minutes
-            if (timeout == 128 * 1000)
-            {
-                timeout = 250;
-                nextSearch = DateTime.Now.AddMinutes(10);
-                return;
-            }
-
-            // The nat-pmp search message. Must be sent to GatewayIP:53531
-            byte[] buffer = new byte[] { PmpConstants.Version, PmpConstants.OperationCode };
-            foreach (IPEndPoint gatewayEndpoint in gatewayLists[client])
-            {
-                await client.SendAsync(buffer, buffer.Length, gatewayEndpoint).ConfigureAwait(false);
-            }
-        }
-
-        bool IsSearchAddress(IPAddress address)
-        {
-            foreach (var gatewayList in gatewayLists.Values)
-                foreach (var gatewayEndpoint in gatewayList)
-                    if (gatewayEndpoint.Address.Equals(address))
-                        return true;
-            return false;
-        }
-
-        public void Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint)
-        {
-            if (!IsSearchAddress(endpoint.Address))
-                return;
-            if (response.Length != 12)
-                return;
-            if (response[0] != PmpConstants.Version)
-                return;
-            if (response[1] != PmpConstants.ServerNoop)
-                return;
-            int errorcode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(response, 2));
-            if (errorcode != 0)
-                _logger.LogDebug("Non zero error: {0}", errorcode);
-
-            var publicIp = new IPAddress(new byte[] { response[8], response[9], response[10], response[11] });
-            nextSearch = DateTime.Now.AddMinutes(5);
-            timeout = 250;
-
-            OnDeviceFound(new DeviceEventArgs(new PmpNatDevice(endpoint.Address, publicIp, _logger)));
-        }
-
-        public DateTime NextSearch
-        {
-            get { return nextSearch; }
-        }
-        private void OnDeviceFound(DeviceEventArgs args)
-        {
-            if (DeviceFound != null)
-                DeviceFound(this, args);
-        }
-
-        public NatProtocol Protocol
-        {
-            get { return NatProtocol.Pmp; }
-        }
-    }
-}

+ 0 - 21
Mono.Nat/Properties/AssemblyInfo.cs

@@ -1,21 +0,0 @@
-using System.Reflection;
-using System.Resources;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Mono.Nat")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Jellyfin Project")]
-[assembly: AssemblyProduct("Jellyfin Server")]
-[assembly: AssemblyCopyright("Copyright ©  2006 Alan McGovern. Copyright ©  2007 Ben Motmans. Code releases under the MIT license. Copyright ©  2019 Jellyfin Contributors. Code released under the GNU General Public License")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-[assembly: NeutralResourcesLanguage("en")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components.  If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]

+ 0 - 64
Mono.Nat/Upnp/Messages/GetServicesMessage.cs

@@ -1,64 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//
-// Copyright (C) 2006 Alan McGovern
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Net;
-using MediaBrowser.Common.Net;
-
-namespace Mono.Nat.Upnp
-{
-    internal class GetServicesMessage : MessageBase
-    {
-        private string _servicesDescriptionUrl;
-        private EndPoint _hostAddress;
-
-        public GetServicesMessage(string description, EndPoint hostAddress)
-            : base(null)
-        {
-            if (string.IsNullOrEmpty(description))
-            {
-                throw new ArgumentException("Description is null/empty", nameof(description));
-            }
-
-            this._servicesDescriptionUrl = description;
-            this._hostAddress = hostAddress ?? throw new ArgumentNullException(nameof(hostAddress));
-        }
-
-        public override string Method => "GET";
-
-        public override HttpRequestOptions Encode()
-        {
-            var req = new HttpRequestOptions()
-            {
-                Url = $"http://{this._hostAddress}{this._servicesDescriptionUrl}"
-            };
-
-            req.RequestHeaders.Add("ACCEPT-LANGUAGE", "en");
-
-            return req;
-        }
-    }
-}

+ 0 - 75
Mono.Nat/Upnp/Messages/Requests/CreatePortMappingMessage.cs

@@ -1,75 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//
-// Copyright (C) 2006 Alan McGovern
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System.Net;
-using System.IO;
-using System.Globalization;
-using System.Text;
-using System.Xml;
-using MediaBrowser.Common.Net;
-
-namespace Mono.Nat.Upnp
-{
-    internal class CreatePortMappingMessage : MessageBase
-    {
-        #region Private Fields
-
-        private IPAddress localIpAddress;
-        private Mapping mapping;
-
-        #endregion
-
-
-        #region Constructors
-        public CreatePortMappingMessage(Mapping mapping, IPAddress localIpAddress, UpnpNatDevice device)
-            : base(device)
-        {
-            this.mapping = mapping;
-            this.localIpAddress = localIpAddress;
-        }
-        #endregion
-
-        public override HttpRequestOptions Encode()
-        {
-            var culture = CultureInfo.InvariantCulture;
-
-            var builder = new StringBuilder(256);
-            XmlWriter writer = CreateWriter(builder);
-
-            WriteFullElement(writer, "NewRemoteHost", string.Empty);
-            WriteFullElement(writer, "NewExternalPort", this.mapping.PublicPort.ToString(culture));
-            WriteFullElement(writer, "NewProtocol", this.mapping.Protocol == Protocol.Tcp ? "TCP" : "UDP");
-            WriteFullElement(writer, "NewInternalPort", this.mapping.PrivatePort.ToString(culture));
-            WriteFullElement(writer, "NewInternalClient", this.localIpAddress.ToString());
-            WriteFullElement(writer, "NewEnabled", "1");
-            WriteFullElement(writer, "NewPortMappingDescription", string.IsNullOrEmpty(mapping.Description) ? "Mono.Nat" : mapping.Description);
-            WriteFullElement(writer, "NewLeaseDuration", mapping.Lifetime.ToString());
-
-            writer.Flush();
-            return CreateRequest("AddPortMapping", builder.ToString());
-        }
-    }
-}

+ 0 - 84
Mono.Nat/Upnp/Messages/UpnpMessage.cs

@@ -1,84 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//
-// Copyright (C) 2006 Alan McGovern
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System.Xml;
-using System.Text;
-using MediaBrowser.Common.Net;
-
-namespace Mono.Nat.Upnp
-{
-    internal abstract class MessageBase
-    {
-        protected UpnpNatDevice device;
-
-        protected MessageBase(UpnpNatDevice device)
-        {
-            this.device = device;
-        }
-
-        protected HttpRequestOptions CreateRequest(string upnpMethod, string methodParameters)
-        {
-            var req = new HttpRequestOptions()
-            {
-                Url = $"http://{this.device.HostEndPoint}{this.device.ControlUrl}",
-                EnableKeepAlive = false,
-                RequestContentType = "text/xml",
-                RequestContent = "<s:Envelope "
-                + "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
-                + "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
-                + "<s:Body>"
-                + "<u:" + upnpMethod + " "
-                + "xmlns:u=\"" + device.ServiceType + "\">"
-                + methodParameters
-                + "</u:" + upnpMethod + ">"
-                + "</s:Body>"
-                + "</s:Envelope>\r\n\r\n"
-            };
-
-            req.RequestHeaders.Add("SOAPACTION", "\"" + device.ServiceType + "#" + upnpMethod + "\"");
-
-            return req;
-        }
-
-        public abstract HttpRequestOptions Encode();
-
-        public virtual string Method => "POST";
-
-        protected void WriteFullElement(XmlWriter writer, string element, string value)
-        {
-            writer.WriteStartElement(element);
-            writer.WriteString(value);
-            writer.WriteEndElement();
-        }
-
-        protected XmlWriter CreateWriter(StringBuilder sb)
-        {
-            var settings = new XmlWriterSettings();
-            settings.ConformanceLevel = ConformanceLevel.Fragment;
-            return XmlWriter.Create(sb, settings);
-        }
-    }
-}

+ 0 - 111
Mono.Nat/Upnp/Searchers/UpnpSearcher.cs

@@ -1,111 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//   Ben Motmans <ben.motmans@gmail.com>
-//   Nicholas Terry <nick.i.terry@gmail.com>
-//
-// Copyright (C) 2006 Alan McGovern
-// Copyright (C) 2007 Ben Motmans
-// Copyright (C) 2014 Nicholas Terry
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Net;
-using Mono.Nat.Upnp;
-using System.Diagnostics;
-using System.Net.Sockets;
-using System.Net.NetworkInformation;
-using MediaBrowser.Common.Net;
-using Microsoft.Extensions.Logging;
-using MediaBrowser.Model.Dlna;
-using System.Threading.Tasks;
-
-namespace Mono.Nat
-{
-    internal class UpnpSearcher : ISearcher
-    {
-        public event EventHandler<DeviceEventArgs> DeviceFound;
-
-        private readonly ILogger _logger;
-        private readonly IHttpClient _httpClient;
-
-        public UpnpSearcher(ILogger logger, IHttpClient httpClient)
-        {
-            _logger = logger;
-            _httpClient = httpClient;
-        }
-
-        public void Search()
-        {
-        }
-
-        public async Task Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint)
-        {
-            if (localAddress == null)
-            {
-                throw new ArgumentNullException(nameof(localAddress));
-            }
-
-            try
-            {
-                /* For UPnP Port Mapping we need ot find either WANPPPConnection or WANIPConnection.
-                 * Any other device type is no good to us for this purpose. See the IGP overview paper
-                 * page 5 for an overview of device types and their hierarchy.
-                 * http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf */
-
-                /* TODO: Currently we are assuming version 1 of the protocol. We should figure out which
-                 * version it is and apply the correct URN. */
-
-                /* Some routers don't correctly implement the version ID on the URN, so we only search for the type
-                 * prefix. */
-
-                // We have an internet gateway device now
-                var d = new UpnpNatDevice(localAddress, deviceInfo, endpoint, string.Empty, _logger, _httpClient);
-
-                await d.GetServicesList().ConfigureAwait(false);
-
-                OnDeviceFound(new DeviceEventArgs(d));
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error decoding device response");
-            }
-        }
-
-        public void Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint)
-        {
-        }
-
-        private void OnDeviceFound(DeviceEventArgs args)
-        {
-            if (DeviceFound != null)
-                DeviceFound(this, args);
-        }
-
-        public NatProtocol Protocol
-        {
-            get { return NatProtocol.Upnp; }
-        }
-    }
-}

+ 0 - 267
Mono.Nat/Upnp/UpnpNatDevice.cs

@@ -1,267 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//   Ben Motmans <ben.motmans@gmail.com>
-//
-// Copyright (C) 2006 Alan McGovern
-// Copyright (C) 2007 Ben Motmans
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Net;
-using System.Xml;
-using System.Text;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using Microsoft.Extensions.Logging;
-using MediaBrowser.Model.Dlna;
-
-namespace Mono.Nat.Upnp
-{
-    public sealed class UpnpNatDevice : AbstractNatDevice, IEquatable<UpnpNatDevice>
-    {
-        private EndPoint hostEndPoint;
-        private IPAddress localAddress;
-        private string serviceDescriptionUrl;
-        private string controlUrl;
-        private string serviceType;
-        private readonly ILogger _logger;
-        private readonly IHttpClient _httpClient;
-
-        public override IPAddress LocalAddress
-        {
-            get { return localAddress; }
-        }
-
-        internal UpnpNatDevice(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint hostEndPoint, string serviceType, ILogger logger, IHttpClient httpClient)
-        {
-            if (localAddress == null)
-            {
-                throw new ArgumentNullException(nameof(localAddress));
-            }
-
-            this.LastSeen = DateTime.Now;
-            this.localAddress = localAddress;
-
-            // Split the string at the "location" section so i can extract the ipaddress and service description url
-            string locationDetails = deviceInfo.Location.ToString();
-            this.serviceType = serviceType;
-            _logger = logger;
-            _httpClient = httpClient;
-
-            // Make sure we have no excess whitespace
-            locationDetails = locationDetails.Trim();
-
-            // FIXME: Is this reliable enough. What if we get a hostname as opposed to a proper http address
-            // Are we going to get addresses with the "http://" attached?
-            if (locationDetails.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
-            {
-                _logger.LogDebug("Found device at: {0}", locationDetails);
-                // This bit strings out the "http://" from the string
-                locationDetails = locationDetails.Substring(7);
-
-                this.hostEndPoint = hostEndPoint;
-
-                // The service description URL is the remainder of the "locationDetails" string. The bit that was originally after the ip
-                // and port information
-                this.serviceDescriptionUrl = locationDetails.Substring(locationDetails.IndexOf('/'));
-            }
-            else
-            {
-                _logger.LogDebug("Couldn't decode address. Please send following string to the developer: ");
-            }
-        }
-
-        public async Task GetServicesList()
-        {
-            // Create a HTTPWebRequest to download the list of services the device offers
-            var message = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint);
-
-            using (var response = await _httpClient.SendAsync(message.Encode(), message.Method).ConfigureAwait(false))
-            {
-                OnServicesReceived(response);
-            }
-        }
-
-        private void OnServicesReceived(HttpResponseInfo response)
-        {
-            int abortCount = 0;
-            int bytesRead = 0;
-            byte[] buffer = new byte[10240];
-            var servicesXml = new StringBuilder();
-            var xmldoc = new XmlDocument();
-
-            using (var s = response.Content)
-            {
-                if (response.StatusCode != HttpStatusCode.OK)
-                {
-                    _logger.LogDebug("{0}: Couldn't get services list: {1}", HostEndPoint, response.StatusCode);
-                    return; // FIXME: This the best thing to do??
-                }
-
-                while (true)
-                {
-                    bytesRead = s.Read(buffer, 0, buffer.Length);
-                    servicesXml.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
-                    try
-                    {
-                        xmldoc.LoadXml(servicesXml.ToString());
-                        break;
-                    }
-                    catch (XmlException)
-                    {
-                        // If we can't receive the entire XML within 500ms, then drop the connection
-                        // Unfortunately not all routers supply a valid ContentLength (mine doesn't)
-                        // so this hack is needed to keep testing our recieved data until it gets successfully
-                        // parsed by the xmldoc. Without this, the code will never pick up my router.
-                        if (abortCount++ > 50)
-                        {
-                            return;
-                        }
-                        _logger.LogDebug("{0}: Couldn't parse services list", HostEndPoint);
-                        System.Threading.Thread.Sleep(10);
-                    }
-                }
-
-                var ns = new XmlNamespaceManager(xmldoc.NameTable);
-                ns.AddNamespace("ns", "urn:schemas-upnp-org:device-1-0");
-                XmlNodeList nodes = xmldoc.SelectNodes("//*/ns:serviceList", ns);
-
-                foreach (XmlNode node in nodes)
-                {
-                    //Go through each service there
-                    foreach (XmlNode service in node.ChildNodes)
-                    {
-                        //If the service is a WANIPConnection, then we have what we want
-                        string type = service["serviceType"].InnerText;
-                        _logger.LogDebug("{0}: Found service: {1}", HostEndPoint, type);
-
-                        // TODO: Add support for version 2 of UPnP.
-                        if (string.Equals(type, "urn:schemas-upnp-org:service:WANPPPConnection:1", StringComparison.OrdinalIgnoreCase) ||
-                            string.Equals(type, "urn:schemas-upnp-org:service:WANIPConnection:1", StringComparison.OrdinalIgnoreCase))
-                        {
-                            this.controlUrl = service["controlURL"].InnerText;
-                            _logger.LogDebug("{0}: Found upnp service at: {1}", HostEndPoint, controlUrl);
-
-                            Uri u;
-                            if (Uri.TryCreate(controlUrl, UriKind.RelativeOrAbsolute, out u))
-                            {
-                                if (u.IsAbsoluteUri)
-                                {
-                                    var old = hostEndPoint;
-                                    IPAddress parsedHostIpAddress;
-                                    if (IPAddress.TryParse(u.Host, out parsedHostIpAddress))
-                                    {
-                                        this.hostEndPoint = new IPEndPoint(parsedHostIpAddress, u.Port);
-                                        //_logger.LogDebug("{0}: Absolute URI detected. Host address is now: {1}", old, HostEndPoint);
-                                        this.controlUrl = controlUrl.Substring(u.GetLeftPart(UriPartial.Authority).Length);
-                                        //_logger.LogDebug("{0}: New control url: {1}", HostEndPoint, controlUrl);
-                                    }
-                                }
-                            }
-                            else
-                            {
-                                _logger.LogDebug("{0}: Assuming control Uri is relative: {1}", HostEndPoint, controlUrl);
-                            }
-                            return;
-                        }
-                    }
-                }
-
-                //If we get here, it means that we didn't get WANIPConnection service, which means no uPnP forwarding
-                //So we don't invoke the callback, so this device is never added to our lists
-            }
-        }
-
-        /// <summary>
-        /// The EndPoint that the device is at
-        /// </summary>
-        internal EndPoint HostEndPoint
-        {
-            get { return this.hostEndPoint; }
-        }
-
-        /// <summary>
-        /// The relative url of the xml file that describes the list of services is at
-        /// </summary>
-        internal string ServiceDescriptionUrl
-        {
-            get { return this.serviceDescriptionUrl; }
-        }
-
-        /// <summary>
-        /// The relative url that we can use to control the port forwarding
-        /// </summary>
-        internal string ControlUrl
-        {
-            get { return this.controlUrl; }
-        }
-
-        /// <summary>
-        /// The service type we're using on the device
-        /// </summary>
-        public string ServiceType
-        {
-            get { return serviceType; }
-        }
-
-        public override async Task CreatePortMap(Mapping mapping)
-        {
-            var message = new CreatePortMappingMessage(mapping, localAddress, this);
-            using (await _httpClient.SendAsync(message.Encode(), message.Method).ConfigureAwait(false))
-            {
-
-            }
-        }
-
-        public override bool Equals(object obj)
-        {
-            var device = obj as UpnpNatDevice;
-            return (device == null) ? false : this.Equals((device));
-        }
-
-
-        public bool Equals(UpnpNatDevice other)
-        {
-            return (other == null) ? false : (this.hostEndPoint.Equals(other.hostEndPoint)
-                //&& this.controlUrl == other.controlUrl
-                && this.serviceDescriptionUrl == other.serviceDescriptionUrl);
-        }
-
-        public override int GetHashCode()
-        {
-            return (this.hostEndPoint.GetHashCode() ^ this.controlUrl.GetHashCode() ^ this.serviceDescriptionUrl.GetHashCode());
-        }
-
-        /// <summary>
-        /// Overridden.
-        /// </summary>
-        /// <returns></returns>
-        public override string ToString()
-        {
-            //GetExternalIP is blocking and can throw exceptions, can't use it here.
-            return String.Format(
-                "UpnpNatDevice - EndPoint: {0}, External IP: {1}, Control Url: {2}, Service Description Url: {3}, Service Type: {4}, Last Seen: {5}",
-                this.hostEndPoint, "Manually Check" /*this.GetExternalIP()*/, this.controlUrl, this.serviceDescriptionUrl, this.serviceType, this.LastSeen);
-        }
-    }
-}