public void StopBlocking() { // All public API methods are wrapped in a single thread lock. // This frees users to invoke the public API from multiple threads, but provides us a bit of sanity. lock (singleThreadLock) { refreshExternalIPThreadFlags = ThreadFlags.ShouldQuit; updatePortMappingsThreadFlags = ThreadFlags.ShouldQuit; Monitor.Enter(multiThreadLock); NATPMP.natpmp_t natpmp = new NATPMP.natpmp_t(); NATPMP.initnatpmp(ref natpmp); List <PortMapping> mappingsToRemove = PortMapper.SharedInstance.PortMappingsToRemove; lock (mappingsToRemove) { while (mappingsToRemove.Count > 0) { PortMapping pm = mappingsToRemove[0]; if (pm.MappingStatus == PortMappingStatus.Mapped) { RemovePortMapping(pm, ref natpmp); } mappingsToRemove.RemoveAt(0); } } List <PortMapping> mappingsToStop = PortMapper.SharedInstance.PortMappings; lock (mappingsToStop) { for (int i = 0; i < mappingsToStop.Count; i++) { PortMapping pm = mappingsToStop[i]; if (pm.MappingStatus == PortMappingStatus.Mapped) { RemovePortMapping(pm, ref natpmp); } } } Monitor.Exit(multiThreadLock); } }
private bool RemovePortMapping(PortMapping portMapping, ref NATPMP.natpmp_t natpmp) { return ApplyPortMapping(portMapping, true, ref natpmp); }
private bool RefreshPortMapping(PortMapping portMapping, ref NATPMP.natpmp_t natpmp) { return ApplyPortMapping(portMapping, false, ref natpmp); }
private bool ApplyPortMapping(PortMapping portMapping, bool remove, ref NATPMP.natpmp_t natpmp) { NATPMP.natpmpresp_t response = new NATPMP.natpmpresp_t(); int r; Win32.TimeValue timeout = new Win32.TimeValue(); Win32.FileDescriptorSet fds = new Win32.FileDescriptorSet(1); if (!remove) { portMapping.SetMappingStatus(PortMappingStatus.Trying); } PortMappingTransportProtocol protocol = portMapping.TransportProtocol; for (int i = 1; i <= 2; i++) { PortMappingTransportProtocol currentProtocol; if(i == 1) currentProtocol = PortMappingTransportProtocol.UDP; else currentProtocol = PortMappingTransportProtocol.TCP; if (protocol == currentProtocol || protocol == PortMappingTransportProtocol.Both) { r = NATPMP.sendnewportmappingrequest(ref natpmp, (i == 1) ? NATPMP.PROTOCOL_UDP : NATPMP.PROTOCOL_TCP, portMapping.LocalPort, portMapping.DesiredExternalPort, (uint)(remove ? 0 : 3600)); do { fds.Count = 1; fds.Array[0] = (IntPtr)natpmp.s; NATPMP.getnatpmprequesttimeout(ref natpmp, ref timeout); Win32.select(0, ref fds, IntPtr.Zero, IntPtr.Zero, ref timeout); r = NATPMP.readnatpmpresponseorretry(ref natpmp, ref response); } while(r == NATPMP.ERR_TRYAGAIN); if (r < 0) { portMapping.SetMappingStatus(PortMappingStatus.Unmapped); return false; } } } if (remove) { portMapping.SetMappingStatus(PortMappingStatus.Unmapped); } else { updateInterval = Math.Min(updateInterval, response.pnu_newportmapping.lifetime / 2); if (updateInterval < 60) { DebugLog.WriteLine("NAT-PMP: ApplyPortMapping: Caution - new port mapping had a lifetime < 120 ({0})", response.pnu_newportmapping.lifetime); updateInterval = 60; } portMapping.SetExternalPort(response.pnu_newportmapping.mappedpublicport); portMapping.SetMappingStatus(PortMappingStatus.Mapped); } return true; }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #region Update Port Mappings Thread /////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void UpdatePortMappingsThread() { Monitor.Enter(multiThreadLock); OnDidBeginWorking(); NATPMP.natpmp_t natpmp = new NATPMP.natpmp_t(); NATPMP.initnatpmp(ref natpmp); // Remove mappings scheduled for removal List <PortMapping> mappingsToRemove = PortMapper.SharedInstance.PortMappingsToRemove; lock (mappingsToRemove) { while ((mappingsToRemove.Count > 0) && (updatePortMappingsThreadFlags == ThreadFlags.None)) { PortMapping mappingToRemove = mappingsToRemove[0]; if (mappingToRemove.MappingStatus == PortMappingStatus.Mapped) { RemovePortMapping(mappingToRemove, ref natpmp); } mappingsToRemove.RemoveAt(0); } } // If the port mapper is running: // -Refresh existing mappings // -Add new mappings // If the port mapper is stopped: // -Remove any existing mappings List <PortMapping> mappings = PortMapper.SharedInstance.PortMappings; lock (mappings) { for (int i = 0; i < mappings.Count && updatePortMappingsThreadFlags == ThreadFlags.None; i++) { PortMapping existingMapping = mappings[i]; bool isRunning = PortMapper.SharedInstance.IsRunning; if (existingMapping.MappingStatus == PortMappingStatus.Mapped) { if (isRunning) { RefreshPortMapping(existingMapping, ref natpmp); } else { RemovePortMapping(existingMapping, ref natpmp); } } } for (int i = 0; i < mappings.Count && updatePortMappingsThreadFlags == ThreadFlags.None; i++) { PortMapping mappingToAdd = mappings[i]; bool isRunning = PortMapper.SharedInstance.IsRunning; if (mappingToAdd.MappingStatus == PortMappingStatus.Unmapped && isRunning) { AddPortMapping(mappingToAdd, ref natpmp); } } } NATPMP.closenatpmp(ref natpmp); Monitor.Exit(multiThreadLock); if (PortMapper.SharedInstance.IsRunning) { if ((updatePortMappingsThreadFlags & ThreadFlags.ShouldRestart) > 0) { UpdatePortMappings(); } else if ((updatePortMappingsThreadFlags & ThreadFlags.ShouldQuit) > 0) { Refresh(); } else { AdjustUpdateTimer(); } } OnDidEndWorking(); }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #region Refresh External IP Thread /////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void RefreshExternalIPThread() { Monitor.Enter(multiThreadLock); OnDidBeginWorking(); NATPMP.natpmp_t natpmp = new NATPMP.natpmp_t(); NATPMP.natpmpresp_t response = new NATPMP.natpmpresp_t(); int r; Win32.TimeValue timeout = new Win32.TimeValue(); Win32.FileDescriptorSet fds = new Win32.FileDescriptorSet(1); bool didFail = false; r = NATPMP.initnatpmp(ref natpmp); if (r < 0) { didFail = true; } else { r = NATPMP.sendpublicaddressrequest(ref natpmp); if (r < 0) { didFail = true; } else { do { fds.Count = 1; fds.Array[0] = (IntPtr)natpmp.s; NATPMP.getnatpmprequesttimeout(ref natpmp, ref timeout); Win32.select(0, ref fds, IntPtr.Zero, IntPtr.Zero, ref timeout); r = NATPMP.readnatpmpresponseorretry(ref natpmp, ref response); if (refreshExternalIPThreadFlags != ThreadFlags.None) { DebugLog.WriteLine("NAT-PMP: RefreshExternalIPThread quit prematurely (1)"); Monitor.Exit(multiThreadLock); if ((refreshExternalIPThreadFlags & ThreadFlags.ShouldRestart) > 0) { Refresh(); } NATPMP.closenatpmp(ref natpmp); OnDidEndWorking(); return; } }while (r == NATPMP.ERR_TRYAGAIN); if (r < 0) { didFail = true; DebugLog.WriteLine("NAT-PMP: IP refresh did time out"); } else { IPAddress ipaddr = new IPAddress((long)response.pnu_publicaddress.addr); OnDidGetExternalIPAddress(ipaddr); } } } NATPMP.closenatpmp(ref natpmp); Monitor.Exit(multiThreadLock); if (refreshExternalIPThreadFlags != ThreadFlags.None) { DebugLog.WriteLine("NAT-PMP: RefreshExternalIPThread quit prematurely (2)"); if ((refreshExternalIPThreadFlags & ThreadFlags.ShouldRestart) > 0) { Refresh(); } } else { if (didFail) { OnDidFail(); } else { UpdatePortMappings(); } } OnDidEndWorking(); }
private bool ApplyPortMapping(PortMapping portMapping, bool remove, ref NATPMP.natpmp_t natpmp) { NATPMP.natpmpresp_t response = new NATPMP.natpmpresp_t(); int r; Win32.TimeValue timeout = new Win32.TimeValue(); Win32.FileDescriptorSet fds = new Win32.FileDescriptorSet(1); if (!remove) { portMapping.SetMappingStatus(PortMappingStatus.Trying); } PortMappingTransportProtocol protocol = portMapping.TransportProtocol; for (int i = 1; i <= 2; i++) { PortMappingTransportProtocol currentProtocol; if (i == 1) { currentProtocol = PortMappingTransportProtocol.UDP; } else { currentProtocol = PortMappingTransportProtocol.TCP; } if (protocol == currentProtocol || protocol == PortMappingTransportProtocol.Both) { r = NATPMP.sendnewportmappingrequest(ref natpmp, (i == 1) ? NATPMP.PROTOCOL_UDP : NATPMP.PROTOCOL_TCP, portMapping.LocalPort, portMapping.DesiredExternalPort, (uint)(remove ? 0 : 3600)); do { fds.Count = 1; fds.Array[0] = (IntPtr)natpmp.s; NATPMP.getnatpmprequesttimeout(ref natpmp, ref timeout); Win32.select(0, ref fds, IntPtr.Zero, IntPtr.Zero, ref timeout); r = NATPMP.readnatpmpresponseorretry(ref natpmp, ref response); }while(r == NATPMP.ERR_TRYAGAIN); if (r < 0) { portMapping.SetMappingStatus(PortMappingStatus.Unmapped); return(false); } } } if (remove) { portMapping.SetMappingStatus(PortMappingStatus.Unmapped); } else { updateInterval = Math.Min(updateInterval, response.pnu_newportmapping.lifetime / 2); if (updateInterval < 60) { DebugLog.WriteLine("NAT-PMP: ApplyPortMapping: Caution - new port mapping had a lifetime < 120 ({0})", response.pnu_newportmapping.lifetime); updateInterval = 60; } portMapping.SetExternalPort(response.pnu_newportmapping.mappedpublicport); portMapping.SetMappingStatus(PortMappingStatus.Mapped); } return(true); }