Files
MyMcKesson/TomcatServer/TCMPortMapper/TCMUPnPPortMapper.cs
2016-07-27 00:32:34 -04:00

955 lines
38 KiB
C#

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<ExistingUPnPPortMapping> existingUPnPPortMappings;
private Dictionary<UInt16, ExistingUPnPPortMapping> existingUPnPPortMappingsUdpDict;
private Dictionary<UInt16, ExistingUPnPPortMapping> 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<PortMapping> mappingsToRemove = PortMapper.SharedInstance.PortMappingsToRemove;
while (mappingsToRemove.Count > 0)
{
PortMapping pm = mappingsToRemove[0];
if (pm.MappingStatus == PortMappingStatus.Mapped)
{
RemovePortMapping(pm);
}
mappingsToRemove.RemoveAt(0);
}
List<PortMapping> 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<ExistingUPnPPortMapping> 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<ExistingUPnPPortMapping> existingMappings = new List<ExistingUPnPPortMapping>();
Dictionary<UInt16, ExistingUPnPPortMapping> existingMappingsUdpDict =
new Dictionary<UInt16, ExistingUPnPPortMapping>();
Dictionary<UInt16, ExistingUPnPPortMapping> existingMappingsTcpDict =
new Dictionary<UInt16, ExistingUPnPPortMapping>();
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<String> descURLs = new List<String>();
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<ExistingUPnPPortMapping> 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<PortMapping> 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<PortMapping> 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
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
}