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; } } }); }
void onPortMappingDone(Open.Nat.Mapping mapping, bool isError) { if (isError) { Debug.Log("Port mapping failed"); } else { Debug.Log("Port " + mapping.PublicPort + " mapped (" + mapping.Protocol + ")"); } }
public void OpenExternalPort(int privatePort, int publicPort, string description) { localHostPort = privatePort; var timeSpan = new TimeSpan(0, 0, 0, 30); var cancellationTokenSource = new CancellationTokenSource(timeSpan); natDiscoverer = new NatDiscoverer(); natDevice = natDiscoverer .DiscoverDeviceAsync(PortMapper.Upnp, cancellationTokenSource) .IsCompleted(); mapping = new Mapping(Protocol.Tcp, privatePort, publicPort, description); natDevice .CreatePortMapAsync(mapping) .IsCompleted(); }
private async Task<Mapping> InternalCreatePortMapAsync(Mapping mapping, bool create) { var package = new List<byte>(); package.Add(PmpConstants.Version); package.Add(mapping.Protocol == Protocol.Tcp ? PmpConstants.OperationCodeTcp : PmpConstants.OperationCodeUdp); package.Add(0); //reserved package.Add(0); //reserved package.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short) mapping.PrivatePort))); package.AddRange( BitConverter.GetBytes(create ? IPAddress.HostToNetworkOrder((short) mapping.PublicPort) : (short) 0)); package.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(mapping.Lifetime))); try { byte[] buffer = package.ToArray(); int attempt = 0; int delay = PmpConstants.RetryDelay; using (var udpClient = new UdpClient()) { CreatePortMapListen(udpClient, mapping); while (attempt < PmpConstants.RetryAttempts) { await udpClient.SendAsync(buffer, buffer.Length, new IPEndPoint(LocalAddress, PmpConstants.ServerPort)); 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); var pmpException = e as MappingException; throw new MappingException(message, pmpException); } return mapping; }
public override async Task CreatePortMapAsync(Mapping mapping) { Guard.IsNotNull(mapping, "mapping"); mapping.PrivateIP = DeviceInfo.LocalAddress; try { var message = new CreatePortMappingRequestMessage(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; 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; case UpnpConstants.ConflictInMappingEntry: NatDiscoverer.TraceSource.LogWarn("Conflict with an already existing mapping"); throw; default: throw; } } }
internal async Task<bool> CreateNatTraversalEntry(NatMappingEntry entry) { try { var discoverer = new NatDiscoverer(); var cts = new CancellationTokenSource(10000); var device = await discoverer.DiscoverDeviceAsync(PortMapper.Upnp, cts); Mapping mapping = new Mapping((Protocol)((int)entry.Protocol), entry.PrivatePort, entry.PublicPort, entry.Description); await device.CreatePortMapAsync(mapping); return true; } catch (NatDeviceNotFoundException NfExc) { //log exc return false; } catch (MappingException MExc) { //log exc return false; } }
public override async Task DeletePortMapAsync(Mapping mapping) { Guard.IsNotNull(mapping, "mapping"); try { var message = new DeletePortMappingRequestMessage(mapping); await _soapClient .InvokeAsync("DeletePortMapping", message.ToXml()) .TimeoutAfter(TimeSpan.FromSeconds(4)); UnregisterMapping(mapping); } catch (MappingException e) { if(e.ErrorCode != UpnpConstants.NoSuchEntryInArray) throw; } }
public DeletePortMappingRequestMessage(Mapping mapping) { _mapping = mapping; }
private void CreatePortMapListen(UdpClient udpClient, Mapping mapping) { var endPoint = new IPEndPoint(LocalAddress, PmpConstants.ServerPort); while (true) { byte[] data = udpClient.Receive(ref endPoint); if (data.Length < 16) continue; if (data[0] != PmpConstants.Version) continue; var opCode = (byte) (data[1] & 127); var protocol = Protocol.Tcp; if (opCode == PmpConstants.OperationCodeUdp) protocol = Protocol.Udp; short resultCode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 2)); int epoch = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 4)); short privatePort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 8)); short publicPort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 10)); var lifetime = (uint) IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 12)); if (privatePort < 0 || publicPort < 0 || resultCode != PmpConstants.ResultCodeSuccess) { var errors = new[] { "Success", "Unsupported Version", "Not Authorized/Refused (e.g. box supports mapping, but user has turned feature off)" , "Network Failure (e.g. NAT box itself has not obtained a DHCP lease)", "Out of resources (NAT box cannot create any more mappings at this time)", "Unsupported opcode" }; 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; } }
/// <summary> /// Creates the port map asynchronous. /// </summary> /// <param name="mapping">The <see cref="Mapping">Mapping</see> entry.</param> /// <example> /// device.CreatePortMapAsync(new Mapping(Protocol.Tcp, 1700, 1600)); /// </example> /// <exception cref="MappingException">MappingException</exception> public abstract Task CreatePortMapAsync(Mapping mapping);
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); } }); }
protected void RegisterMapping(Mapping mapping) { _openedMapping.Remove(mapping); _openedMapping.Add(mapping); }
/// <summary> /// Deletes a mapped port asynchronous. /// </summary> /// <param name="mapping">The <see cref="Mapping">Mapping</see> entry.</param> /// <example> /// device.DeletePortMapAsync(new Mapping(Protocol.Tcp, 1700, 1600)); /// </example> /// <exception cref="MappingException">MappingException-class</exception> public abstract Task DeletePortMapAsync(Mapping mapping);
protected void UnregisterMapping(Mapping mapping) { _openedMapping.RemoveWhere(x => x.Equals(mapping)); }
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; } } })); }
public void GetGenericMappingAsync(int index, List <Mapping> mappings, TaskCompletionSource <IEnumerable <Mapping> > 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 async Task<IEnumerable<Mapping>> GetAllMappingsAsync() { var index = 0; var mappings = new List<Mapping>(); while (true) { try { var message = new GetGenericPortMappingEntry(index); var responseData = await _soapClient .InvokeAsync("GetGenericPortMappingEntry", message.ToXml()) .TimeoutAfter(TimeSpan.FromSeconds(4)); var responseMessage = new GetPortMappingEntryResponseMessage(responseData, DeviceInfo.ServiceType, true); var mapping = new Mapping(responseMessage.Protocol , IPAddress.Parse(responseMessage.InternalClient) , responseMessage.InternalPort , responseMessage.ExternalPort , responseMessage.LeaseDuration , responseMessage.PortMappingDescription); mappings.Add(mapping); index++; } catch (MappingException e) { if (e.ErrorCode == UpnpConstants.SpecifiedArrayIndexInvalid) break; // there are no more mappings throw; } } return mappings.ToArray(); }
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; }
public CreatePortMappingRequestMessage(Mapping mapping) { _mapping = mapping; }
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; } }); }
private async Task RenewMapping(Mapping mapping) { var renewMapping = new Mapping(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); } }
public void GetGenericMappingAsync(int index, List<Mapping> mappings, TaskCompletionSource<IEnumerable<Mapping>> 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 async Task<IEnumerable<Mapping>> GetAllMappingsAsync() { var index = 0; var mappings = new List<Mapping>(); NatDiscoverer.TraceSource.LogInfo("GetAllMappingsAsync - Getting all mappings"); while (true) { try { var message = new GetGenericPortMappingEntry(index++); var responseData = await _soapClient .InvokeAsync("GetGenericPortMappingEntry", message.ToXml()) .TimeoutAfter(TimeSpan.FromSeconds(4)); 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!"); continue; } var mapping = new Mapping(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 == UpnpConstants.SpecifiedArrayIndexInvalid || e.ErrorCode == UpnpConstants.NoSuchEntryInArray // DD-WRT Linux base router (and others probably) fails with 402-InvalidArgument when index is out of range || e.ErrorCode == UpnpConstants.InvalidArguments // LINKSYS WRT1900AC AC1900 it returns errocode 501-PAL_UPNP_SOAP_E_ACTION_FAILED || e.ErrorCode == UpnpConstants.ActionFailed) { NatDiscoverer.TraceSource.LogWarn("Router failed with {0}-{1}. No more mappings is assumed.", e.ErrorCode, e.ErrorText); break; } throw; } } return mappings.ToArray(); }
public override async Task DeletePortMapAsync(Mapping mapping) { await InternalCreatePortMapAsync(mapping, false) .TimeoutAfter(TimeSpan.FromSeconds(4)); UnregisterMapping(mapping); }
private Task<Mapping> InternalCreatePortMapAsync(Mapping mapping, bool create) { var package = new List<byte>(); package.Add(PmpConstants.Version); package.Add(mapping.Protocol == Protocol.Tcp ? PmpConstants.OperationCodeTcp : PmpConstants.OperationCodeUdp); package.Add(0); //reserved package.Add(0); //reserved package.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short) mapping.PrivatePort))); package.AddRange( BitConverter.GetBytes(create ? IPAddress.HostToNetworkOrder((short) mapping.PublicPort) : (short) 0)); package.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(mapping.Lifetime))); byte[] buffer = package.ToArray(); int attempt = 0; int delay = PmpConstants.RetryDelay; var udpClient = new UdpClient(); CreatePortMapListen(udpClient, mapping); Task task = Task.Factory.FromAsync<byte[], int, IPEndPoint, int>( udpClient.BeginSend, udpClient.EndSend, buffer, buffer.Length, new IPEndPoint(LocalAddress, PmpConstants.ServerPort), 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<byte[], int, IPEndPoint, int>( udpClient.BeginSend, udpClient.EndSend, buffer, buffer.Length, new IPEndPoint(LocalAddress, PmpConstants.ServerPort), null); }).Unwrap(); attempt++; delay *= 2; Thread.Sleep(delay); } return task.ContinueWith(t => { udpClient.Close(); return mapping; }); }
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 { var message = new DeletePortMappingRequestMessage(mapping); await _soapClient .InvokeAsync("DeletePortMapping", message.ToXml()) .TimeoutAfter(TimeSpan.FromSeconds(4)); UnregisterMapping(mapping); } catch (MappingException e) { if(e.ErrorCode != UpnpConstants.NoSuchEntryInArray) throw; } }
public override Task DeletePortMapAsync(Mapping mapping) { return InternalCreatePortMapAsync(mapping, false) .TimeoutAfter(TimeSpan.FromSeconds(4)) .ContinueWith(t => UnregisterMapping(mapping)); }