diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index b10103a..3d779af 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -18,9 +18,9 @@ jobs: Workspace: ${{ github.workspace }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 8.0.x - name: Restore dependencies @@ -28,7 +28,7 @@ jobs: - name: Build run: dotnet build src/Perpetuum.ServerService2/Perpetuum.ServerService2.csproj --no-restore --configuration Release --verbosity quiet -p:Platform=x64 - name: Upload a Build Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: Perpetuum-Server-v2-${{ github.sha }} path: ${{ env.Workspace }}/bin/x64/Release/net8.0 diff --git a/src/Open.Nat/Discovery/ISearcher.cs b/src/Open.Nat/Discovery/ISearcher.cs deleted file mode 100644 index a8f9ca5..0000000 --- a/src/Open.Nat/Discovery/ISearcher.cs +++ /dev/null @@ -1,37 +0,0 @@ -// -// Authors: -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2014 Lucas Ontivero -// -// 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; - -namespace Open.Nat.Discovery -{ - internal interface ISearcher - { - void Search(CancellationToken cancellationToken); - IEnumerable Receive(); - NatDevice AnalyseReceivedResponse(IPAddress localAddress, byte[] response, IPEndPoint endpoint); - } -} \ No newline at end of file diff --git a/src/Open.Nat/Discovery/Searcher.cs b/src/Open.Nat/Discovery/Searcher.cs deleted file mode 100644 index ecc351d..0000000 --- a/src/Open.Nat/Discovery/Searcher.cs +++ /dev/null @@ -1,136 +0,0 @@ -// -// Authors: -// Ben Motmans -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2007 Ben Motmans -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.EventArgs; -using Open.Nat.Utils; -using System.Net; -using System.Net.Sockets; - -namespace Open.Nat.Discovery -{ - internal abstract class Searcher - { - private readonly List _devices = []; - protected List? UdpClients; - public EventHandler? DeviceFound; - internal DateTime NextSearch = DateTime.UtcNow; - -#if NET35 - public Task> Search(CancellationToken cancelationToken) - { - return Task.Factory.StartNew(_ => - { - NatDiscoverer.TraceSource.LogInfo("Searching for: {0}", GetType().Name); - while (!cancelationToken.IsCancellationRequested) - { - Discover(cancelationToken); - Receive(cancelationToken); - } - CloseUdpClients(); - }, cancelationToken) - .ContinueWith>((Task task) => _devices); - } -#else - public async Task> Search(CancellationToken cancelationToken) - { - await Task.Factory.StartNew(_ => - { - NatDiscoverer.TraceSource.LogInfo("Searching for: {0}", GetType().Name); - while (!cancelationToken.IsCancellationRequested) - { - Discover(cancelationToken); - Receive(cancelationToken); - } - CloseUdpClients(); - }, null, cancelationToken); - return _devices; - } -#endif - - private void Discover(CancellationToken cancelationToken) - { - if (DateTime.UtcNow < NextSearch) - { - return; - } - - foreach (UdpClient socket in UdpClients) - { - try - { - Discover(socket, cancelationToken); - } - catch (Exception e) - { - NatDiscoverer.TraceSource.LogError("Error searching {0} - Details:", GetType().Name); - NatDiscoverer.TraceSource.LogError(e.ToString()); - } - } - } - - private void Receive(CancellationToken cancelationToken) - { - foreach (UdpClient? client in UdpClients.Where(x => x.Available > 0)) - { - if (cancelationToken.IsCancellationRequested) - { - return; - } - - IPAddress localHost = ((IPEndPoint)client.Client.LocalEndPoint).Address; - IPEndPoint receivedFrom = new(IPAddress.None, 0); - byte[] buffer = client.Receive(ref receivedFrom); - NatDevice device = AnalyseReceivedResponse(localHost, buffer, receivedFrom); - - if (device != null) - { - RaiseDeviceFound(device); - } - } - } - - - protected abstract void Discover(UdpClient client, CancellationToken cancelationToken); - - public abstract NatDevice AnalyseReceivedResponse(IPAddress localAddress, byte[] response, IPEndPoint endpoint); - - public void CloseUdpClients() - { - foreach (UdpClient udpClient in UdpClients) - { - udpClient.Close(); - } - } - - private void RaiseDeviceFound(NatDevice device) - { - _devices.Add(device); - DeviceFound?.Invoke(this, new DeviceEventArgs(device)); - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Enums/ProtocolType.cs b/src/Open.Nat/Enums/ProtocolType.cs deleted file mode 100644 index eb5ba28..0000000 --- a/src/Open.Nat/Enums/ProtocolType.cs +++ /dev/null @@ -1,43 +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. -// - -namespace Open.Nat.Enums -{ - /// - /// Protocol to allow/disallow - /// - public enum Protocol - { - /// - /// Transport Control Protocol - /// - Tcp, - /// - /// Datagram Protocol - /// - Udp - } -} \ No newline at end of file diff --git a/src/Open.Nat/EventArgs/DeviceEventArgs.cs b/src/Open.Nat/EventArgs/DeviceEventArgs.cs deleted file mode 100644 index 5f6d439..0000000 --- a/src/Open.Nat/EventArgs/DeviceEventArgs.cs +++ /dev/null @@ -1,40 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2014 Lucas Ontivero -// -// 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. -// - -namespace Open.Nat.EventArgs -{ - internal class DeviceEventArgs : System.EventArgs - { - public DeviceEventArgs(NatDevice device) - { - Device = device; - } - - public NatDevice Device { get; private set; } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Exceptions/MappingException.cs b/src/Open.Nat/Exceptions/MappingException.cs deleted file mode 100644 index c117484..0000000 --- a/src/Open.Nat/Exceptions/MappingException.cs +++ /dev/null @@ -1,94 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2014 Lucas Ontivero -// -// 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.Runtime.Serialization; -using System.Security.Permissions; - -namespace Open.Nat.Exceptions -{ - /// - /// - /// - [Serializable] - public class MappingException : Exception - { - /// - /// - /// - public int ErrorCode { get; private set; } - - /// - /// - /// - public string ErrorText { get; private set; } - - #region Constructors - - internal MappingException() - { - } - - internal MappingException(string message) - : base(message) - { - } - - internal MappingException(int errorCode, string errorText) - : base(string.Format("Error {0}: {1}", errorCode, errorText)) - { - ErrorCode = errorCode; - ErrorText = errorText; - } - - internal MappingException(string message, Exception innerException) - : base(message, innerException) - { - } - - protected MappingException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - - #endregion - - [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] - [Obsolete] - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new ArgumentNullException("info"); - } - - ErrorCode = info.GetInt32("errorCode"); - ErrorText = info.GetString("errorText"); - base.GetObjectData(info, context); - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Exceptions/NatDeviceNotFoundException.cs b/src/Open.Nat/Exceptions/NatDeviceNotFoundException.cs deleted file mode 100644 index b76fc8c..0000000 --- a/src/Open.Nat/Exceptions/NatDeviceNotFoundException.cs +++ /dev/null @@ -1,68 +0,0 @@ -// -// Authors: -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2014 Lucas Ontivero -// -// 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.Runtime.Serialization; - -namespace Open.Nat.Exceptions -{ - /// - /// - /// - [Serializable] - public class NatDeviceNotFoundException : Exception - { - /// - /// - /// - public NatDeviceNotFoundException() - { - } - - /// - /// - /// - /// - public NatDeviceNotFoundException(string message) - : base(message) - { - } - - /// - /// - /// - /// - /// - public NatDeviceNotFoundException(string message, Exception innerException) - : base(message, innerException) - { - } - - protected NatDeviceNotFoundException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Finalizer.cs b/src/Open.Nat/Finalizer.cs deleted file mode 100644 index b58d474..0000000 --- a/src/Open.Nat/Finalizer.cs +++ /dev/null @@ -1,39 +0,0 @@ -// -// Authors: -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Utils; - -namespace Open.Nat -{ - internal sealed class Finalizer - { - ~Finalizer() - { - NatDiscoverer.TraceSource.LogInfo("Closing ports opened in this session"); - NatDiscoverer.RenewTimer.Dispose(); - NatDiscoverer.ReleaseSessionMappings(); - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Mapping.cs b/src/Open.Nat/Mapping.cs deleted file mode 100644 index d4b7444..0000000 --- a/src/Open.Nat/Mapping.cs +++ /dev/null @@ -1,282 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Ben Motmans -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2007 Ben Motmans -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Enums; -using Open.Nat.Utils; -using System.Net; - -namespace Open.Nat -{ - internal enum MappingLifetime - { - Permanent, - Session, - Manual, - ForcedSession - } - - /// - /// Represents a port forwarding entry in the NAT translation table. - /// - public class Mapping - { - private DateTime _expiration; - private int _lifetime; - internal MappingLifetime LifetimeType { get; set; } - - - /// - /// Gets the mapping's description. It is the value stored in the NewPortMappingDescription parameter. - /// The NewPortMappingDescription parameter is a human readable string that describes the connection. - /// It is used in sorme web interfaces of routers so the user can see which program is using what port. - /// - public string Description { get; internal set; } - /// - /// Gets the private ip. - /// - public IPAddress PrivateIP { get; internal set; } - /// - /// Gets the protocol. - /// - public Protocol Protocol { get; internal set; } - /// - /// The PrivatePort parameter specifies the port on a client machine to which all traffic - /// coming in on PublicPort for the protocol specified by - /// Protocol should be forwarded to. - /// - /// Protocol enum - public int PrivatePort { get; internal set; } - /// - /// Gets the public ip. - /// - public IPAddress PublicIP { get; internal set; } - /// - /// Gets the external (visible) port number. - /// It is the value stored in the NewExternalPort parameter . - /// The NewExternalPort parameter is used to specify the TCP or UDP port on the WAN side of the router which should be forwarded. - /// - public int PublicPort { get; internal set; } - /// - /// Gets the lifetime in seconds. The Lifetime parameter tells the router how long the portmapping should be active. - /// Since most programs don't know this in advance, it is often set to 0, which means 'unlimited' or 'permanent'. - /// - /// - /// All portmappings are release automatically as part of the shutdown process when NatUtility.ReleaseOnShutdown is true. - /// Permanent portmappings will not be released if the process ends anormally. - /// Since most programs don't know the lifetime in advance, Open.NAT renew all the portmappings (except the permanents) before they expires. So, developers have to close explicitly those portmappings - /// they don't want to remain open for the session. - /// - public int Lifetime - { - get => _lifetime; - internal set - { - switch (value) - { - case int.MaxValue: - LifetimeType = MappingLifetime.Session; - _lifetime = 10 * 60; // ten minutes - _expiration = DateTime.UtcNow.AddSeconds(_lifetime); ; - break; - case 0: - LifetimeType = MappingLifetime.Permanent; - _lifetime = 0; - _expiration = DateTime.UtcNow; - break; - default: - LifetimeType = MappingLifetime.Manual; - _lifetime = value; - _expiration = DateTime.UtcNow.AddSeconds(_lifetime); - break; - } - } - } - - /// - /// Gets the expiration. The property value is calculated using Lifetime property. - /// - public DateTime Expiration - { - get => _expiration; - internal set - { - _expiration = value; - _lifetime = (int)(_expiration - DateTime.UtcNow).TotalSeconds; - } - } - - internal Mapping(Protocol protocol, IPAddress privateIP, int privatePort, int publicPort) - : this(protocol, privateIP, privatePort, publicPort, 0, "Open.Nat") - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The protocol. - /// The private ip. - /// The private port. - /// The public port. - /// The lifetime in seconds. - /// The description. - public Mapping(Protocol protocol, IPAddress privateIP, int privatePort, int publicPort, int lifetime, string description) - { - Guard.IsInRange(privatePort, 0, ushort.MaxValue, "privatePort"); - Guard.IsInRange(publicPort, 0, ushort.MaxValue, "publicPort"); - Guard.IsInRange(lifetime, 0, int.MaxValue, "lifetime"); - Guard.IsTrue(protocol is Protocol.Tcp or Protocol.Udp, "protocol"); - Guard.IsNotNull(privateIP, "privateIP"); - - Protocol = protocol; - PrivateIP = privateIP; - PrivatePort = privatePort; - PublicIP = IPAddress.None; - PublicPort = publicPort; - Lifetime = lifetime; - Description = description; - } - - /// - /// Initializes a new instance of the class. - /// - /// The protocol. - /// The private port. - /// The public port. - /// - /// This constructor initializes a Permanent mapping. The description by deafult is "Open.NAT" - /// - public Mapping(Protocol protocol, int privatePort, int publicPort) - : this(protocol, IPAddress.None, privatePort, publicPort, 0, "Open.NAT") - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The protocol. - /// The private port. - /// The public port. - /// The description. - /// - /// This constructor initializes a Permanent mapping. - /// - public Mapping(Protocol protocol, int privatePort, int publicPort, string description) - : this(protocol, IPAddress.None, privatePort, publicPort, 0, description) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The protocol. - /// The private port. - /// The public port. - /// The lifetime in seconds. - /// The description. - public Mapping(Protocol protocol, int privatePort, int publicPort, int lifetime, string description) - : this(protocol, IPAddress.None, privatePort, publicPort, lifetime, description) - { - } - - internal Mapping(Mapping mapping) - { - PrivateIP = mapping.PrivateIP; - PrivatePort = mapping.PrivatePort; - Protocol = mapping.Protocol; - PublicIP = mapping.PublicIP; - PublicPort = mapping.PublicPort; - LifetimeType = mapping.LifetimeType; - Description = mapping.Description; - _lifetime = mapping._lifetime; - _expiration = mapping._expiration; - } - - /// - /// Determines whether this instance is expired. - /// - /// - /// Permanent mappings never expires. - /// - public bool IsExpired() - { - return LifetimeType != MappingLifetime.Permanent - && LifetimeType != MappingLifetime.ForcedSession - && Expiration < DateTime.UtcNow; - } - - internal bool ShoundRenew() - { - return LifetimeType == MappingLifetime.Session && IsExpired(); - } - - public override bool Equals(object obj) - { - if (obj is null) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - Mapping? m = obj as Mapping; - return m is not null && PublicPort == m.PublicPort && PrivatePort == m.PrivatePort; - } - - public override int GetHashCode() - { - unchecked - { - int hashCode = PublicPort; - hashCode = (hashCode * 397) ^ (PrivateIP != null ? PrivateIP.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ PrivatePort; - return hashCode; - } - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return string.Format("{0} {1} --> {2}:{3} ({4})", - Protocol == Protocol.Tcp ? "Tcp" : "Udp", - PublicPort, - PrivateIP, - PrivatePort, - Description); - } - } -} diff --git a/src/Open.Nat/NatDevice.cs b/src/Open.Nat/NatDevice.cs deleted file mode 100644 index cf4a740..0000000 --- a/src/Open.Nat/NatDevice.cs +++ /dev/null @@ -1,233 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Ben Motmans -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2007 Ben Motmans -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Enums; -using Open.Nat.Exceptions; -using Open.Nat.Utils; -using System.Net; - -namespace Open.Nat -{ - /// - /// Represents a NAT device and provides access to the operation set that allows - /// open (forward) ports, close ports and get the externa (visible) IP address. - /// - public abstract class NatDevice - { - /// - /// A local endpoint of NAT device. - /// - public abstract IPEndPoint HostEndPoint { get; } - - /// - /// A local IP address of client. - /// - public abstract IPAddress LocalAddress { get; } - - private readonly HashSet _openedMapping = []; - protected DateTime LastSeen { get; private set; } - - internal void Touch() - { - LastSeen = DateTime.Now; - } - - /// - /// Creates the port map asynchronous. - /// - /// The Mapping entry. - /// - /// device.CreatePortMapAsync(new Mapping(Protocol.Tcp, 1700, 1600)); - /// - /// MappingException - public abstract Task CreatePortMapAsync(Mapping mapping); - - /// - /// Deletes a mapped port asynchronous. - /// - /// The Mapping entry. - /// - /// device.DeletePortMapAsync(new Mapping(Protocol.Tcp, 1700, 1600)); - /// - /// MappingException-class - public abstract Task DeletePortMapAsync(Mapping mapping); - - /// - /// Gets all mappings asynchronous. - /// - /// - /// The list of all forwarded ports - /// - /// - /// var mappings = await device.GetAllMappingsAsync(); - /// foreach(var mapping in mappings) - /// { - /// Console.WriteLine(mapping) - /// } - /// - /// MappingException - public abstract Task> GetAllMappingsAsync(); - - /// - /// Gets the external (visible) IP address asynchronous. This is the NAT device IP address - /// - /// - /// The public IP addrees - /// - /// - /// Console.WriteLine("My public IP is: {0}", await device.GetExternalIPAsync()); - /// - /// MappingException - public abstract Task GetExternalIPAsync(); - - /// - /// Gets the specified mapping asynchronous. - /// - /// The protocol. - /// The port. - /// - /// The matching mapping - /// - public abstract Task GetSpecificMappingAsync(Protocol protocol, int port); - - protected void RegisterMapping(Mapping mapping) - { - _openedMapping.Remove(mapping); - _openedMapping.Add(mapping); - } - - protected void UnregisterMapping(Mapping mapping) - { - _openedMapping.RemoveWhere(x => x.Equals(mapping)); - } - - - internal void ReleaseMapping(IEnumerable mappings) - { - Mapping[] maparr = mappings.ToArray(); - int mapCount = maparr.Length; - NatDiscoverer.TraceSource.LogInfo("{0} ports to close", mapCount); - for (int i = 0; i < mapCount; i++) - { - Mapping mapping = _openedMapping.ElementAt(i); - - try - { - DeletePortMapAsync(mapping); - NatDiscoverer.TraceSource.LogInfo(mapping + " port successfully closed"); - } - catch (Exception) - { - NatDiscoverer.TraceSource.LogError(mapping + " port couldn't be close"); - } - } - } - - internal void ReleaseAll() - { - ReleaseMapping(_openedMapping); - } - - internal void ReleaseSessionMappings() - { - IEnumerable mappings = from m in _openedMapping - where m.LifetimeType == MappingLifetime.Session - select m; - - ReleaseMapping(mappings); - } - -#if NET35 - internal Task RenewMappings() - { - Task task = null; - var mappings = _openedMapping.Where(x => x.ShoundRenew()); - foreach (var mapping in mappings.ToArray()) - { - var m = mapping; - task = task == null ? RenewMapping(m) : task.ContinueWith(t => RenewMapping(m)).Unwrap(); - } - - return task; - } -#else - internal async Task RenewMappings() - { - IEnumerable mappings = _openedMapping.Where(x => x.ShoundRenew()); - foreach (Mapping? mapping in mappings.ToArray()) - { - Mapping m = mapping; - await RenewMapping(m); - } - } -#endif - -#if NET35 - private Task RenewMapping(Mapping mapping) - { - var renewMapping = new Mapping(mapping); - renewMapping.Expiration = DateTime.UtcNow.AddSeconds(mapping.Lifetime); - - NatDiscoverer.TraceSource.LogInfo("Renewing mapping {0}", renewMapping); - return CreatePortMapAsync(renewMapping) - .ContinueWith(task => - { - if (task.IsFaulted) - { - NatDiscoverer.TraceSource.LogWarn("Renew {0} failed", mapping); - } - else - { - NatDiscoverer.TraceSource.LogInfo("Next renew scheduled at: {0}", - renewMapping.Expiration.ToLocalTime().TimeOfDay); - } - }); - } -#else - private async Task RenewMapping(Mapping mapping) - { - Mapping renewMapping = new(mapping); - try - { - renewMapping.Expiration = DateTime.UtcNow.AddSeconds(mapping.Lifetime); - - NatDiscoverer.TraceSource.LogInfo("Renewing mapping {0}", renewMapping); - await CreatePortMapAsync(renewMapping); - NatDiscoverer.TraceSource.LogInfo("Next renew scheduled at: {0}", - renewMapping.Expiration.ToLocalTime().TimeOfDay); - } - catch (Exception) - { - NatDiscoverer.TraceSource.LogWarn("Renew {0} failed", mapping); - } - } -#endif - } -} \ No newline at end of file diff --git a/src/Open.Nat/NatDiscoverer.cs b/src/Open.Nat/NatDiscoverer.cs deleted file mode 100644 index 989bad8..0000000 --- a/src/Open.Nat/NatDiscoverer.cs +++ /dev/null @@ -1,261 +0,0 @@ -using Open.Nat.Exceptions; -using Open.Nat.Pmp; -using Open.Nat.Upnp; -using Open.Nat.Utils; -using System.Diagnostics; - -namespace Open.Nat -{ - /// - /// - /// - public class NatDiscoverer - { - /// - /// The TraceSource instance - /// used for debugging and Troubleshooting - /// - /// - /// NatUtility.TraceSource.Switch.Level = SourceLevels.Verbose; - /// NatUtility.TraceSource.Listeners.Add(new ConsoleListener()); - /// - /// - /// At least one trace listener has to be added to the Listeners collection if a trace is required; if no listener is added - /// there will no be tracing to analyse. - /// - /// - /// Open.NAT only supports SourceLevels.Verbose, SourceLevels.Error, SourceLevels.Warning and SourceLevels.Information. - /// - public static readonly TraceSource TraceSource = new("Open.NAT"); - - private static readonly Dictionary Devices = []; - - // Finalizer is never used however its destructor, that releases the open ports, is invoked by the - // process as part of the shuting down step. So, don't remove it! - private static readonly Finalizer Finalizer = new(); - internal static readonly Timer RenewTimer = new(RenewMappings, null, 5000, 2000); - - /// - /// Discovers and returns an UPnp or Pmp NAT device; otherwise a NatDeviceNotFoundException - /// exception is thrown after 3 seconds. - /// - /// A NAT device - /// when no NAT found before 3 seconds. -#if NET35 - public Task DiscoverDeviceAsync() - { - var cts = new CancellationTokenSource(); - cts.CancelAfter(3 * 1000); - return DiscoverDeviceAsync(PortMapper.Pmp | PortMapper.Upnp, cts); - } -#else - public async Task DiscoverDeviceAsync() - { - CancellationTokenSource cts = new(3 * 1000); - return await DiscoverDeviceAsync(PortMapper.Pmp | PortMapper.Upnp, cts); - } -#endif - - /// - /// Discovers and returns a NAT device for the specified type; otherwise a NatDeviceNotFoundException - /// exception is thrown when it is cancelled. - /// - /// - /// It allows to specify the NAT type to discover as well as the cancellation token in order. - /// - /// Port mapper protocol; Upnp, Pmp or both - /// Cancellation token source for cancelling the discovery process - /// A NAT device - /// when no NAT found before cancellation -#if NET35 - public Task DiscoverDeviceAsync(PortMapper portMapper, CancellationTokenSource cancellationTokenSource) - { - Guard.IsTrue(portMapper.HasFlag(PortMapper.Upnp) || portMapper.HasFlag(PortMapper.Pmp), "portMapper"); - Guard.IsNotNull(cancellationTokenSource, "cancellationTokenSource"); - - return DiscoverAsync(portMapper, true, cancellationTokenSource) - .ContinueWith(task => - { - var devices = task.Result; - var device = devices.FirstOrDefault(); - if (device == null) - { - TraceSource.LogInfo("Device not found. Common reasons:"); - TraceSource.LogInfo("\t* No device is present or,"); - TraceSource.LogInfo("\t* Upnp is disabled in the router or"); - TraceSource.LogInfo("\t* Antivirus software is filtering SSDP (discovery protocol)."); - throw new NatDeviceNotFoundException(); - } - return device; - }); - } -#else - public async Task DiscoverDeviceAsync(PortMapper portMapper, CancellationTokenSource cancellationTokenSource) - { - Guard.IsTrue(portMapper.HasFlag(PortMapper.Upnp) || portMapper.HasFlag(PortMapper.Pmp), "portMapper"); - Guard.IsNotNull(cancellationTokenSource, "cancellationTokenSource"); - - IEnumerable devices = await DiscoverAsync(portMapper, true, cancellationTokenSource); - NatDevice? device = devices.FirstOrDefault(); - if (device == null) - { - TraceSource.LogInfo("Device not found. Common reasons:"); - TraceSource.LogInfo("\t* No device is present or,"); - TraceSource.LogInfo("\t* Upnp is disabled in the router or"); - TraceSource.LogInfo("\t* Antivirus software is filtering SSDP (discovery protocol)."); - throw new NatDeviceNotFoundException(); - } - return device; - } -#endif - - /// - /// Discovers and returns all NAT devices for the specified type. If no NAT device is found it returns an empty enumerable - /// - /// Port mapper protocol; Upnp, Pmp or both - /// Cancellation token source for cancelling the discovery process - /// All found NAT devices -#if NET35 - public Task> DiscoverDevicesAsync(PortMapper portMapper, CancellationTokenSource cancellationTokenSource) - { - Guard.IsTrue(portMapper.HasFlag(PortMapper.Upnp) || portMapper.HasFlag(PortMapper.Pmp), "portMapper"); - Guard.IsNotNull(cancellationTokenSource, "cancellationTokenSource"); - - return DiscoverAsync(portMapper, false, cancellationTokenSource) - .ContinueWith(t => (IEnumerable)t.Result.ToArray()); - } -#else - public async Task> DiscoverDevicesAsync(PortMapper portMapper, CancellationTokenSource cancellationTokenSource) - { - Guard.IsTrue(portMapper.HasFlag(PortMapper.Upnp) || portMapper.HasFlag(PortMapper.Pmp), "portMapper"); - Guard.IsNotNull(cancellationTokenSource, "cancellationTokenSource"); - - IEnumerable devices = await DiscoverAsync(portMapper, false, cancellationTokenSource); - return devices.ToArray(); - } -#endif - -#if NET35 - private Task> DiscoverAsync(PortMapper portMapper, bool onlyOne, CancellationTokenSource cts) - { - TraceSource.LogInfo("Start Discovery"); - var searcherTasks = new List>>(); - if (portMapper.HasFlag(PortMapper.Upnp)) - { - var upnpSearcher = new UpnpSearcher(new IPAddressesProvider()); - upnpSearcher.DeviceFound += (sender, args) => { if (onlyOne) cts.Cancel(); }; - searcherTasks.Add(upnpSearcher.Search(cts.Token)); - } - if (portMapper.HasFlag(PortMapper.Pmp)) - { - var pmpSearcher = new PmpSearcher(new IPAddressesProvider()); - pmpSearcher.DeviceFound += (sender, args) => { if (onlyOne) cts.Cancel(); }; - searcherTasks.Add(pmpSearcher.Search(cts.Token)); - } - - return TaskExtension.WhenAll(searcherTasks.ToArray()) - .ContinueWith(t => - { - TraceSource.LogInfo("Stop Discovery"); - - var devices = searcherTasks.SelectMany(x => x.Result); - foreach (var device in devices) - { - var key = device.ToString(); - NatDevice nat; - if (Devices.TryGetValue(key, out nat)) - { - nat.Touch(); - } - else - { - Devices.Add(key, device); - } - } - return devices; - }); - } -#else - private async Task> DiscoverAsync(PortMapper portMapper, bool onlyOne, CancellationTokenSource cts) - { - TraceSource.LogInfo("Start Discovery"); - List>> searcherTasks = []; - if (portMapper.HasFlag(PortMapper.Upnp)) - { - UpnpSearcher upnpSearcher = new(new IPAddressesProvider()); - upnpSearcher.DeviceFound += (sender, args) => { if (onlyOne) { cts.Cancel(); } }; - searcherTasks.Add(upnpSearcher.Search(cts.Token)); - } - if (portMapper.HasFlag(PortMapper.Pmp)) - { - PmpSearcher pmpSearcher = new(new IPAddressesProvider()); - pmpSearcher.DeviceFound += (sender, args) => { if (onlyOne) { cts.Cancel(); } }; - searcherTasks.Add(pmpSearcher.Search(cts.Token)); - } - - await Task.WhenAll(searcherTasks); - TraceSource.LogInfo("Stop Discovery"); - - IEnumerable devices = searcherTasks.SelectMany(x => x.Result); - foreach (NatDevice? device in devices) - { - string? key = device.ToString(); - if (Devices.TryGetValue(key, out NatDevice nat)) - { - nat.Touch(); - } - else - { - Devices.Add(key, device); - } - } - return devices; - } -#endif - - /// - /// Release all ports opened by Open.NAT. - /// - /// - /// If ReleaseOnShutdown value is true, it release all the mappings created through the library. - /// - public static void ReleaseAll() - { - foreach (NatDevice device in Devices.Values) - { - device.ReleaseAll(); - } - } - - internal static void ReleaseSessionMappings() - { - foreach (NatDevice device in Devices.Values) - { - device.ReleaseSessionMappings(); - } - } - - private static void RenewMappings(object state) - { -#if NET35 - Task.Factory.StartNew(()=> - { - Task task = null; - foreach (var device in Devices.Values) - { - var d = device; - task = (task == null) ? device.RenewMappings() : task.ContinueWith(t => d.RenewMappings()); - } - }); -#else - Task.Factory.StartNew(async () => - { - foreach (NatDevice device in Devices.Values) - { - await device.RenewMappings(); - } - }); -#endif - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Open.Nat.csproj b/src/Open.Nat/Open.Nat.csproj deleted file mode 100644 index 2420faa..0000000 --- a/src/Open.Nat/Open.Nat.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - net8.0 - enable - enable - x64 - - - diff --git a/src/Open.Nat/Pmp/PmpConstants.cs b/src/Open.Nat/Pmp/PmpConstants.cs deleted file mode 100644 index de0fda6..0000000 --- a/src/Open.Nat/Pmp/PmpConstants.cs +++ /dev/null @@ -1,63 +0,0 @@ -// -// Authors: -// Ben Motmans -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2007 Ben Motmans -// Copyright (C) 2014 Lucas Ontivero -// -// 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. -// - -namespace Open.Nat.Pmp -{ - internal static class PmpConstants - { - public const byte Version = 0; - - public const byte OperationExternalAddressRequest = 0; - public const byte OperationCodeUdp = 1; - public const byte OperationCodeTcp = 2; - public const byte ServerNoop = 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; // Success - public const short ResultCodeUnsupportedVersion = 1; // Unsupported Version - - public const short ResultCodeNotAuthorized = 2; - // Not Authorized/Refused (e.g. box supports mapping, but user has turned feature off) - - public const short ResultCodeNetworkFailure = 3; - // Network Failure (e.g. NAT box itself has not obtained a DHCP lease) - - public const short ResultCodeOutOfResources = 4; - // Out of resources (NAT box cannot create any more mappings at this time) - - public const short ResultCodeUnsupportedOperationCode = 5; // Unsupported opcode - } -} \ No newline at end of file diff --git a/src/Open.Nat/Pmp/PmpNatDevice.cs b/src/Open.Nat/Pmp/PmpNatDevice.cs deleted file mode 100644 index 0332060..0000000 --- a/src/Open.Nat/Pmp/PmpNatDevice.cs +++ /dev/null @@ -1,286 +0,0 @@ -// -// Authors: -// Ben Motmans -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2007 Ben Motmans -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Enums; -using Open.Nat.Exceptions; -using Open.Nat.Utils; -using System.Net; -using System.Net.Sockets; - -namespace Open.Nat.Pmp -{ - internal sealed class PmpNatDevice : NatDevice - { - public override IPEndPoint HostEndPoint => _hostEndPoint; - - public override IPAddress LocalAddress => _localAddress; - - private readonly IPEndPoint _hostEndPoint; - private readonly IPAddress _localAddress; - private readonly IPAddress _publicAddress; - - internal PmpNatDevice(IPAddress hostEndPointAddress, IPAddress localAddress, IPAddress publicAddress) - { - _hostEndPoint = new IPEndPoint(hostEndPointAddress, PmpConstants.ServerPort); - _localAddress = localAddress; - _publicAddress = publicAddress; - } - -#if NET35 - public override Task CreatePortMapAsync(Mapping mapping) - { - return InternalCreatePortMapAsync(mapping, true) - .TimeoutAfter(TimeSpan.FromSeconds(4)) - .ContinueWith(t => RegisterMapping(mapping)); - } -#else - public override async Task CreatePortMapAsync(Mapping mapping) - { - await InternalCreatePortMapAsync(mapping, true) - .TimeoutAfter(TimeSpan.FromSeconds(4)); - RegisterMapping(mapping); - } -#endif - -#if NET35 - public override Task DeletePortMapAsync(Mapping mapping) - { - return InternalCreatePortMapAsync(mapping, false) - .TimeoutAfter(TimeSpan.FromSeconds(4)) - .ContinueWith(t => UnregisterMapping(mapping)); - } -#else - public override async Task DeletePortMapAsync(Mapping mapping) - { - await InternalCreatePortMapAsync(mapping, false) - .TimeoutAfter(TimeSpan.FromSeconds(4)); - UnregisterMapping(mapping); - } -#endif - - public override Task> GetAllMappingsAsync() - { - throw new NotSupportedException(); - } - - public override Task GetExternalIPAsync() - { -#if NET35 - return Task.Factory.StartNew(() => _publicAddress) -#else - return Task.Run(() => _publicAddress) -#endif - .TimeoutAfter(TimeSpan.FromSeconds(4)); - } - - public override Task GetSpecificMappingAsync(Protocol protocol, int port) - { - throw new NotSupportedException("NAT-PMP does not specify a way to get a specific port map"); - } - -#if NET35 - private Task InternalCreatePortMapAsync(Mapping mapping, bool create) - { - var package = new List(); - - 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))); - - byte[] buffer = package.ToArray(); - int attempt = 0; - int delay = PmpConstants.RetryDelay; - - var udpClient = new UdpClient(); - CreatePortMapListen(udpClient, mapping); - - Task task = Task.Factory.FromAsync( - udpClient.BeginSend, udpClient.EndSend, - buffer, buffer.Length, - HostEndPoint, - null); - - while (attempt < PmpConstants.RetryAttempts - 1) - { - task = task.ContinueWith(t => - { - if (t.IsFaulted) - { - string type = create ? "create" : "delete"; - string message = String.Format("Failed to {0} portmap (protocol={1}, private port={2})", - type, - mapping.Protocol, - mapping.PrivatePort); - NatDiscoverer.TraceSource.LogError(message); - throw new MappingException(message, t.Exception); - } - - return Task.Factory.FromAsync( - udpClient.BeginSend, udpClient.EndSend, - buffer, buffer.Length, - HostEndPoint, - null); - }).Unwrap(); - - attempt++; - delay *= 2; - Thread.Sleep(delay); - } - - return task.ContinueWith(t => - { - udpClient.Close(); - return mapping; - }); - } -#else - private async Task InternalCreatePortMapAsync(Mapping mapping, bool create) - { - List package = new() - { - PmpConstants.Version, - mapping.Protocol == Protocol.Tcp ? PmpConstants.OperationCodeTcp : PmpConstants.OperationCodeUdp, - 0, //reserved - 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 UdpClient udpClient = new(); - CreatePortMapListen(udpClient, mapping); - - while (attempt < PmpConstants.RetryAttempts) - { - await - udpClient.SendAsync(buffer, buffer.Length, HostEndPoint); - - attempt++; - delay *= 2; - Thread.Sleep(delay); - } - } - catch (Exception e) - { - string type = create ? "create" : "delete"; - string message = string.Format("Failed to {0} portmap (protocol={1}, private port={2})", - type, - mapping.Protocol, - mapping.PrivatePort); - NatDiscoverer.TraceSource.LogError(message); - MappingException? pmpException = e as MappingException; - throw new MappingException(message, pmpException); - } - - return mapping; - } -#endif - - private void CreatePortMapListen(UdpClient udpClient, Mapping mapping) - { - IPEndPoint endPoint = HostEndPoint; - - while (true) - { - byte[] data = udpClient.Receive(ref endPoint); - - if (data.Length < 16) - { - continue; - } - - if (data[0] != PmpConstants.Version) - { - continue; - } - - byte opCode = (byte)(data[1] & 127); - - Protocol 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)); - - uint lifetime = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 12)); - - if (privatePort < 0 || publicPort < 0 || resultCode != PmpConstants.ResultCodeSuccess) - { - string[] 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" - }; - throw new MappingException(resultCode, errors[resultCode]); - } - - 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; - } - } - - - public override string ToString() - { - return string.Format("Local Address: {0}\nPublic IP: {1}\nLast Seen: {2}", - HostEndPoint.Address, _publicAddress, LastSeen); - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Pmp/PmpSearcher.cs b/src/Open.Nat/Pmp/PmpSearcher.cs deleted file mode 100644 index 582de2a..0000000 --- a/src/Open.Nat/Pmp/PmpSearcher.cs +++ /dev/null @@ -1,153 +0,0 @@ -// -// Authors: -// Ben Motmans -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2007 Ben Motmans -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Discovery; -using Open.Nat.Utils; -using System.Net; -using System.Net.Sockets; - -namespace Open.Nat.Pmp -{ - internal class PmpSearcher : Searcher - { - private readonly IIPAddressesProvider _ipprovider; - private Dictionary> _gatewayLists; - private int _timeout; - - internal PmpSearcher(IIPAddressesProvider ipprovider) - { - _ipprovider = ipprovider; - _timeout = 250; - CreateSocketsAndAddGateways(); - } - - private void CreateSocketsAndAddGateways() - { - UdpClients = []; - _gatewayLists = []; - - try - { - List gatewayList = _ipprovider.GatewayAddresses() - .Select(ip => new IPEndPoint(ip, PmpConstants.ServerPort)) - .ToList(); - - if (!gatewayList.Any()) - { - gatewayList.AddRange( - _ipprovider.DnsAddresses() - .Select(ip => new IPEndPoint(ip, PmpConstants.ServerPort))); - } - - if (!gatewayList.Any()) - { - return; - } - - foreach (IPAddress address in _ipprovider.UnicastAddresses()) - { - UdpClient client; - - try - { - client = new UdpClient(new IPEndPoint(address, 0)); - } - catch (SocketException) - { - continue; // Move on to the next address. - } - - _gatewayLists.Add(client, gatewayList); - UdpClients.Add(client); - } - } - catch (Exception e) - { - NatDiscoverer.TraceSource.LogError("There was a problem finding gateways: " + e); - // NAT-PMP does not use multicast, so there isn't really a good fallback. - } - } - - protected override void Discover(UdpClient client, CancellationToken cancelationToken) - { - // 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.UtcNow.AddMilliseconds(_timeout); - _timeout *= 2; - - if (_timeout >= 3000) - { - _timeout = 250; - NextSearch = DateTime.UtcNow.AddSeconds(10); - return; - } - - // The nat-pmp search message. Must be sent to GatewayIP:53531 - byte[] buffer = new[] { PmpConstants.Version, PmpConstants.OperationExternalAddressRequest }; - foreach (IPEndPoint gatewayEndpoint in _gatewayLists[client]) - { - if (cancelationToken.IsCancellationRequested) - { - return; - } - - client.Send(buffer, buffer.Length, gatewayEndpoint); - } - } - - private bool IsSearchAddress(IPAddress address) - { - return _gatewayLists.Values.SelectMany(x => x) - .Any(x => x.Address.Equals(address)); - } - - public override NatDevice? AnalyseReceivedResponse(IPAddress localAddress, byte[] response, IPEndPoint endpoint) - { - if (!IsSearchAddress(endpoint.Address) - || response.Length != 12 - || response[0] != PmpConstants.Version - || response[1] != PmpConstants.ServerNoop) - { - return null; - } - - int errorcode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(response, 2)); - if (errorcode != 0) - { - NatDiscoverer.TraceSource.LogError("Non zero error: {0}", errorcode); - } - - IPAddress publicIp = new(new[] { response[8], response[9], response[10], response[11] }); - //NextSearch = DateTime.Now.AddMinutes(5); - - _timeout = 250; - return new PmpNatDevice(localAddress, endpoint.Address, publicIp); - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/PortMapper.cs b/src/Open.Nat/PortMapper.cs deleted file mode 100644 index 2f9b691..0000000 --- a/src/Open.Nat/PortMapper.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Open.Nat -{ - /// - /// Protocol that should be used for searching a NAT device. - /// - [Flags] - public enum PortMapper - { - /// - /// Use only Port Mapping Protocol - /// - Pmp = 1, - - /// - /// Use only Universal Plug and Play - /// - Upnp = 2 - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/DiscoveryResponseMessage.cs b/src/Open.Nat/Upnp/DiscoveryResponseMessage.cs deleted file mode 100644 index f00bb9d..0000000 --- a/src/Open.Nat/Upnp/DiscoveryResponseMessage.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// Authors: -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2014 Lucas Ontivero -// -// 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. -// -namespace Open.Nat.Upnp -{ - internal class DiscoveryResponseMessage - { - private readonly IDictionary _headers; - - public DiscoveryResponseMessage(string message) - { - string[] lines = message.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); - var headers = from h in lines.Skip(1) - let c = h.Split(':') - let key = c[0] - let value = c.Length > 1 - ? string.Join(":", c.Skip(1).ToArray()) - : string.Empty - select new { Key = key, Value = value.Trim() }; - _headers = headers.ToDictionary(x => x.Key.ToUpperInvariant(), x => x.Value); - } - - public string this[string key] => _headers[key.ToUpperInvariant()]; - } -} diff --git a/src/Open.Nat/Upnp/Messages/DiscoverDeviceMessage.cs b/src/Open.Nat/Upnp/Messages/DiscoverDeviceMessage.cs deleted file mode 100644 index 57825a1..0000000 --- a/src/Open.Nat/Upnp/Messages/DiscoverDeviceMessage.cs +++ /dev/null @@ -1,55 +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.Globalization; -using System.Net; -using System.Net.Sockets; - -namespace Open.Nat.Upnp.Messages -{ - internal static class DiscoverDeviceMessage - { - /// - /// The message sent to discover all uPnP devices on the network - /// - /// - public static string Encode(string serviceType, IPAddress address) - { - string fmtAddress = string.Format( - address.AddressFamily == AddressFamily.InterNetwork ? "{0}" : "[{0}]", - address); - - string s = "M-SEARCH * HTTP/1.1\r\n" - + "HOST: " + fmtAddress + ":1900\r\n" - + "MAN: \"ssdp:discover\"\r\n" - + "MX: 3\r\n" - + "ST: urn:schemas-upnp-org:service:{0}\r\n\r\n"; - //+ "ST:upnp:rootdevice\r\n\r\n"; - - return string.Format(CultureInfo.InvariantCulture, s, serviceType); - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/Messages/Requests/CreatePortMappingRequestMessage.cs b/src/Open.Nat/Upnp/Messages/Requests/CreatePortMappingRequestMessage.cs deleted file mode 100644 index 1c0d51b..0000000 --- a/src/Open.Nat/Upnp/Messages/Requests/CreatePortMappingRequestMessage.cs +++ /dev/null @@ -1,62 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Enums; -using System.Net; - -namespace Open.Nat.Upnp.Messages.Requests -{ - internal class CreatePortMappingRequestMessage : RequestMessageBase - { - private readonly Mapping _mapping; - - public CreatePortMappingRequestMessage(Mapping mapping) - { - _mapping = mapping; - } - - public override IDictionary ToXml() - { - string remoteHost = _mapping.PublicIP.Equals(IPAddress.None) - ? string.Empty - : _mapping.PublicIP.ToString(); - - return new Dictionary - { - {"NewRemoteHost", remoteHost}, - {"NewExternalPort", _mapping.PublicPort}, - {"NewProtocol", _mapping.Protocol == Protocol.Tcp ? "TCP" : "UDP"}, - {"NewInternalPort", _mapping.PrivatePort}, - {"NewInternalClient", _mapping.PrivateIP}, - {"NewEnabled", 1}, - {"NewPortMappingDescription", _mapping.Description}, - {"NewLeaseDuration", _mapping.Lifetime} - }; - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/Messages/Requests/DeletePortMappingRequestMessage.cs b/src/Open.Nat/Upnp/Messages/Requests/DeletePortMappingRequestMessage.cs deleted file mode 100644 index 46cd3e7..0000000 --- a/src/Open.Nat/Upnp/Messages/Requests/DeletePortMappingRequestMessage.cs +++ /dev/null @@ -1,52 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Enums; - -namespace Open.Nat.Upnp.Messages.Requests -{ - internal class DeletePortMappingRequestMessage : RequestMessageBase - { - private readonly Mapping _mapping; - - public DeletePortMappingRequestMessage(Mapping mapping) - { - _mapping = mapping; - } - - public override IDictionary ToXml() - { - return new Dictionary - { - {"NewRemoteHost", string.Empty}, - {"NewExternalPort", _mapping.PublicPort}, - {"NewProtocol", _mapping.Protocol == Protocol.Tcp ? "TCP" : "UDP"} - }; - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/Messages/Requests/GetExternalIPAddressRequestMessage.cs b/src/Open.Nat/Upnp/Messages/Requests/GetExternalIPAddressRequestMessage.cs deleted file mode 100644 index f12d35e..0000000 --- a/src/Open.Nat/Upnp/Messages/Requests/GetExternalIPAddressRequestMessage.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2014 Lucas Ontivero -// -// 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. -// - -namespace Open.Nat.Upnp.Messages.Requests -{ - internal class GetExternalIPAddressRequestMessage : RequestMessageBase - { - public override IDictionary ToXml() - { - return new Dictionary(); - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/Messages/Requests/GetGenericPortMappingEntry.cs b/src/Open.Nat/Upnp/Messages/Requests/GetGenericPortMappingEntry.cs deleted file mode 100644 index 67f84b5..0000000 --- a/src/Open.Nat/Upnp/Messages/Requests/GetGenericPortMappingEntry.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2014 Lucas Ontivero -// -// 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. -// - -namespace Open.Nat.Upnp.Messages.Requests -{ - internal class GetGenericPortMappingEntry : RequestMessageBase - { - private readonly int _index; - - public GetGenericPortMappingEntry(int index) - { - _index = index; - } - - public override IDictionary ToXml() - { - return new Dictionary - { - {"NewPortMappingIndex", _index} - }; - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/Messages/Requests/GetSpecificPortMappingEntryRequestMessage.cs b/src/Open.Nat/Upnp/Messages/Requests/GetSpecificPortMappingEntryRequestMessage.cs deleted file mode 100644 index aacb173..0000000 --- a/src/Open.Nat/Upnp/Messages/Requests/GetSpecificPortMappingEntryRequestMessage.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Enums; - -namespace Open.Nat.Upnp.Messages.Requests -{ - internal class GetSpecificPortMappingEntryRequestMessage : RequestMessageBase - { - private readonly int _externalPort; - private readonly Protocol _protocol; - - public GetSpecificPortMappingEntryRequestMessage(Protocol protocol, int externalPort) - { - _protocol = protocol; - _externalPort = externalPort; - } - - public override IDictionary ToXml() - { - return new Dictionary - { - {"NewRemoteHost", string.Empty}, - {"NewExternalPort", _externalPort}, - {"NewProtocol", _protocol == Protocol.Tcp ? "TCP" : "UDP"} - }; - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/Messages/Responses/GetExternalIPAddressResponseMessage.cs b/src/Open.Nat/Upnp/Messages/Responses/GetExternalIPAddressResponseMessage.cs deleted file mode 100644 index 8b7fda3..0000000 --- a/src/Open.Nat/Upnp/Messages/Responses/GetExternalIPAddressResponseMessage.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Utils; -using System.Net; -using System.Xml; - -namespace Open.Nat.Upnp.Messages.Responses -{ - internal class GetExternalIPAddressResponseMessage : ResponseMessageBase - { - public GetExternalIPAddressResponseMessage(XmlDocument response, string serviceType) - : base(response, serviceType, "GetExternalIPAddressResponseMessage") - { - string ip = GetNode().GetXmlElementText("NewExternalIPAddress"); - - if (IPAddress.TryParse(ip, out IPAddress ipAddr)) - { - ExternalIPAddress = ipAddr; - } - } - - public IPAddress ExternalIPAddress { get; private set; } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/Messages/Responses/GetPortMappingEntryResponseMessage.cs b/src/Open.Nat/Upnp/Messages/Responses/GetPortMappingEntryResponseMessage.cs deleted file mode 100644 index 1fc4cdf..0000000 --- a/src/Open.Nat/Upnp/Messages/Responses/GetPortMappingEntryResponseMessage.cs +++ /dev/null @@ -1,66 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Enums; -using Open.Nat.Utils; -using System.Xml; - -namespace Open.Nat.Upnp.Messages.Responses -{ - internal class GetPortMappingEntryResponseMessage : ResponseMessageBase - { - internal GetPortMappingEntryResponseMessage(XmlDocument response, string serviceType, bool genericMapping) - : base(response, serviceType, genericMapping ? "GetGenericPortMappingEntryResponseMessage" : "GetSpecificPortMappingEntryResponseMessage") - { - XmlNode data = GetNode(); - - RemoteHost = genericMapping ? data.GetXmlElementText("NewRemoteHost") : string.Empty; - ExternalPort = genericMapping ? Convert.ToInt32(data.GetXmlElementText("NewExternalPort")) : ushort.MaxValue; - Protocol = genericMapping - ? data.GetXmlElementText("NewProtocol").Equals("TCP", StringComparison.InvariantCultureIgnoreCase) - ? Protocol.Tcp - : Protocol.Udp - : Protocol.Udp; - - InternalPort = Convert.ToInt32(data.GetXmlElementText("NewInternalPort")); - InternalClient = data.GetXmlElementText("NewInternalClient"); - Enabled = data.GetXmlElementText("NewEnabled") == "1"; - PortMappingDescription = data.GetXmlElementText("NewPortMappingDescription"); - LeaseDuration = Convert.ToInt32(data.GetXmlElementText("NewLeaseDuration")); - } - - public string RemoteHost { get; private set; } - public int ExternalPort { get; private set; } - public Protocol Protocol { get; private set; } - public int InternalPort { get; private set; } - public string InternalClient { get; private set; } - public bool Enabled { get; private set; } - public string PortMappingDescription { get; private set; } - public int LeaseDuration { get; private set; } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/RequestMessageBase.cs b/src/Open.Nat/Upnp/RequestMessageBase.cs deleted file mode 100644 index f32f7a5..0000000 --- a/src/Open.Nat/Upnp/RequestMessageBase.cs +++ /dev/null @@ -1,35 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2014 Lucas Ontivero -// -// 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. -// - -namespace Open.Nat.Upnp -{ - internal abstract class RequestMessageBase - { - public abstract IDictionary ToXml(); - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/ResponseMessageBase.cs b/src/Open.Nat/Upnp/ResponseMessageBase.cs deleted file mode 100644 index bb2ade2..0000000 --- a/src/Open.Nat/Upnp/ResponseMessageBase.cs +++ /dev/null @@ -1,55 +0,0 @@ -// -// Authors: -// Lucas Ontivero lucas.ontivero@gmail.com -// -// Copyright (C) 2014 Lucas Ontivero -// -// 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; - -namespace Open.Nat.Upnp -{ - internal abstract class ResponseMessageBase - { - private readonly XmlDocument _document; - protected string ServiceType; - private readonly string _typeName; - - protected ResponseMessageBase(XmlDocument response, string serviceType, string typeName) - { - _document = response; - ServiceType = serviceType; - _typeName = typeName; - } - - protected XmlNode GetNode() - { - XmlNamespaceManager nsm = new(_document.NameTable); - nsm.AddNamespace("responseNs", ServiceType); - - string typeName = _typeName; - string messageName = typeName[..^"Message".Length]; - XmlNode node = _document.SelectSingleNode("//responseNs:" + messageName, nsm); - return node == null ? throw new InvalidOperationException("The response is invalid: " + messageName) : node; - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/SoapClient.cs b/src/Open.Nat/Upnp/SoapClient.cs deleted file mode 100644 index 0fc9cc3..0000000 --- a/src/Open.Nat/Upnp/SoapClient.cs +++ /dev/null @@ -1,251 +0,0 @@ -// -// Authors: -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Exceptions; -using Open.Nat.Utils; -using System.Diagnostics; -using System.Globalization; -using System.Net; -using System.Text; -using System.Xml; - -namespace Open.Nat.Upnp -{ - internal class SoapClient - { - private readonly string _serviceType; - private readonly Uri _url; - - public SoapClient(Uri url, string serviceType) - { - _url = url; - _serviceType = serviceType; - } - -#if NET35 - public Task InvokeAsync(string operationName, IDictionary args) - { - NatDiscoverer.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "SOAPACTION: **{0}** url:{1}", operationName, - _url); - byte[] messageBody = BuildMessageBody(operationName, args); - HttpWebRequest request = BuildHttpWebRequest(operationName, messageBody); - - Task responseTask; - if (messageBody.Length > 0) - { - Stream requestStream = null; - responseTask = Task.Factory.FromAsync(request.BeginGetRequestStream, request.EndGetRequestStream, null) - .ContinueWith(requestSteamTask => - { - requestStream = requestSteamTask.Result; - return Task.Factory.FromAsync(requestStream.BeginWrite, - requestStream.EndWrite, messageBody, 0, messageBody.Length, null); - }) - .Unwrap() - .ContinueWith(streamWriteTask => - { - requestStream.Close(); - return GetWebResponse(request); - }) - .Unwrap(); - } - else - { - responseTask = GetWebResponse(request); - } - - return responseTask.ContinueWith(task => - { - using (WebResponse response = task.Result) - { - var stream = response.GetResponseStream(); - var contentLength = response.ContentLength; - - var reader = new StreamReader(stream, Encoding.UTF8); - - var responseBody = contentLength != -1 - ? reader.ReadAsMany((int)contentLength) - : reader.ReadToEnd(); - - var responseXml = GetXmlDocument(responseBody.Trim()); - - response.Close(); - return responseXml; - } - }); - } -#else - public async Task InvokeAsync(string operationName, IDictionary args) - { - NatDiscoverer.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "SOAPACTION: **{0}** url:{1}", operationName, - _url); - byte[] messageBody = BuildMessageBody(operationName, args); - HttpWebRequest request = BuildHttpWebRequest(operationName, messageBody); - - if (messageBody.Length > 0) - { - using Stream stream = await request.GetRequestStreamAsync(); - await stream.WriteAsync(messageBody, 0, messageBody.Length); - } - - using (WebResponse response = await GetWebResponse(request)) - { - Stream stream = response.GetResponseStream(); - long contentLength = response.ContentLength; - - StreamReader reader = new(stream, Encoding.UTF8); - - string responseBody = contentLength != -1 - ? reader.ReadAsMany((int)contentLength) - : reader.ReadToEnd(); - - XmlDocument responseXml = GetXmlDocument(responseBody); - - response.Close(); - return responseXml; - } - } -#endif - -#if NET35 - private static Task GetWebResponse(WebRequest request) - { - return Task.Factory - .FromAsync(request.BeginGetResponse, request.EndGetResponse, null) - .ContinueWith(task => - { - WebResponse response; - if (!task.IsFaulted) - { - response = task.Result; - } - else - { - WebException ex = task.Exception.InnerException as WebException; - if (ex == null) - { - throw task.Exception; - } - - NatDiscoverer.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "WebException status: {0}", ex.Status); - - // Even if the request "failed" we need to continue reading the response from the router - response = ex.Response as HttpWebResponse; - - if (response == null) - { - throw task.Exception; - } - } - - return response; - }); - } -#else - private static async Task GetWebResponse(WebRequest request) - { - WebResponse? response; - try - { - response = await request.GetResponseAsync(); - } - catch (WebException ex) - { - NatDiscoverer.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "WebException status: {0}", ex.Status); - - // Even if the request "failed" we need to continue reading the response from the router - response = ex.Response as HttpWebResponse; - - if (response == null) - { - throw; - } - } - return response; - } -#endif - - private HttpWebRequest BuildHttpWebRequest(string operationName, byte[] messageBody) - { -#if NET35 - var request = (HttpWebRequest)WebRequest.Create(_url); -#else - HttpWebRequest request = WebRequest.CreateHttp(_url); -#endif - request.KeepAlive = false; - request.Method = "POST"; - request.ContentType = "text/xml; charset=\"utf-8\""; - request.Headers.Add("SOAPACTION", "\"" + _serviceType + "#" + operationName + "\""); - request.ContentLength = messageBody.Length; - return request; - } - - private byte[] BuildMessageBody(string operationName, IEnumerable> args) - { - StringBuilder sb = new(); - sb.AppendLine(""); - sb.AppendLine(" "); - sb.AppendLine(" "); - foreach (KeyValuePair a in args) - { - sb.AppendLine(" <" + a.Key + ">" + Convert.ToString(a.Value, CultureInfo.InvariantCulture) + - ""); - } - sb.AppendLine(" "); - sb.AppendLine(" "); - sb.Append("\r\n\r\n"); - string requestBody = sb.ToString(); - - byte[] messageBody = Encoding.UTF8.GetBytes(requestBody); - return messageBody; - } - - private XmlDocument GetXmlDocument(string response) - { - XmlNode node; - XmlDocument doc = new(); - doc.LoadXml(response); - - XmlNamespaceManager nsm = new(doc.NameTable); - - // Error messages should be found under this namespace - nsm.AddNamespace("errorNs", "urn:schemas-upnp-org:control-1-0"); - - // Check to see if we have a fault code message. - if ((node = doc.SelectSingleNode("//errorNs:UPnPError", nsm)) != null) - { - int code = Convert.ToInt32(node.GetXmlElementText("errorCode"), CultureInfo.InvariantCulture); - string errorMessage = node.GetXmlElementText("errorDescription"); - NatDiscoverer.TraceSource.LogWarn("Server failed with error: {0} - {1}", code, errorMessage); - throw new MappingException(code, errorMessage); - } - - return doc; - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/UpnpConstants.cs b/src/Open.Nat/Upnp/UpnpConstants.cs deleted file mode 100644 index 5a56ec7..0000000 --- a/src/Open.Nat/Upnp/UpnpConstants.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// Authors: -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2014 Lucas Ontivero -// -// 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. -// - -namespace Open.Nat.Upnp -{ - internal static class UpnpConstants - { - public const int InvalidArguments = 402; - public const int ActionFailed = 501; - public const int Unathorized = 606; - public const int SpecifiedArrayIndexInvalid = 713; - public const int NoSuchEntryInArray = 714; - public const int WildCardNotPermittedInSourceIp = 715; - public const int WildCardNotPermittedInExternalPort = 716; - public const int ConflictInMappingEntry = 718; - public const int SamePortValuesRequired = 724; - public const int OnlyPermanentLeasesSupported = 725; - public const int RemoteHostOnlySupportsWildcard = 726; - public const int ExternalPortOnlySupportsWildcard = 727; - public const int NoPortMapsAvailable = 728; - public const int ConflictWithOtherMechanisms = 729; - public const int WildCardNotPermittedInIntPort = 732; - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/UpnpNatDevice.cs b/src/Open.Nat/Upnp/UpnpNatDevice.cs deleted file mode 100644 index 900de62..0000000 --- a/src/Open.Nat/Upnp/UpnpNatDevice.cs +++ /dev/null @@ -1,486 +0,0 @@ -// -// Authors: -// Alan McGovern alan.mcgovern@gmail.com -// Ben Motmans -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2006 Alan McGovern -// Copyright (C) 2007 Ben Motmans -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Enums; -using Open.Nat.Exceptions; -using Open.Nat.Upnp.Messages.Requests; -using Open.Nat.Upnp.Messages.Responses; -using Open.Nat.Utils; -using System.Net; - -namespace Open.Nat.Upnp -{ - internal sealed class UpnpNatDevice : NatDevice - { - public override IPEndPoint HostEndPoint => DeviceInfo.HostEndPoint; - - public override IPAddress LocalAddress => DeviceInfo.LocalAddress; - - internal readonly UpnpNatDeviceInfo DeviceInfo; - private readonly SoapClient _soapClient; - - internal UpnpNatDevice(UpnpNatDeviceInfo deviceInfo) - { - Touch(); - DeviceInfo = deviceInfo; - _soapClient = new SoapClient(DeviceInfo.ServiceControlUri, DeviceInfo.ServiceType); - } - -#if NET35 - public override Task GetExternalIPAsync() - { - NatDiscoverer.TraceSource.LogInfo("GetExternalIPAsync - Getting external IP address"); - var message = new GetExternalIPAddressRequestMessage(); - return _soapClient - .InvokeAsync("GetExternalIPAddress", message.ToXml()) - .TimeoutAfter(TimeSpan.FromSeconds(4)) - .ContinueWith(task => - { - var responseData = task.Result; - var response = new GetExternalIPAddressResponseMessage(responseData, DeviceInfo.ServiceType); - return response.ExternalIPAddress; - }); - } -#else - public override async Task GetExternalIPAsync() - { - NatDiscoverer.TraceSource.LogInfo("GetExternalIPAsync - Getting external IP address"); - GetExternalIPAddressRequestMessage message = new(); - System.Xml.XmlDocument responseData = await _soapClient - .InvokeAsync("GetExternalIPAddress", message.ToXml()) - .TimeoutAfter(TimeSpan.FromSeconds(4)); - - GetExternalIPAddressResponseMessage response = new(responseData, DeviceInfo.ServiceType); - return response.ExternalIPAddress; - } -#endif - -#if NET35 - public override Task CreatePortMapAsync(Mapping mapping) - { - Guard.IsNotNull(mapping, "mapping"); - if (mapping.PrivateIP.Equals(IPAddress.None)) mapping.PrivateIP = DeviceInfo.LocalAddress; - - NatDiscoverer.TraceSource.LogInfo("CreatePortMapAsync - Creating port mapping {0}", mapping); - - var message = new CreatePortMappingRequestMessage(mapping); - return _soapClient - .InvokeAsync("AddPortMapping", message.ToXml()) - .TimeoutAfter(TimeSpan.FromSeconds(4)) - .ContinueWith(task => - { - if (!task.IsFaulted) - { - RegisterMapping(mapping); - } - else - { - MappingException me = task.Exception.InnerException as MappingException; - - if (me == null) - { - throw task.Exception.InnerException; - } - - switch (me.ErrorCode) - { - case UpnpConstants.OnlyPermanentLeasesSupported: - NatDiscoverer.TraceSource.LogWarn( - "Only Permanent Leases Supported - There is no warranty it will be closed"); - mapping.Lifetime = 0; - // We create the mapping anyway. It must be released on shutdown. - mapping.LifetimeType = MappingLifetime.ForcedSession; - CreatePortMapAsync(mapping); - break; - case UpnpConstants.SamePortValuesRequired: - NatDiscoverer.TraceSource.LogWarn( - "Same Port Values Required - Using internal port {0}", mapping.PrivatePort); - mapping.PublicPort = mapping.PrivatePort; - CreatePortMapAsync(mapping); - break; - case UpnpConstants.RemoteHostOnlySupportsWildcard: - NatDiscoverer.TraceSource.LogWarn("Remote Host Only Supports Wildcard"); - mapping.PublicIP = IPAddress.None; - CreatePortMapAsync(mapping); - break; - case UpnpConstants.ExternalPortOnlySupportsWildcard: - NatDiscoverer.TraceSource.LogWarn("External Port Only Supports Wildcard"); - throw me; - case UpnpConstants.ConflictInMappingEntry: - NatDiscoverer.TraceSource.LogWarn("Conflict with an already existing mapping"); - throw me; - - default: - throw me; - } - } - }); - } -#else - public override async Task CreatePortMapAsync(Mapping mapping) - { - Guard.IsNotNull(mapping, "mapping"); - if (mapping.PrivateIP.Equals(IPAddress.None)) - { - mapping.PrivateIP = DeviceInfo.LocalAddress; - } - - NatDiscoverer.TraceSource.LogInfo("CreatePortMapAsync - Creating port mapping {0}", mapping); - bool retry = false; - try - { - CreatePortMappingRequestMessage message = new(mapping); - await _soapClient - .InvokeAsync("AddPortMapping", message.ToXml()) - .TimeoutAfter(TimeSpan.FromSeconds(4)); - RegisterMapping(mapping); - } - catch (MappingException me) - { - switch (me.ErrorCode) - { - case UpnpConstants.OnlyPermanentLeasesSupported: - NatDiscoverer.TraceSource.LogWarn("Only Permanent Leases Supported - There is no warranty it will be closed"); - mapping.Lifetime = 0; - // We create the mapping anyway. It must be released on shutdown. - mapping.LifetimeType = MappingLifetime.ForcedSession; - retry = true; - break; - case UpnpConstants.SamePortValuesRequired: - NatDiscoverer.TraceSource.LogWarn("Same Port Values Required - Using internal port {0}", mapping.PrivatePort); - mapping.PublicPort = mapping.PrivatePort; - retry = true; - break; - case UpnpConstants.RemoteHostOnlySupportsWildcard: - NatDiscoverer.TraceSource.LogWarn("Remote Host Only Supports Wildcard"); - mapping.PublicIP = IPAddress.None; - retry = true; - break; - case UpnpConstants.ExternalPortOnlySupportsWildcard: - NatDiscoverer.TraceSource.LogWarn("External Port Only Supports Wildcard"); - throw; - case UpnpConstants.ConflictInMappingEntry: - NatDiscoverer.TraceSource.LogWarn("Conflict with an already existing mapping"); - throw; - - default: - throw; - } - } - if (retry) - { - await CreatePortMapAsync(mapping); - } - } -#endif - -#if NET35 - public override Task DeletePortMapAsync(Mapping mapping) - { - Guard.IsNotNull(mapping, "mapping"); - - if (mapping.PrivateIP.Equals(IPAddress.None)) mapping.PrivateIP = DeviceInfo.LocalAddress; - - NatDiscoverer.TraceSource.LogInfo("DeletePortMapAsync - Deleteing port mapping {0}", mapping); - - var message = new DeletePortMappingRequestMessage(mapping); - return _soapClient - .InvokeAsync("DeletePortMapping", message.ToXml()) - .TimeoutAfter(TimeSpan.FromSeconds(4)) - .ContinueWith(task => - { - if (!task.IsFaulted) - { - UnregisterMapping(mapping); - } - else - { - MappingException e = task.Exception.InnerException as MappingException; - if (e != null && e.ErrorCode != UpnpConstants.NoSuchEntryInArray) throw e; - } - }); - } -#else - public override async Task DeletePortMapAsync(Mapping mapping) - { - Guard.IsNotNull(mapping, "mapping"); - - if (mapping.PrivateIP.Equals(IPAddress.None)) - { - mapping.PrivateIP = DeviceInfo.LocalAddress; - } - - NatDiscoverer.TraceSource.LogInfo("DeletePortMapAsync - Deleteing port mapping {0}", mapping); - - try - { - DeletePortMappingRequestMessage message = new(mapping); - await _soapClient - .InvokeAsync("DeletePortMapping", message.ToXml()) - .TimeoutAfter(TimeSpan.FromSeconds(4)); - UnregisterMapping(mapping); - } - catch (MappingException e) - { - if (e.ErrorCode != UpnpConstants.NoSuchEntryInArray) - { - throw; - } - } - } -#endif - -#if NET35 - public void GetGenericMappingAsync(int index, List mappings, - TaskCompletionSource> taskCompletionSource) - { - var message = new GetGenericPortMappingEntry(index); - - _soapClient - .InvokeAsync("GetGenericPortMappingEntry", message.ToXml()) - .TimeoutAfter(TimeSpan.FromSeconds(4)) - .ContinueWith(task => - { - if (!task.IsFaulted) - { - var responseData = task.Result; - var responseMessage = new GetPortMappingEntryResponseMessage(responseData, - DeviceInfo.ServiceType, true); - - IPAddress internalClientIp; - if (!IPAddress.TryParse(responseMessage.InternalClient, out internalClientIp)) - { - NatDiscoverer.TraceSource.LogWarn("InternalClient is not an IP address. Mapping ignored!"); - } - else - { - var mapping = new Mapping(responseMessage.Protocol - , internalClientIp - , responseMessage.InternalPort - , responseMessage.ExternalPort - , responseMessage.LeaseDuration - , responseMessage.PortMappingDescription); - mappings.Add(mapping); - } - - GetGenericMappingAsync(index + 1, mappings, taskCompletionSource); - } - else - { - MappingException e = task.Exception.InnerException as MappingException; - - if (e == null) - { - throw task.Exception.InnerException; - } - - if (e.ErrorCode == UpnpConstants.SpecifiedArrayIndexInvalid - || e.ErrorCode == UpnpConstants.NoSuchEntryInArray) - { - // there are no more mappings - taskCompletionSource.SetResult(mappings); - return; - } - - // DD-WRT Linux base router (and others probably) fails with 402-InvalidArgument when index is out of range - if (e.ErrorCode == UpnpConstants.InvalidArguments) - { - NatDiscoverer.TraceSource.LogWarn("Router failed with 402-InvalidArgument. No more mappings is assumed."); - taskCompletionSource.SetResult(mappings); - return; - } - - throw task.Exception.InnerException; - } - }); - } - - public override Task> GetAllMappingsAsync() - { - var taskCompletionSource = new TaskCompletionSource>(); - - NatDiscoverer.TraceSource.LogInfo("GetAllMappingsAsync - Getting all mappings"); - - GetGenericMappingAsync(0, new List(), taskCompletionSource); - return taskCompletionSource.Task; - } -#else - public override async Task> GetAllMappingsAsync() - { - int index = 0; - List mappings = []; - - NatDiscoverer.TraceSource.LogInfo("GetAllMappingsAsync - Getting all mappings"); - while (true) - { - try - { - GetGenericPortMappingEntry message = new(index++); - - System.Xml.XmlDocument responseData = await _soapClient - .InvokeAsync("GetGenericPortMappingEntry", message.ToXml()) - .TimeoutAfter(TimeSpan.FromSeconds(4)); - - GetPortMappingEntryResponseMessage responseMessage = new(responseData, DeviceInfo.ServiceType, true); - - if (!IPAddress.TryParse(responseMessage.InternalClient, out IPAddress internalClientIp)) - { - NatDiscoverer.TraceSource.LogWarn("InternalClient is not an IP address. Mapping ignored!"); - continue; - } - - Mapping mapping = new(responseMessage.Protocol - , internalClientIp - , responseMessage.InternalPort - , responseMessage.ExternalPort - , responseMessage.LeaseDuration - , responseMessage.PortMappingDescription); - mappings.Add(mapping); - } - catch (MappingException e) - { - // there are no more mappings - if (e.ErrorCode is UpnpConstants.SpecifiedArrayIndexInvalid - or UpnpConstants.NoSuchEntryInArray - // DD-WRT Linux base router (and others probably) fails with 402-InvalidArgument when index is out of range - or UpnpConstants.InvalidArguments - // LINKSYS WRT1900AC AC1900 it returns errocode 501-PAL_UPNP_SOAP_E_ACTION_FAILED - or UpnpConstants.ActionFailed) - { - NatDiscoverer.TraceSource.LogWarn("Router failed with {0}-{1}. No more mappings is assumed.", e.ErrorCode, e.ErrorText); - break; - } - throw; - } - } - - return mappings.ToArray(); - } -#endif - -#if NET35 - public override Task GetSpecificMappingAsync(Protocol protocol, int publicPort) - { - Guard.IsTrue(protocol == Protocol.Tcp || protocol == Protocol.Udp, "protocol"); - Guard.IsInRange(publicPort, 0, ushort.MaxValue, "port"); - - NatDiscoverer.TraceSource.LogInfo("GetSpecificMappingAsync - Getting mapping for protocol: {0} port: {1}", Enum.GetName(typeof(Protocol), protocol), publicPort); - - var message = new GetSpecificPortMappingEntryRequestMessage(protocol, publicPort); - return _soapClient - .InvokeAsync("GetSpecificPortMappingEntry", message.ToXml()) - .TimeoutAfter(TimeSpan.FromSeconds(4)) - .ContinueWith(task => - { - if (!task.IsFaulted) - { - var responseData = task.Result; - - var messageResponse = new GetPortMappingEntryResponseMessage(responseData, DeviceInfo.ServiceType, false); - - return new Mapping(messageResponse.Protocol - , IPAddress.Parse(messageResponse.InternalClient) - , messageResponse.InternalPort - , publicPort // messageResponse.ExternalPort is short.MaxValue - , messageResponse.LeaseDuration - , messageResponse.PortMappingDescription); - } - else - { - MappingException e = task.Exception.InnerException as MappingException; - if (e != null && e.ErrorCode == UpnpConstants.NoSuchEntryInArray) return null; - - // DD-WRT Linux base router (and others probably) fails with 402-InvalidArgument - // when no mapping is found in the mappings table - if (e != null && e.ErrorCode == UpnpConstants.InvalidArguments) - { - NatDiscoverer.TraceSource.LogWarn("Router failed with 402-InvalidArgument. Mapping not found is assumed."); - return null; - } - throw task.Exception.InnerException; - } - }); - } -#else - public override async Task GetSpecificMappingAsync(Protocol protocol, int publicPort) - { - Guard.IsTrue(protocol is Protocol.Tcp or Protocol.Udp, "protocol"); - Guard.IsInRange(publicPort, 0, ushort.MaxValue, "port"); - - NatDiscoverer.TraceSource.LogInfo("GetSpecificMappingAsync - Getting mapping for protocol: {0} port: {1}", Enum.GetName(typeof(Protocol), protocol), publicPort); - - try - { - GetSpecificPortMappingEntryRequestMessage message = new(protocol, publicPort); - System.Xml.XmlDocument responseData = await _soapClient - .InvokeAsync("GetSpecificPortMappingEntry", message.ToXml()) - .TimeoutAfter(TimeSpan.FromSeconds(4)); - - GetPortMappingEntryResponseMessage messageResponse = new(responseData, DeviceInfo.ServiceType, false); - - if (messageResponse.Protocol != protocol) - { - NatDiscoverer.TraceSource.LogWarn("Router responded to a protocol {0} query with a protocol {1} answer, work around applied.", protocol, messageResponse.Protocol); - } - - return new Mapping(protocol - , IPAddress.Parse(messageResponse.InternalClient) - , messageResponse.InternalPort - , publicPort // messageResponse.ExternalPort is short.MaxValue - , messageResponse.LeaseDuration - , messageResponse.PortMappingDescription); - } - catch (MappingException e) - { - // there are no more mappings - if (e.ErrorCode is UpnpConstants.SpecifiedArrayIndexInvalid - or UpnpConstants.NoSuchEntryInArray - // DD-WRT Linux base router (and others probably) fails with 402-InvalidArgument when index is out of range - or UpnpConstants.InvalidArguments - // LINKSYS WRT1900AC AC1900 it returns errocode 501-PAL_UPNP_SOAP_E_ACTION_FAILED - or UpnpConstants.ActionFailed) - { - NatDiscoverer.TraceSource.LogWarn("Router failed with {0}-{1}. No more mappings is assumed.", e.ErrorCode, e.ErrorText); - return null; - } - throw; - } - } -#endif - - public override string ToString() - { - //GetExternalIP is blocking and can throw exceptions, can't use it here. - return string.Format( - "EndPoint: {0}\nControl Url: {1}\nService Type: {2}\nLast Seen: {3}", - DeviceInfo.HostEndPoint, DeviceInfo.ServiceControlUri, DeviceInfo.ServiceType, LastSeen); - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/UpnpNatDeviceInfo.cs b/src/Open.Nat/Upnp/UpnpNatDeviceInfo.cs deleted file mode 100644 index ed89a0b..0000000 --- a/src/Open.Nat/Upnp/UpnpNatDeviceInfo.cs +++ /dev/null @@ -1,60 +0,0 @@ -// -// Authors: -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Utils; -using System.Net; - -namespace Open.Nat.Upnp -{ - internal class UpnpNatDeviceInfo - { - public UpnpNatDeviceInfo(IPAddress localAddress, Uri locationUri, string serviceControlUrl, string serviceType) - { - LocalAddress = localAddress; - ServiceType = serviceType; - HostEndPoint = new IPEndPoint(IPAddress.Parse(locationUri.Host), locationUri.Port); - - if (Uri.IsWellFormedUriString(serviceControlUrl, UriKind.Absolute)) - { - Uri u = new(serviceControlUrl); - IPEndPoint old = HostEndPoint; - serviceControlUrl = u.PathAndQuery; - - NatDiscoverer.TraceSource.LogInfo("{0}: Absolute URI detected. Host address is now: {1}", old, - HostEndPoint); - NatDiscoverer.TraceSource.LogInfo("{0}: New control url: {1}", HostEndPoint, serviceControlUrl); - } - - UriBuilder builder = new("http", locationUri.Host, locationUri.Port); - ServiceControlUri = new Uri(builder.Uri, serviceControlUrl); ; - } - - public IPEndPoint HostEndPoint { get; private set; } - public IPAddress LocalAddress { get; private set; } - public string ServiceType { get; private set; } - public Uri ServiceControlUri { get; private set; } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Upnp/UpnpSearcher.cs b/src/Open.Nat/Upnp/UpnpSearcher.cs deleted file mode 100644 index 822625e..0000000 --- a/src/Open.Nat/Upnp/UpnpSearcher.cs +++ /dev/null @@ -1,319 +0,0 @@ -// -// Authors: -// Ben Motmans -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2007 Ben Motmans -// Copyright (C) 2014 Lucas Ontivero -// -// 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 Open.Nat.Discovery; -using Open.Nat.Upnp.Messages; -using Open.Nat.Utils; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Xml; - -namespace Open.Nat.Upnp -{ - internal class UpnpSearcher : Searcher - { - private readonly IIPAddressesProvider _ipprovider; - private readonly IDictionary _devices; - private readonly Dictionary _lastFetched; - private static readonly string[] ServiceTypes = new[]{ - "WANIPConnection:2", - "WANPPPConnection:2", - "WANIPConnection:1", - "WANPPPConnection:1" - }; - - internal UpnpSearcher(IIPAddressesProvider ipprovider) - { - _ipprovider = ipprovider; - UdpClients = CreateUdpClients(); - _devices = new Dictionary(); - _lastFetched = []; - } - - private List CreateUdpClients() - { - List clients = new(); - try - { - IEnumerable ips = _ipprovider.UnicastAddresses(); - - foreach (IPAddress ipAddress in ips) - { - try - { - clients.Add(new UdpClient(new IPEndPoint(ipAddress, 0))); - } - catch (Exception) - { - continue; // Move on to the next address. - } - } - } - catch (Exception) - { - clients.Add(new UdpClient(0)); - } - return clients; - } - - protected override void Discover(UdpClient client, CancellationToken cancelationToken) - { - // for testing use: - // var ip = IPAddress.Broadcast; - Discover(client, WellKnownConstants.IPv4MulticastAddress, cancelationToken); - if (Socket.OSSupportsIPv6) - { - Discover(client, WellKnownConstants.IPv6LinkLocalMulticastAddress, cancelationToken); - Discover(client, WellKnownConstants.IPv6LinkSiteMulticastAddress, cancelationToken); - } - } - - private void Discover(UdpClient client, IPAddress address, CancellationToken cancelationToken) - { - if (!IsValidClient(client.Client, address)) - { - return; - } - - NextSearch = DateTime.UtcNow.AddSeconds(1); - IPEndPoint searchEndpoint = new(address, 1900); - - foreach (string serviceType in ServiceTypes) - { - string datax = DiscoverDeviceMessage.Encode(serviceType, address); - byte[] data = Encoding.ASCII.GetBytes(datax); - - // UDP is unreliable, so send 3 requests at a time (per Upnp spec, sec 1.1.2) - // Yes, however it works perfectly well with just 1 request. - for (int i = 0; i < 3; i++) - { - if (cancelationToken.IsCancellationRequested) - { - return; - } - - client.Send(data, data.Length, searchEndpoint); - } - } - } - - private bool IsValidClient(Socket socket, IPAddress address) - { - IPEndPoint? endpoint = (IPEndPoint)socket.LocalEndPoint; - if (socket.AddressFamily != address.AddressFamily) - { - return false; - } - - switch (socket.AddressFamily) - { - case AddressFamily.InterNetwork: - socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, endpoint.Address.GetAddressBytes()); - return true; - case AddressFamily.InterNetworkV6: - if (endpoint.Address.IsIPv6LinkLocal && !Equals(address, WellKnownConstants.IPv6LinkLocalMulticastAddress)) - { - return false; - } - - if (!endpoint.Address.IsIPv6LinkLocal && !Equals(address, WellKnownConstants.IPv6LinkSiteMulticastAddress)) - { - return false; - } - - socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.MulticastInterface, BitConverter.GetBytes((int)endpoint.Address.ScopeId)); - return true; - } - return false; - } - - public override NatDevice? AnalyseReceivedResponse(IPAddress localAddress, byte[] response, IPEndPoint endpoint) - { - // Convert it to a string for easy parsing - string dataString = null; - - // No matter what, this method should never throw an exception. If something goes wrong - // we should still be in a position to handle the next reply correctly. - try - { - dataString = Encoding.UTF8.GetString(response); - DiscoveryResponseMessage message = new(dataString); - string serviceType = message["ST"]; - - if (!IsValidControllerService(serviceType)) - { - NatDiscoverer.TraceSource.LogWarn("Invalid controller service. Ignoring."); - - return null; - } - NatDiscoverer.TraceSource.LogInfo("UPnP Response: Router advertised a '{0}' service!!!", serviceType); - - string location = message["Location"] ?? message["AL"]; - Uri locationUri = new(location); - NatDiscoverer.TraceSource.LogInfo("Found device at: {0}", locationUri.ToString()); - - if (_devices.ContainsKey(locationUri)) - { - NatDiscoverer.TraceSource.LogInfo("Already found - Ignored"); - _devices[locationUri].Touch(); - return null; - } - - // If we send 3 requests at a time, ensure we only fetch the services list once - // even if three responses are received - if (_lastFetched.ContainsKey(endpoint.Address)) - { - DateTime last = _lastFetched[endpoint.Address]; - if (DateTime.Now - last < TimeSpan.FromSeconds(20)) - { - return null; - } - } - _lastFetched[endpoint.Address] = DateTime.Now; - - NatDiscoverer.TraceSource.LogInfo("{0}:{1}: Fetching service list", locationUri.Host, locationUri.Port); - - UpnpNatDeviceInfo deviceInfo = BuildUpnpNatDeviceInfo(localAddress, locationUri); - - UpnpNatDevice device; - lock (_devices) - { - device = new UpnpNatDevice(deviceInfo); - if (!_devices.ContainsKey(locationUri)) - { - _devices.Add(locationUri, device); - } - } - return device; - } - catch (Exception ex) - { - NatDiscoverer.TraceSource.LogError("Unhandled exception when trying to decode a device's response. "); - NatDiscoverer.TraceSource.LogError("Report the issue in https://github.com/lontivero/Open.Nat/issues"); - NatDiscoverer.TraceSource.LogError("Also copy and paste the following info:"); - NatDiscoverer.TraceSource.LogError("-- beging ---------------------------------"); - NatDiscoverer.TraceSource.LogError(ex.Message); - NatDiscoverer.TraceSource.LogError("Data string:"); - NatDiscoverer.TraceSource.LogError(dataString ?? "No data available"); - NatDiscoverer.TraceSource.LogError("-- end ------------------------------------"); - } - return null; - } - - private static bool IsValidControllerService(string serviceType) - { - var services = from serviceName in ServiceTypes - let serviceUrn = string.Format("urn:schemas-upnp-org:service:{0}", serviceName) - where serviceType.ContainsIgnoreCase(serviceUrn) - select new { ServiceName = serviceName, ServiceUrn = serviceUrn }; - - return services.Any(); - } - - private UpnpNatDeviceInfo BuildUpnpNatDeviceInfo(IPAddress localAddress, Uri location) - { - NatDiscoverer.TraceSource.LogInfo("Found device at: {0}", location.ToString()); - - IPEndPoint hostEndPoint = new(IPAddress.Parse(location.Host), location.Port); - - WebResponse response = null; - try - { -#if NET35 - var request = WebRequest.Create(location); -#else - HttpWebRequest request = WebRequest.CreateHttp(location); -#endif - request.Headers.Add("ACCEPT-LANGUAGE", "en"); - request.Method = "GET"; - - response = request.GetResponse(); - - - if (response is HttpWebResponse httpresponse && httpresponse.StatusCode != HttpStatusCode.OK) - { - string message = string.Format("Couldn't get services list: {0} {1}", httpresponse.StatusCode, httpresponse.StatusDescription); - throw new Exception(message); - } - - XmlDocument xmldoc = ReadXmlResponse(response); - - NatDiscoverer.TraceSource.LogInfo("{0}: Parsed services list", hostEndPoint); - - XmlNamespaceManager ns = new(xmldoc.NameTable); - ns.AddNamespace("ns", "urn:schemas-upnp-org:device-1-0"); - XmlNodeList? services = xmldoc.SelectNodes("//ns:service", ns); - - foreach (XmlNode service in services) - { - string serviceType = service.GetXmlElementText("serviceType"); - if (!IsValidControllerService(serviceType)) - { - continue; - } - - NatDiscoverer.TraceSource.LogInfo("{0}: Found service: {1}", hostEndPoint, serviceType); - - string serviceControlUrl = service.GetXmlElementText("controlURL"); - NatDiscoverer.TraceSource.LogInfo("{0}: Found upnp service at: {1}", hostEndPoint, serviceControlUrl); - - NatDiscoverer.TraceSource.LogInfo("{0}: Handshake Complete", hostEndPoint); - return new UpnpNatDeviceInfo(localAddress, location, serviceControlUrl, serviceType); - } - - throw new Exception("No valid control service was found in the service descriptor document"); - } - catch (WebException ex) - { - // Just drop the connection, FIXME: Should i retry? - NatDiscoverer.TraceSource.LogError("{0}: Device denied the connection attempt: {1}", hostEndPoint, ex); - if (ex.InnerException is SocketException inner) - { - NatDiscoverer.TraceSource.LogError("{0}: ErrorCode:{1}", hostEndPoint, inner.ErrorCode); - NatDiscoverer.TraceSource.LogError("Go to http://msdn.microsoft.com/en-us/library/system.net.sockets.socketerror.aspx"); - NatDiscoverer.TraceSource.LogError("Usually this happens. Try resetting the device and try again. If you are in a VPN, disconnect and try again."); - } - throw; - } - finally - { - response?.Close(); - } - } - - private static XmlDocument ReadXmlResponse(WebResponse response) - { - using StreamReader reader = new(response.GetResponseStream(), Encoding.UTF8); - string servicesXml = reader.ReadToEnd(); - XmlDocument xmldoc = new(); - xmldoc.LoadXml(servicesXml); - return xmldoc; - } - } -} diff --git a/src/Open.Nat/Utils/Guard.cs b/src/Open.Nat/Utils/Guard.cs deleted file mode 100644 index b0407e4..0000000 --- a/src/Open.Nat/Utils/Guard.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Open.Nat.Utils -{ - internal class Guard - { - private Guard() - { - } - - internal static void IsInRange(int paramValue, int lowerBound, int upperBound, string paramName) - { - if (paramValue < lowerBound || paramValue > upperBound) - { - throw new ArgumentOutOfRangeException(paramName); - } - } - - internal static void IsTrue(bool exp, string paramName) - { - if (!exp) - { - throw new ArgumentOutOfRangeException(paramName); - } - } - - internal static void IsNotNull(object obj, string paramName) - { - if (obj == null) - { - throw new ArgumentNullException(paramName); - } - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Utils/IIPAddressesProvider.cs b/src/Open.Nat/Utils/IIPAddressesProvider.cs deleted file mode 100644 index 845b65d..0000000 --- a/src/Open.Nat/Utils/IIPAddressesProvider.cs +++ /dev/null @@ -1,37 +0,0 @@ -// -// Authors: -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2014 Lucas Ontivero -// -// 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; - -namespace Open.Nat.Utils -{ - internal interface IIPAddressesProvider - { - IEnumerable DnsAddresses(); - IEnumerable GatewayAddresses(); - IEnumerable UnicastAddresses(); - } -} \ No newline at end of file diff --git a/src/Open.Nat/Utils/IPAddressesProvider.cs b/src/Open.Nat/Utils/IPAddressesProvider.cs deleted file mode 100644 index cf8bde2..0000000 --- a/src/Open.Nat/Utils/IPAddressesProvider.cs +++ /dev/null @@ -1,67 +0,0 @@ -// -// Authors: -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2014 Lucas Ontivero -// -// 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.Net.NetworkInformation; -using System.Net.Sockets; - -namespace Open.Nat.Utils -{ - internal class IPAddressesProvider : IIPAddressesProvider - { - #region IIPAddressesProvider Members - - public IEnumerable UnicastAddresses() - { - return IPAddresses(p => p.UnicastAddresses.Select(x => x.Address)); - } - - public IEnumerable DnsAddresses() - { - return IPAddresses(p => p.DnsAddresses); - } - - public IEnumerable GatewayAddresses() - { - return IPAddresses(p => p.GatewayAddresses.Select(x => x.Address)); - } - - #endregion - - private static IEnumerable IPAddresses(Func> ipExtractor) - { - return from networkInterface in NetworkInterface.GetAllNetworkInterfaces() - where - networkInterface.OperationalStatus is OperationalStatus.Up or - OperationalStatus.Unknown - let properties = networkInterface.GetIPProperties() - from address in ipExtractor(properties) - where address.AddressFamily is AddressFamily.InterNetwork - or AddressFamily.InterNetworkV6 - select address; - } - } -} \ No newline at end of file diff --git a/src/Open.Nat/Utils/StreamExtensions.cs b/src/Open.Nat/Utils/StreamExtensions.cs deleted file mode 100644 index 8892cc1..0000000 --- a/src/Open.Nat/Utils/StreamExtensions.cs +++ /dev/null @@ -1,228 +0,0 @@ -// -// Authors: -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2014 Lucas Ontivero -// -// 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.Diagnostics; -using System.Text; -using System.Xml; - -namespace Open.Nat.Utils -{ - internal static class StreamExtensions - { - internal static string ReadAsMany(this StreamReader stream, int bytesToRead) - { - char[] buffer = new char[bytesToRead]; - stream.ReadBlock(buffer, 0, bytesToRead); - return new string(buffer); - } - - internal static string GetXmlElementText(this XmlNode node, string elementName) - { - XmlElement element = node[elementName]; - return element != null ? element.InnerText : string.Empty; - } - - internal static bool ContainsIgnoreCase(this string s, string pattern) - { - return s.IndexOf(pattern, StringComparison.OrdinalIgnoreCase) >= 0; - } - - internal static void LogInfo(this TraceSource source, string format, params object[] args) - { - try - { - source.TraceEvent(TraceEventType.Information, 0, format, args); - } - catch (ObjectDisposedException) - { - source.Switch.Level = SourceLevels.Off; - } - } - - internal static void LogWarn(this TraceSource source, string format, params object[] args) - { - try - { - source.TraceEvent(TraceEventType.Warning, 0, format, args); - } - catch (ObjectDisposedException) - { - source.Switch.Level = SourceLevels.Off; - } - } - - - internal static void LogError(this TraceSource source, string format, params object[] args) - { - try - { - source.TraceEvent(TraceEventType.Error, 0, format, args); - } - catch (ObjectDisposedException) - { - source.Switch.Level = SourceLevels.Off; - } - } - - internal static string ToPrintableXml(this XmlDocument document) - { - using MemoryStream stream = new(); - using XmlTextWriter writer = new(stream, Encoding.Unicode); - try - { - writer.Formatting = Formatting.Indented; - - document.WriteContentTo(writer); - writer.Flush(); - stream.Flush(); - - // Have to rewind the MemoryStream in order to read - // its contents. - stream.Position = 0; - - // Read MemoryStream contents into a StreamReader. - StreamReader reader = new(stream); - - // Extract the text from the StreamReader. - return reader.ReadToEnd(); - } - catch (Exception) - { - return document.ToString(); - } - } - -#if NET35 - public static Task TimeoutAfter(this Task task, TimeSpan timeout) - { -#if DEBUG - return task; -#endif - var timeoutCancellationTokenSource = new CancellationTokenSource(); - - return TaskExtension.WhenAny(task, TaskExtension.Delay(timeout, timeoutCancellationTokenSource.Token)) - .ContinueWith(t => - { - Task completedTask = t.Result; - - if (completedTask == task) - { - timeoutCancellationTokenSource.Cancel(); - return task; - } - throw new TimeoutException( - "The operation has timed out. The network is broken, router has gone or is too busy."); - }).Unwrap(); - } -#else - public static async Task TimeoutAfter(this Task task, TimeSpan timeout) - { -#if DEBUG - return await task; -#endif - CancellationTokenSource timeoutCancellationTokenSource = new(); - - Task completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token)); - if (completedTask == task) - { - timeoutCancellationTokenSource.Cancel(); - return await task; - } - throw new TimeoutException( - "The operation has timed out. The network is broken, router has gone or is too busy."); - } -#endif //NET35 - } - -#if NET35 - internal static class EnumExtension - { - public static bool HasFlag(this Enum value, Enum flag) - { - int intValue = (int)(ValueType)value; - int intFlag = (int)(ValueType)flag; - - return (intValue & intFlag) == intFlag; - } - } - - public static class CancellationTokenSourceExtension - { - public static void CancelAfter(this CancellationTokenSource source, int millisecondsDelay) - { - if (millisecondsDelay < -1) - { - throw new ArgumentOutOfRangeException("millisecondsDelay"); - } - Timer timer = new Timer(self => { - ((Timer)self).Dispose(); - try - { - source.Cancel(); - } - catch (ObjectDisposedException) { } - }); - timer.Change(millisecondsDelay, -1); - } - } - - public static class TaskExtension - { - public static Task Delay(TimeSpan delay, CancellationToken token) - { - long delayMs = (long)delay.TotalMilliseconds; - if (delayMs < -1L || delayMs > int.MaxValue) - { - throw new ArgumentOutOfRangeException("delay"); - } - TaskCompletionSource tcs = new TaskCompletionSource(); - - Timer timer = new Timer(self => - { - tcs.TrySetResult(null); //timer expired, attempt to move task to the completed state. - }, null, delayMs, -1); - - token.Register(() => - { - timer.Dispose(); //stop the timer - tcs.TrySetCanceled(); //attempt to mode task to canceled state - }); - - return tcs.Task; - } - - public static Task WhenAny(params Task[] tasks) - { - return Task.Factory.ContinueWhenAny(tasks, t => t); - } - - public static Task WhenAll(params Task[] tasks) - { - return Task.Factory.ContinueWhenAll(tasks, t => t); - } - } -#endif //NET35 -} \ No newline at end of file diff --git a/src/Open.Nat/Utils/WellKnownConstants.cs b/src/Open.Nat/Utils/WellKnownConstants.cs deleted file mode 100644 index 40324de..0000000 --- a/src/Open.Nat/Utils/WellKnownConstants.cs +++ /dev/null @@ -1,39 +0,0 @@ -// -// Authors: -// Lucas Ontivero lucasontivero@gmail.com -// -// Copyright (C) 2014 Lucas Ontivero -// -// 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; - -namespace Open.Nat.Utils -{ - internal static class WellKnownConstants - { - public static IPAddress IPv4MulticastAddress = IPAddress.Parse("239.255.255.250"); - public static IPAddress IPv6LinkLocalMulticastAddress = IPAddress.Parse("FF02::C"); - public static IPAddress IPv6LinkSiteMulticastAddress = IPAddress.Parse("FF05::C"); - - public static IPEndPoint NatPmpEndPoint = new(IPAddress.Parse("192.168.0.1"), 5351); - } -} \ No newline at end of file diff --git a/src/Perpetuum.Bootstrapper/Modules/LoggersModule.cs b/src/Perpetuum.Bootstrapper/Modules/LoggersModule.cs index 50437eb..0f66523 100644 --- a/src/Perpetuum.Bootstrapper/Modules/LoggersModule.cs +++ b/src/Perpetuum.Bootstrapper/Modules/LoggersModule.cs @@ -100,7 +100,11 @@ protected override void Load(ContainerBuilder builder) } }; - return new CompositeLogger(fileLogger, new ColoredConsoleLogger(formater)); + if (Environment.UserInteractive) + { + return new CompositeLogger(fileLogger, new ColoredConsoleLogger(formater)); + } + return new CompositeLogger(fileLogger); }).As>(); _ = builder.RegisterType(); diff --git a/src/Perpetuum.Bootstrapper/PerpetuumBootstrapper.cs b/src/Perpetuum.Bootstrapper/PerpetuumBootstrapper.cs index 8717ade..2050915 100644 --- a/src/Perpetuum.Bootstrapper/PerpetuumBootstrapper.cs +++ b/src/Perpetuum.Bootstrapper/PerpetuumBootstrapper.cs @@ -59,6 +59,7 @@ using Perpetuum.Zones.Terrains.Terraforming; using SharpOpenNat; using System.Numerics; +using System.Reflection; using System.Runtime; using System.Runtime.Caching; using System.Runtime.Versioning; @@ -138,7 +139,15 @@ public void Init(string gameRoot) GlobalConfiguration config = _container.Resolve(); _container.Resolve().State = HostState.Init; - + // Get last commit hash + var version = Assembly.GetEntryAssembly() + ?.GetCustomAttribute() + ?.InformationalVersion; +#if DEBUG + Logger.Warning($"DEBUG Version: {version}, UserInteractive: {Environment.UserInteractive}"); +#else + Logger.Info($"RELEASE Version: {version}, UserInteractive: {Environment.UserInteractive}"); +#endif Logger.Info($"Game root: {config.GameRoot}"); Logger.Info($"GC isServerGC: {GCSettings.IsServerGC}"); Logger.Info($"GC Latency mode: {GCSettings.LatencyMode}"); diff --git a/src/Perpetuum/IO/FileSystem.cs b/src/Perpetuum/IO/FileSystem.cs index f0cb976..b83549a 100644 --- a/src/Perpetuum/IO/FileSystem.cs +++ b/src/Perpetuum/IO/FileSystem.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using System.Security.Cryptography; namespace Perpetuum.IO { @@ -37,6 +38,27 @@ public void WriteAllBytes(string path, byte[] bytes) File.WriteAllBytes(CreatePath(path),bytes); } + public byte[] WriteAllBytesAndMD5(string path, ReadOnlySpan bytes) + { + // The algorithm calculates the hash and passes the data through without modification + using (var md5 = MD5.Create()) + { + // Open a stream to a file + using (var fileStream = File.Create(CreatePath(path))) + // Wrap it in a CryptoStream, passing in the hashing algorithm + using (var cryptoStream = new CryptoStream(fileStream, md5, CryptoStreamMode.Write)) + { + // We write the data (it will go both to the file and to MD5) + cryptoStream.Write(bytes); + // Important: Call FlushFinalBlock to complete the hash calculation + cryptoStream.FlushFinalBlock(); + } + + // Obtain the final hash from the algorithm object + return md5.Hash; + } + } + public void WriteAllLines(string path, IEnumerable lines) { File.WriteAllLines(CreatePath(path), lines); @@ -82,5 +104,19 @@ public override string ToString() { return $"Root: {_root}"; } + + public byte[] MD5SUM(string path) + { + // We use the hash algorithm MD5 + using (var md5 = MD5.Create()) + { + // Using a file stream for reading to calculate the hash + using (var stream = File.OpenRead(CreatePath(path))) + { + // The method itself will read the entire stream to the end + return md5.ComputeHash(stream); + } + } + } } -} \ No newline at end of file +} diff --git a/src/Perpetuum/IO/FileSystemExtensions.cs b/src/Perpetuum/IO/FileSystemExtensions.cs index 2f18983..2816a95 100644 --- a/src/Perpetuum/IO/FileSystemExtensions.cs +++ b/src/Perpetuum/IO/FileSystemExtensions.cs @@ -14,6 +14,29 @@ public static byte[] ReadLayerAsByteArray(this IFileSystem fileSystem, string fi return fileSystem.ReadAllBytes(CreateLayerPath(filename)); } + /// + /// Returns a hash for the layer file. + /// + /// + /// Layer filename + /// 16 bytes of hash + public static byte[] MD5(this IFileSystem fileSystem, string filename) + { + return fileSystem.MD5SUM(CreateLayerPath(filename)); + } + + /// + /// Writes all bytes to a file on disk, calculating the hash in parallel. + /// + /// + /// Layer filename + /// Layer data as bytes + /// 16 bytes of hash + public static byte[] WriteLayerAndMD5(this IFileSystem fileSystem, string filename, ReadOnlySpan bytes) + { + return fileSystem.WriteAllBytesAndMD5(CreateLayerPath(filename), bytes); + } + private static string CreateLayerPath(string filename) { return Path.Combine("layers", filename); diff --git a/src/Perpetuum/IO/IFileSystem.cs b/src/Perpetuum/IO/IFileSystem.cs index 2f4bff3..35f368d 100644 --- a/src/Perpetuum/IO/IFileSystem.cs +++ b/src/Perpetuum/IO/IFileSystem.cs @@ -10,6 +10,14 @@ public interface IFileSystem string[] ReadAllLines(string path); void WriteAllBytes(string path, byte[] bytes); + + /// + /// Writes all bytes to a file on disk, calculating the hash in parallel. + /// + /// Path within the game's root directory + /// Bytes to write + /// MD5 hash + public byte[] WriteAllBytesAndMD5(string path, ReadOnlySpan bytes); void WriteAllLines(string path,IEnumerable lines); void AppendAllText(string path, string text); @@ -21,6 +29,13 @@ public interface IFileSystem string CreatePath(string path); + /// + /// Calculates the hash using the MD5 algorithm + /// + /// Path within the game's root directory + /// 16 bytes of hash + byte[] MD5SUM(string path); + IEnumerable GetFiles(string path, string mask); } -} \ No newline at end of file +} diff --git a/src/Perpetuum/Robots/RobotComponent.cs b/src/Perpetuum/Robots/RobotComponent.cs index da9f1d0..88b14cd 100644 --- a/src/Perpetuum/Robots/RobotComponent.cs +++ b/src/Perpetuum/Robots/RobotComponent.cs @@ -130,7 +130,7 @@ public bool IsValidSlotTo(Module module, int slot) public ErrorCodes CanEquipModule(Module module, int slot, int robotDefinition = 0) { - return IsRobotAllowed(module, robotDefinition) + return IsRobotNotAllowed(module, robotDefinition) ? ErrorCodes.NotAllowedOnThisBot : IsUsedSlot(slot) ? ErrorCodes.UsedSlot @@ -145,7 +145,7 @@ public ErrorCodes CanEquipModule(Module module, int slot, int robotDefinition = : ErrorCodes.NoError; } - private bool IsRobotAllowed(Module module, int robotDefinition = 0) + private bool IsRobotNotAllowed(Module module, int robotDefinition = 0) { return module.ED.Options.AllowedBots.Length > 0 && !module.ED.Options.AllowedBots.Contains(robotDefinition); } diff --git a/src/Perpetuum/Zones/Terrains/TerraformableAltitude.cs b/src/Perpetuum/Zones/Terrains/TerraformableAltitude.cs index 971d84c..0fd9a6a 100644 --- a/src/Perpetuum/Zones/Terrains/TerraformableAltitude.cs +++ b/src/Perpetuum/Zones/Terrains/TerraformableAltitude.cs @@ -7,26 +7,23 @@ public class TerraformableAltitude : AltitudeLayer private const ushort BARRIER_MINIMUM = 1850; private const ushort BARRIER_MAXIMUM = 30000; //32768at SOHA nem lephetjuk at - private readonly ILayer _blend; - public TerraformableAltitude(ILayer original,ILayer blend,ushort[] rawData) : base(rawData,original.Width,original.Height) { OriginalAltitude = original; - _blend = blend; Barrier = new Layer(LayerType.Barrier, Width, Height); - CalculateBarrier(); + CalculateBarrier(blend); } public ILayer OriginalAltitude { get; } public ILayer Barrier { get; } - private void CalculateBarrier() + private void CalculateBarrier(ILayer blend) { for (var i = 0; i < Barrier.RawData.Length; i++) { var originalValue = OriginalAltitude.RawData[i]; - var blendValue = _blend.RawData[i] / (double)ushort.MaxValue; + var blendValue = blend.RawData[i] / (double)ushort.MaxValue; var minBarrier = BARRIER_MINIMUM.Mix(originalValue, blendValue); var maxBarrier = BARRIER_MAXIMUM.Mix(originalValue, blendValue); diff --git a/src/Perpetuum/Zones/ZoneExtensions.cs b/src/Perpetuum/Zones/ZoneExtensions.cs index fe9e047..250c1f0 100644 --- a/src/Perpetuum/Zones/ZoneExtensions.cs +++ b/src/Perpetuum/Zones/ZoneExtensions.cs @@ -8,7 +8,7 @@ using Perpetuum.Zones.RemoteControl; using Perpetuum.Zones.Terrains; using System.Drawing; -using System.Security.Cryptography; +using System.Runtime.InteropServices; namespace Perpetuum.Zones { @@ -46,14 +46,14 @@ public T[] LoadLayerData(IZone zone, string name) where T : struct public void SaveLayerToDisk(IZone zone, ILayer layer) where T : struct { string baseFilename = zone.CreateTerrainDataFilename(layer.LayerType.ToString().ToLower(), ""); - - using MD5 md5 = MD5.Create(); string tmpFn = baseFilename + "tmp" + DateTime.Now.Ticks + ".bin"; - byte[] layerData = layer.RawData.ToByteArray(); - _fileSystem.WriteLayer(tmpFn, layerData); + + ReadOnlySpan layerData = MemoryMarshal.AsBytes(layer.RawData.AsSpan()); + var hash = _fileSystem.WriteLayerAndMD5(tmpFn, layerData); - if (!md5.ComputeHash(layerData).SequenceEqual(md5.ComputeHash(_fileSystem.ReadLayerAsByteArray(tmpFn)))) + if (!hash.SequenceEqual(_fileSystem.MD5(tmpFn))) { + Logger.Error("Layer not saved. (" + baseFilename + ")"); return; }