// // 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 System; using System.Net; namespace Open.Nat { 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. 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 { return _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 { return _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. /// The description. internal 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 == Protocol.Tcp || protocol == Protocol.Udp, "protocol"); 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, int.MaxValue, description) { } /// /// Initializes a new instance of the class. /// /// The protocol. /// The private port. /// The public port. /// The lifetime. /// 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 (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; var m = obj as Mapping; if (ReferenceEquals(null, m)) return false; return PublicPort == m.PublicPort && PrivatePort == m.PrivatePort; } public override int GetHashCode() { unchecked { var 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); } } }