using System; using System.Collections.Generic; using System.Net; using System.Threading; using System.Text; using TCMPortMapper.Helpers; namespace TCMPortMapper { public class ExistingUPnPPortMapping { private IPAddress localAddress; private UInt16 localPort; private UInt16 externalPort; private PortMappingTransportProtocol transportProtocol; private String description; public ExistingUPnPPortMapping(IPAddress localAddress, UInt16 localPort, UInt16 externalPort, PortMappingTransportProtocol transportProtocol, String description) { this.localAddress = localAddress; this.localPort = localPort; this.externalPort = externalPort; this.transportProtocol = transportProtocol; this.description = description; } public IPAddress LocalAddress { get { return localAddress; } } public UInt16 LocalPort { get { return localPort; } } public UInt16 ExternalPort { get { return externalPort; } } public PortMappingTransportProtocol TransportProtocol { get { return transportProtocol; } } public String Description { get { return description; } } } class UPnPPortMapper { public delegate void PMDidFail(UPnPPortMapper sender); public delegate void PMDidGetExternalIPAddress(UPnPPortMapper sender, IPAddress ip, bool isFromRouter); public delegate void PMDidBeginWorking(UPnPPortMapper sender); public delegate void PMDidEndWorking(UPnPPortMapper sender); public event PMDidFail DidFail; public event PMDidGetExternalIPAddress DidGetExternalIPAddress; public event PMDidBeginWorking DidBeginWorking; public event PMDidEndWorking DidEndWorking; private Object multiThreadLock = new Object(); private Object singleThreadLock = new Object(); private volatile ThreadID threadID; private volatile ThreadFlags refreshExternalIPThreadFlags; private volatile ThreadFlags updatePortMappingsThreadFlags; private List existingUPnPPortMappings; private Dictionary existingUPnPPortMappingsUdpDict; private Dictionary existingUPnPPortMappingsTcpDict; private MiniUPnP.UPNPUrls urls = new MiniUPnP.UPNPUrls(); private MiniUPnP.IGDdatas igddata = new MiniUPnP.IGDdatas(); private enum ThreadID { None = 0, RefreshExternalIP = 1, UpdatePortMappings = 2 } [Flags] private enum ThreadFlags { None = 0, ShouldQuit = 1, ShouldRestart = 2 } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #region Public API /////////////////////////////////////////////////////////////////////////////////////////////////////////////// public UPnPPortMapper() { // Nothing to do here } public void Refresh() { refreshExternalIPThreadFlags = ThreadFlags.None; updatePortMappingsThreadFlags = ThreadFlags.None; //threadID = ThreadID.RefreshExternalIP; RefreshExternalIPThread(); //if (threadID == ThreadID.RefreshExternalIP) //{ // refreshExternalIPThreadFlags = ThreadFlags.ShouldQuit | ThreadFlags.ShouldRestart; //} //else if (threadID == ThreadID.UpdatePortMappings) //{ // updatePortMappingsThreadFlags = ThreadFlags.ShouldQuit; //} } public void UpdatePortMappings() { updatePortMappingsThreadFlags = ThreadFlags.None; //threadID = ThreadID.UpdatePortMappings; UpdatePortMappingsThread(); //if (threadID == ThreadID.UpdatePortMappings) //{ // updatePortMappingsThreadFlags = ThreadFlags.ShouldQuit | ThreadFlags.ShouldRestart; //} } public void Stop() { // Restart update to remove mappings before stopping UpdatePortMappings(); } public void StopBlocking() { refreshExternalIPThreadFlags = ThreadFlags.ShouldQuit; updatePortMappingsThreadFlags = ThreadFlags.ShouldQuit; DoUpdateExistingUPnPPortMappings(); List mappingsToRemove = PortMapper.SharedInstance.PortMappingsToRemove; while (mappingsToRemove.Count > 0) { PortMapping pm = mappingsToRemove[0]; if (pm.MappingStatus == PortMappingStatus.Mapped) { RemovePortMapping(pm); } mappingsToRemove.RemoveAt(0); } List mappingsToStop = PortMapper.SharedInstance.PortMappings; for (int i = 0; i < mappingsToStop.Count; i++) { PortMapping pm = mappingsToStop[i]; if (pm.MappingStatus == PortMappingStatus.Mapped) { RemovePortMapping(pm); } } } public void UpdateExistingUPnPPortMappings() { // 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) //{ //Thread bgThread = new Thread(new ThreadStart(UpdateExistingUPnPMappingsThread)); //bgThread.Start(); //} UpdateExistingUPnPMappingsThread(); } public List ExistingUPnPPortMappings { get { return existingUPnPPortMappings; } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #region Delegate Methods /////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected virtual void OnDidFail() { if (DidFail != null) { DidFail(this); //PortMapper.SharedInstance.Invoke(DidFail, this); } } protected virtual void OnDidGetExternalIPAddress(IPAddress ip, bool isFromRouter) { if (DidGetExternalIPAddress != null) { DidGetExternalIPAddress(this, ip, isFromRouter); //PortMapper.SharedInstance.Invoke(DidGetExternalIPAddress, this, ip, isFromRouter); } } protected virtual void OnDidBeginWorking() { if (DidBeginWorking != null) { // This is thread safe, so there's no need to Invoke it DidBeginWorking(this); } } protected virtual void OnDidEndWorking() { if (DidEndWorking != null) { // This is thread safe, so there's no need to Invoke it DidEndWorking(this); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #region Private API /////////////////////////////////////////////////////////////////////////////////////////////////////////////// private String GetPortMappingDescription() { String machineName = Environment.MachineName; String userName = Environment.UserName; System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess(); String processName = currentProcess.ProcessName; int processId = currentProcess.Id; return machineName + "/" + userName + "/" + processName + "/" + processId; } private void DoUpdateExistingUPnPPortMappings() { List existingMappings = new List(); Dictionary existingMappingsUdpDict = new Dictionary(); Dictionary existingMappingsTcpDict = new Dictionary(); int r = 0; int i = 0; int j = 0; byte[] index = new byte[6]; byte[] intClient = new byte[16]; byte[] intPort = new byte[6]; byte[] extPort = new byte[6]; byte[] protocol = new byte[4]; byte[] desc = new byte[80]; byte[] enabled = new byte[6]; byte[] rHost = new byte[64]; byte[] duration = new byte[16]; do { // Convert "int i" to a null-terminated char array String iStr = i.ToString(); int maxCount = Math.Min(iStr.Length, 5); System.Text.Encoding.ASCII.GetBytes(iStr, 0, maxCount, index, 0); // Reset all the other null-terminated char arrays intClient[0] = 0; intPort[0] = 0; extPort[0] = 0; protocol[0] = 0; // Warning - not in Cocoa version desc[0] = 0; enabled[0] = 0; rHost[0] = 0; duration[0] = 0; r = MiniUPnP.UPNPCOMMAND_UNKNOWN_ERROR; try { r = MiniUPnP.UPNP_GetGenericPortMappingEntry(urls.controlURL, igddata.ServiceType, index, extPort, intClient, intPort, protocol, desc, enabled, rHost, duration); } catch (AccessViolationException) { // I have no idea why the above method sometimes throws an AccessException. // The odd part about it is that all the data gets marshaled over and back properly. // So the exception can safely be ignored, it just bugs me because it feels like a hack. DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_GetGenericPortMappingEntry"); r = MiniUPnP.UPNPCOMMAND_SUCCESS; } if (r == MiniUPnP.UPNPCOMMAND_SUCCESS) { IPAddress iAddr; IPAddress.TryParse(MiniUPnP.NullTerminatedArrayToString(intClient), out iAddr); UInt16 iPort; UInt16.TryParse(MiniUPnP.NullTerminatedArrayToString(intPort), out iPort); UInt16 ePort; UInt16.TryParse(MiniUPnP.NullTerminatedArrayToString(extPort), out ePort); PortMappingTransportProtocol transportProtocol = 0; String protocolStr = MiniUPnP.NullTerminatedArrayToString(protocol); if (protocolStr.Equals("UDP", StringComparison.OrdinalIgnoreCase)) { transportProtocol |= PortMappingTransportProtocol.UDP; } if (protocolStr.Equals("TCP", StringComparison.OrdinalIgnoreCase)) { transportProtocol |= PortMappingTransportProtocol.TCP; } String description = MiniUPnP.NullTerminatedArrayToString(desc); ExistingUPnPPortMapping existingPM; existingPM = new ExistingUPnPPortMapping(iAddr, iPort, ePort, transportProtocol, description); existingMappings.Add(existingPM); if ((transportProtocol & PortMappingTransportProtocol.UDP) > 0) { existingMappingsUdpDict[ePort] = existingPM; } if ((transportProtocol & PortMappingTransportProtocol.TCP) > 0) { existingMappingsTcpDict[ePort] = existingPM; } DebugLog.WriteLine("Existing UPnP: {0}: {1} {2}->{3}:{4} ({5})", i, protocolStr, ePort, iAddr, iPort, description); } i++; j++; } while ((r == MiniUPnP.UPNPCOMMAND_SUCCESS) && (j < 10)); // Update stored list of existing mappings existingUPnPPortMappings = existingMappings; existingUPnPPortMappingsUdpDict = existingMappingsUdpDict; existingUPnPPortMappingsTcpDict = existingMappingsTcpDict; } private bool AddPortMapping(PortMapping portMapping) { portMapping.SetMappingStatus(PortMappingStatus.Trying); String intPortStr = portMapping.LocalPort.ToString(); String intClient = PortMapper.SharedInstance.LocalIPAddress.ToString(); //String description = GetPortMappingDescription(); String description = portMapping.Description; bool done = false; int attemptCount = 0; do { int udpErrCode = 0; int tcpErrCode = 0; bool udpResult = true; bool tcpResult = true; UInt16 extPort; if (portMapping.DesiredExternalPort < (65535 - 40)) extPort = (UInt16)(portMapping.DesiredExternalPort + attemptCount); else extPort = (UInt16)(portMapping.DesiredExternalPort - attemptCount); String extPortStr = extPort.ToString(); if ((portMapping.TransportProtocol & PortMappingTransportProtocol.UDP) > 0) { ExistingUPnPPortMapping existingPM; if (existingUPnPPortMappingsUdpDict.TryGetValue(extPort, out existingPM)) { udpErrCode = 718; DebugLog.WriteLine("UPnP: AddPortMapping: UDP: mapping already exists"); } else { udpErrCode = MiniUPnP.UPNP_AddPortMapping(urls.controlURL, igddata.ServiceType, extPortStr, intPortStr, intClient, description, "UDP"); DebugLog.WriteLine("UPnP: AddPortMapping: UDP: result = {0}", udpErrCode); } udpResult = (udpErrCode == MiniUPnP.UPNPCOMMAND_SUCCESS); } if ((portMapping.TransportProtocol & PortMappingTransportProtocol.TCP) > 0) { ExistingUPnPPortMapping existingPM; if (existingUPnPPortMappingsTcpDict.TryGetValue(extPort, out existingPM)) { tcpErrCode = 718; DebugLog.WriteLine("UPnP: AddPortMapping: TCP: mapping already exists"); } else { tcpErrCode = MiniUPnP.UPNP_AddPortMapping(urls.controlURL, igddata.ServiceType, extPortStr, intPortStr, intClient, description, "TCP"); DebugLog.WriteLine("UPnP: AddPortMapping: TCP: result = {0}", tcpErrCode); } tcpResult = (tcpErrCode == MiniUPnP.UPNPCOMMAND_SUCCESS); } if (udpResult && !tcpResult) { DebugLog.WriteLine("Deleting UDP mapping"); try { MiniUPnP.UPNP_DeletePortMapping(urls.controlURL, igddata.ServiceType, extPortStr, "UDP"); } catch (AccessViolationException) { // I have no idea why the above method sometimes throws an AccessException. // The odd part about it is that it works perfect, except for the stupid exception. // So the exception can safely be ignored, it just bugs me because it feels like a hack. DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_DeletePortMapping"); } } if (tcpResult && !udpResult) { DebugLog.WriteLine("Deleting TCP mapping"); try { MiniUPnP.UPNP_DeletePortMapping(urls.controlURL, igddata.ServiceType, extPortStr, "TCP"); } catch (AccessViolationException) { // I have no idea why the above method sometimes throws an AccessException. // The odd part about it is that it works perfect, except for the stupid exception. // So the exception can safely be ignored, it just bugs me because it feels like a hack. DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_DeletePortMapping"); } } if (udpResult && tcpResult) { // All attempted port mappings were successful portMapping.SetExternalPort(extPort); portMapping.SetMappingStatus(PortMappingStatus.Mapped); return true; } attemptCount++; //if (attemptCount >= 10) if (attemptCount >= 100) { // We've tried 100 different mappings and still no success done = true; } else if (!udpResult && udpErrCode != 718) { // We received non-conflict error done = true; } else if (!tcpResult && tcpErrCode != 718) { // We received non-conflict error done = true; } } while (!done); portMapping.SetMappingStatus(PortMappingStatus.Unmapped); return false; } private bool RemovePortMapping(PortMapping portMapping) { // Make sure the mapping still belongs to us IPAddress ourIP = PortMapper.SharedInstance.LocalIPAddress; String ourDescription = GetPortMappingDescription(); bool udpMappingStolen = false; bool tcpMappingStolen = false; if ((portMapping.TransportProtocol & PortMappingTransportProtocol.UDP) > 0) { ExistingUPnPPortMapping existingPM; if (existingUPnPPortMappingsUdpDict.TryGetValue(portMapping.ExternalPort, out existingPM)) { if (!existingPM.LocalAddress.Equals(ourIP) || !existingPM.Description.Equals(ourDescription)) { // The mapping was stolen by another machine or process // Do not remove it, but for our purposes we can consider it removed DebugLog.WriteLine("UPnP: RemovePortMapping: UDP mapping stolen"); udpMappingStolen = true; } } } if ((portMapping.TransportProtocol & PortMappingTransportProtocol.TCP) > 0) { ExistingUPnPPortMapping existingPM; if (existingUPnPPortMappingsTcpDict.TryGetValue(portMapping.ExternalPort, out existingPM)) { if (!existingPM.LocalAddress.Equals(ourIP) || !existingPM.Description.Equals(ourDescription)) { // The mapping was stolen by another machine or process // Do not remove it, but for our purposes we can consider it removed DebugLog.WriteLine("UPnP: RemovePortMapping: TCM mapping stolen"); tcpMappingStolen = true; } } } int result = MiniUPnP.UPNPCOMMAND_SUCCESS; bool udpResult = true; bool tcpResult = true; String extPortStr = portMapping.ExternalPort.ToString(); if ((portMapping.TransportProtocol & PortMappingTransportProtocol.UDP) > 0 && !udpMappingStolen) { try { result = MiniUPnP.UPNP_DeletePortMapping(urls.controlURL, igddata.ServiceType, extPortStr, "UDP"); } catch (AccessViolationException) { // I have no idea why the above method sometimes throws an AccessException. // The odd part about it is that it works perfect, except for the stupid exception. // So the exception can safely be ignored, it just bugs me because it feels like a hack. DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_DeletePortMapping"); } DebugLog.WriteLine("UPnP: RemovePortMapping: UDP: result = {0}", result); udpResult = (result == MiniUPnP.UPNPCOMMAND_SUCCESS); } if ((portMapping.TransportProtocol & PortMappingTransportProtocol.TCP) > 0 && !tcpMappingStolen) { try { result = MiniUPnP.UPNP_DeletePortMapping(urls.controlURL, igddata.ServiceType, extPortStr, "TCP"); } catch (AccessViolationException) { // I have no idea why the above method sometimes throws an AccessException. // The odd part about it is that it works perfect, except for the stupid exception. // So the exception can safely be ignored, it just bugs me because it feels like a hack. DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_DeletePortMapping"); } DebugLog.WriteLine("UPnP: RemovePortMapping: TCP: result = {0}", result); tcpResult = (result == MiniUPnP.UPNPCOMMAND_SUCCESS); } portMapping.SetMappingStatus(PortMappingStatus.Unmapped); return (udpResult && tcpResult); } private bool RemovePortMapping(ExistingUPnPPortMapping portMapping) { int result = MiniUPnP.UPNPCOMMAND_SUCCESS; bool udpResult = true; bool tcpResult = true; String extPortStr = portMapping.ExternalPort.ToString(); if ((portMapping.TransportProtocol & PortMappingTransportProtocol.UDP) > 0) { try { result = MiniUPnP.UPNP_DeletePortMapping(urls.controlURL, igddata.ServiceType, extPortStr, "UDP"); } catch (AccessViolationException) { // I have no idea why the above method sometimes throws an AccessException. // The odd part about it is that it works perfect, except for the stupid exception. // So the exception can safely be ignored, it just bugs me because it feels like a hack. DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_DeletePortMapping"); } DebugLog.WriteLine("UPnP: RemovePortMapping: UDP: result = {0}", result); udpResult = (result == MiniUPnP.UPNPCOMMAND_SUCCESS); } if ((portMapping.TransportProtocol & PortMappingTransportProtocol.TCP) > 0) { try { result = MiniUPnP.UPNP_DeletePortMapping(urls.controlURL, igddata.ServiceType, extPortStr, "TCP"); } catch (AccessViolationException) { // I have no idea why the above method sometimes throws an AccessException. // The odd part about it is that it works perfect, except for the stupid exception. // So the exception can safely be ignored, it just bugs me because it feels like a hack. DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_DeletePortMapping"); } DebugLog.WriteLine("UPnP: RemovePortMapping: TCP: result = {0}", result); tcpResult = (result == MiniUPnP.UPNPCOMMAND_SUCCESS); } return (udpResult && tcpResult); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #region Refresh External IP Thread /////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void RefreshExternalIPThread() { OnDidBeginWorking(); IPAddress externalIP; IntPtr devlistP = IntPtr.Zero; byte[] lanAddr = new byte[16]; byte[] externalAddr = new byte[16]; bool didFail = false; devlistP = MiniUPnP.upnpDiscover(2500, IntPtr.Zero, IntPtr.Zero); if (devlistP == IntPtr.Zero) { //First do a last ditch effort check for an external IP //in case the server is not connected to a router, but //directly to a modem or is external facing already var ipAddress = Utility.GetExternalIP(10000, true); if (String.IsNullOrEmpty(ipAddress)) { DebugLog.WriteLine("UPnP: No IDG Device found on the network (1)"); didFail = true; } else { IPAddress.TryParse(ipAddress, out externalIP); if (externalIP != null) { OnDidGetExternalIPAddress(externalIP, false); didFail = false; } } } else { MiniUPnP.UPNPDev devlist = MiniUPnP.PtrToUPNPDev(devlistP); // Check all of the devices for reachability bool foundIDGDevice = false; MiniUPnP.UPNPDev device = devlist; IPAddress routerIP = PortMapper.SharedInstance.RouterIPAddress; List descURLs = new List(); bool done = false; while (!done) { try { Uri uri = new Uri(device.descURL); if (routerIP != null) { if (uri.Host == routerIP.ToString()) { descURLs.Insert(0, device.descURL); } else { descURLs.Add(device.descURL); } } else { descURLs.Add(device.descURL); } } catch (Exception e) { DebugLog.WriteLine("UPnP: Error while inspecting url: {0}", device.descURL); DebugLog.WriteLine("UPnP: Exception: {0}", e); } if (device.pNext == IntPtr.Zero) done = true; else device = device.Next; } for (int i = 0; i < descURLs.Count && !foundIDGDevice; i++) { String url = descURLs[i]; DebugLog.WriteLine("UPnP: Trying URL: {0}", url); // Reset service type. // This will help us determine if the exception below can safely be ignored. igddata.ServiceType = null; int r = 0; try { // r = MiniUPnP.UPNP_GetIGDFromUrl(url, ref urls, ref igddata, lanAddr, lanAddr.Length); MiniUPnP.UPNPUrls_2 urls_2 = new MiniUPnP.UPNPUrls_2(); unsafe { r = MiniUPnP.UPNP_GetIGDFromUrl(url, &urls_2, ref igddata, lanAddr, lanAddr.Length); MiniUPnP.FreeUPNPUrls(&urls_2); } // Find urls GetUPNPUrls(url); } catch (AccessViolationException) { // I have no idea why the above method sometimes throws an AccessException. // The odd part about it is that all the data gets marshaled over and back properly. // So the exception can safely be ignored, it just bugs me because it feels like a hack. DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_GetIGDFromUrl"); if (igddata.ServiceType != null) { r = 1; } } if (r == 1) { r = MiniUPnP.UPNPCOMMAND_UNKNOWN_ERROR; try { r = MiniUPnP.UPNP_GetExternalIPAddress(urls.controlURL, igddata.ServiceType, externalAddr); } catch (AccessViolationException) { // I have no idea why the above method sometimes throws an AccessException. // The odd part about it is that all the data gets marshaled over and back properly. // So the exception can safely be ignored, it just bugs me because it feels like a hack. DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_GetExternalIPAddress"); if (externalAddr[0] != 0) { r = MiniUPnP.UPNPCOMMAND_SUCCESS; } } if (r != MiniUPnP.UPNPCOMMAND_SUCCESS) { DebugLog.WriteLine("UPnP: GetExternalIPAddress returned {0}", r); } else { IPAddress.TryParse(MiniUPnP.NullTerminatedArrayToString(externalAddr), out externalIP); if (externalIP != null) { OnDidGetExternalIPAddress(externalIP, true); foundIDGDevice = true; didFail = false; } } } } if (!foundIDGDevice) { DebugLog.WriteLine("UPnP: No IDG Device found on the network (2)"); didFail = true; } try { MiniUPnP.freeUPNPDevlist(devlistP); } catch (AccessViolationException) { // I have no idea why the above method sometimes throws an AccessException. // The odd part about it is that all the data gets marshaled over and back properly. // So the exception can safely be ignored, it just bugs me because it feels like a hack. DebugLog.WriteLine("Ignoring exception from method MiniUPnP.freeUPNPDevlist"); } } if (refreshExternalIPThreadFlags != ThreadFlags.None) { if ((refreshExternalIPThreadFlags & ThreadFlags.ShouldRestart) > 0) { Refresh(); } } else { if (didFail) { OnDidFail(); } else { UpdatePortMappings(); } } OnDidEndWorking(); } private void GetUPNPUrls(String url) { if (String.IsNullOrEmpty(igddata.urlbase)) urls.ipcondescURL = url; else urls.ipcondescURL = igddata.urlbase; int index_fin_url = urls.ipcondescURL.IndexOf('/', 7); // 7 = http:// if (index_fin_url >= 0) { urls.ipcondescURL = urls.ipcondescURL.Substring(0, index_fin_url); } urls.controlURL = urls.ipcondescURL + igddata.controlurl; urls.controlURL_CIF = urls.ipcondescURL + igddata.controlurl_CIF; urls.ipcondescURL += igddata.scpdurl; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #region Update Port Mappings Thread /////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void UpdatePortMappingsThread() { OnDidBeginWorking(); // Remove existing mappings scheduled for removal. // These are mappings that weren't created by us, but have been explicity set for removal. List existingMappingsToRemove; existingMappingsToRemove = PortMapper.SharedInstance.ExistingUPnPPortMappingsToRemove; while ((existingMappingsToRemove.Count > 0) && (updatePortMappingsThreadFlags == ThreadFlags.None)) { ExistingUPnPPortMapping existingMappingToRemove = existingMappingsToRemove[0]; RemovePortMapping(existingMappingToRemove); existingMappingsToRemove.RemoveAt(0); } // We need to safeguard mappings that others might have made. // UPnP is quite generous in giving us what we want, // even if other mappings are there, especially from the same local machine. DoUpdateExistingUPnPPortMappings(); // Remove mappings scheduled for removal List mappingsToRemove = PortMapper.SharedInstance.PortMappingsToRemove; while ((mappingsToRemove.Count > 0) && (updatePortMappingsThreadFlags == ThreadFlags.None)) { PortMapping mappingToRemove = mappingsToRemove[0]; if (mappingToRemove.MappingStatus == PortMappingStatus.Mapped) { RemovePortMapping(mappingToRemove); } mappingsToRemove.RemoveAt(0); } // If the port mapper is running: // -Add new mappings // If the port mapper is stopped: // -Remove any existing mappings List mappings = PortMapper.SharedInstance.PortMappings; for (int i = 0; i < mappings.Count && updatePortMappingsThreadFlags == ThreadFlags.None; i++) { PortMapping currentMapping = mappings[i]; bool isRunning = PortMapper.SharedInstance.IsRunning; if (currentMapping.MappingStatus == PortMappingStatus.Unmapped && isRunning) { AddPortMapping(currentMapping); } else if (currentMapping.MappingStatus == PortMappingStatus.Mapped && !isRunning) { RemovePortMapping(currentMapping); } } if (PortMapper.SharedInstance.IsRunning) { if ((updatePortMappingsThreadFlags & ThreadFlags.ShouldRestart) > 0) { UpdatePortMappings(); } else if ((updatePortMappingsThreadFlags & ThreadFlags.ShouldQuit) > 0) { Refresh(); } } OnDidEndWorking(); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #region Update Existing UPnP Port Mappings Thread /////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void UpdateExistingUPnPMappingsThread() { OnDidBeginWorking(); DoUpdateExistingUPnPPortMappings(); OnDidEndWorking(); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////// } }