Initial Commit

This commit is contained in:
2016-07-27 00:32:34 -04:00
commit 8d162b2035
701 changed files with 188672 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
using System;
using System.Diagnostics;
using System.IO;
namespace TCMPortMapper
{
public abstract class OutputLog
{
protected const String F = "yyyy-MM-dd HH:mm:ss:fff";
protected static String Timestamp()
{
return DateTime.Now.ToString(F);
}
}
public class DebugLog : OutputLog
{
[Conditional("DEBUG")]
public static void Write(String format, params Object[] args)
{
String partMessage = String.Format(format, args);
String fullMessage = String.Format("{0}: {1}", Timestamp(), partMessage);
Debug.Write(fullMessage);
}
[Conditional("DEBUG")]
public static void WriteIf(bool flag, String format, params Object[] args)
{
if (flag)
{
String partMessage = String.Format(format, args);
String fullMessage = String.Format("{0}: {1}", Timestamp(), partMessage);
Debug.Write(fullMessage);
}
}
[Conditional("DEBUG")]
public static void WriteLine(String format, params Object[] args)
{
String partMessage = String.Format(format, args);
String fullMessage = String.Format("{0}: {1}", Timestamp(), partMessage);
Debug.WriteLine(fullMessage);
}
[Conditional("DEBUG")]
public static void WriteLineIf(bool flag, String format, params Object[] args)
{
if (flag)
{
String partMessage = String.Format(format, args);
String fullMessage = String.Format("{0}: {1}", Timestamp(), partMessage);
Debug.WriteLine(fullMessage);
}
}
}
public class ReleaseLog : OutputLog
{
public static void WriteError(String format, params Object[] args)
{
String partMessage = String.Format(format, args);
String fullMessage = String.Format("{0}: {1}", Timestamp(), partMessage);
EventLog.WriteEntry("TCMPortMapper", fullMessage, EventLogEntryType.Error);
}
public static void WriteInformation(String format, params Object[] args)
{
String partMessage = String.Format(format, args);
String fullMessage = String.Format("{0}: {1}", Timestamp(), partMessage);
EventLog.WriteEntry("TCMPortMapper", fullMessage, EventLogEntryType.Information);
}
public static void WriteWarning(String format, params Object[] args)
{
String partMessage = String.Format(format, args);
String fullMessage = String.Format("{0}: {1}", Timestamp(), partMessage);
EventLog.WriteEntry("TCMPortMapper", fullMessage, EventLogEntryType.Warning);
}
}
}

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using Yaulw.Registry;
namespace TCMPortMapper.Helpers
{
public static class Utility
{
/// <summary>
/// This function uses a Web request to find the external IP Address your machine is using
/// </summary>
/// <param name="pTimeOutMiliSeconds">Number of miliseconds to wait for a response</param>
/// <param name="forceMultipleChecks">Tells if we should attempt to get the external ip in multiple tries</param>
/// <returns>External facing IP address</returns>
public static string GetExternalIP(int pTimeOutMiliSeconds, bool forceMultipleChecks)
{
var externalIP = Yaulw.Net.IPHostHelper.GetExternalIP(forceMultipleChecks);
if (externalIP == null || externalIP == IPAddress.None)
return string.Empty;
return externalIP.ToString();
}
/// <summary>
/// This function uses a Web request to find the external IP Address your machine is using
/// </summary>
/// <param name="pTimeOutMiliSeconds">Number of miliseconds to wait for a response</param>
/// <returns>External facing IP address</returns>
public static string GetExternalIP(int pTimeOutMiliSeconds)
{
string whatIsMyIp = "http://automation.whatismyip.com/n09230945.asp";
WebClient wc = new WebClient();
UTF8Encoding utf8 = new UTF8Encoding();
try
{
string ipaddr = null;
bool done = false;
wc.DownloadDataCompleted += new
DownloadDataCompletedEventHandler((object sender,
DownloadDataCompletedEventArgs e) =>
{
ipaddr = utf8.GetString(e.Result);
done = true;
});
wc.DownloadDataAsync(new Uri(whatIsMyIp));
System.DateTime startTime = System.DateTime.Now;
while (!done)
{
System.TimeSpan sp = System.DateTime.Now - startTime;
// We should get a response in less than timeout.
// If not, we cancel all and return the internal IP Address
if (sp.TotalMilliseconds > pTimeOutMiliSeconds)
{
done = true;
wc.CancelAsync();
}
}
return ipaddr;
}
catch
{
return null;
}
finally
{
if (wc != null)
{
wc.Dispose();
wc = null;
}
}
}
}
}

View File

@@ -0,0 +1,321 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Collections.Generic;
namespace TCMPortMapper
{
class MiniUPnP
{
public const int UPNPCOMMAND_SUCCESS = 0;
public const int UPNPCOMMAND_UNKNOWN_ERROR = -1;
public const int UPNPCOMMAND_INVALID_ARGS = -2;
public const int MINIUPNPC_URL_MAXSIZE = 128;
[StructLayout(LayoutKind.Sequential)]
public unsafe struct UPNPDev
{
public IntPtr pNext;
public String descURL;
public String st;
public fixed byte buffer[2];
public UPNPDev Next
{
get { return (UPNPDev)Marshal.PtrToStructure(pNext, typeof(UPNPDev)); }
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct UPNPUrls
{
public String controlURL;
public String ipcondescURL;
public String controlURL_CIF;
}
/// <summary>
/// This structure is used to avoid a SystemAccessViolation,
/// and to prevent corruption of the process' memory.
/// See issue #2 for further discussion.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public unsafe struct UPNPUrls_2
{
public char *controlURL;
public char *ipcondescURL;
public char *controlURL_CIF;
}
[StructLayout(LayoutKind.Sequential, Size = 1796, CharSet = CharSet.Ansi)]
public struct IGDdatas
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
private String cureltname;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
public String urlbase;
private Int32 level;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
public String controlurl_CIF;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
private String eventsuburl_CIF;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
private String scpdurl_CIF;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
private String servicetype_CIF;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
public String controlurl;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
private String eventsuburl;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
public String scpdurl;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
private String serviceType;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
private String controlurl_tmp;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
private String eventsuburl_tmp;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
private String scpdurl_tmp;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MINIUPNPC_URL_MAXSIZE)]
private String servicetype_tmp;
public String ServiceType
{
get { return serviceType; }
set { serviceType = value; }
}
}
/// <summary>
/// Discover UPnP devices on the network.
/// The discovered devices are returned as a chained list.
/// It is up to the caller to free the list with freeUPNPDevlist().
/// If available, device list will be obtained from MiniSSDPd.
/// </summary>
/// <param name="delay">
/// Delay (in milliseconds) is the maximum time for waiting any device response.
/// </param>
/// <param name="multicastif">
/// If NULL, default multicast interface for sending SSDP discover packets will be used.
/// </param>
/// <param name="minissdpdsock">
/// If null, default path for minissdpd socket will be used.
/// </param>
/// <returns>
/// A pointer to a UPNPDev structure. Free this when done.
/// Use the PtrToUPNPDev method to convert to a UPNPDev structure.
/// </returns>
[DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr upnpDiscover([In] int delay,
[In] String multicastif,
[In] String minissdpdsock);
[DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr upnpDiscover([In] int delay,
[In] IntPtr multicastif,
[In] String minissdpdsock);
[DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr upnpDiscover([In] int delay,
[In] String multicastif,
[In] IntPtr minissdpdsock);
[DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr upnpDiscover([In] int delay,
[In] IntPtr multicastif,
[In] IntPtr minissdpdsock);
/// <summary>
/// frees list returned by upnpDiscover()
/// </summary>
/// <param name="devlistP">
/// IntPtr (pointer to UPNPDev structure) as returned by upnpDiscover().
/// </param>
[DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void freeUPNPDevlist([In] IntPtr devlistP);
/// <summary>
/// Used when skipping the discovery process.
/// Return value:
/// 0 - Not OK
/// 1 - OK
/// </summary>
/// <param name="rootDescUrl"></param>
/// <param name="urls"></param>
/// <param name="data"></param>
/// <param name="lanAddr"></param>
/// <param name="lanAddrLength"></param>
/// <returns></returns>
[DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern int UPNP_GetIGDFromUrl([In] String rootDescUrl,
[In] UPNPUrls_2 *urls,
[In, Out] ref IGDdatas datas,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] byte[] lanAddr,
[In] int lanAddrLength);
[DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern void FreeUPNPUrls([In] UPNPUrls_2 *urls);
/// <summary>
/// Extracts the external IP address.
/// </summary>
/// <param name="controlURL"></param>
/// <param name="serviceType"></param>
/// <param name="externalIPAddr">
/// The array to copy the external IP address bytes into.
/// The array must be 16 bytes in length.
/// </param>
/// <returns>
/// 0: SUCCESS
/// NON ZERO: ERROR Either a UPnP error code or an unknown error.
///
/// Possible UPnP Errors:
/// 402 Invalid Args - See UPnP Device Architecture section on Control.
/// 501 Action Failed - See UPnP Device Architecture section on Control.
/// </returns>
[DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int UPNP_GetExternalIPAddress([In] String controlURL,
[In] String serviceType,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeConst = 16)] byte[] externalIPAddr);
[DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int UPNP_GetExternalIPAddress([In] String controlURL,
[In, MarshalAs(UnmanagedType.LPArray, SizeConst = MINIUPNPC_URL_MAXSIZE)] byte[] serviceType,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeConst = 16)] byte[] externalIPAddr);
/// <summary>
/// Description forthcoming...
/// </summary>
/// <param name="controlURL"></param>
/// <param name="serviceType"></param>
/// <param name="index"></param>
/// <param name="extPort"></param>
/// <param name="intClient"></param>
/// <param name="intPort"></param>
/// <param name="protocol"></param>
/// <param name="desc"></param>
/// <param name="enabled"></param>
/// <param name="rHost"></param>
/// <param name="duration"></param>
/// <returns>
/// UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR or a UPnP Error Code.
///
/// Possible UPNP Error codes:
/// 402 Invalid Args - See UPnP Device Architecture section on Control
/// 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
/// </returns>
[DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int UPNP_GetGenericPortMappingEntry([In] String controlURL,
[In] String serviceType,
[In] [MarshalAs(UnmanagedType.LPArray, SizeConst = 6)] byte[] index,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 6)] byte[] extPort,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 16)] byte[] intClient,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 6)] byte[] intPort,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 4)] byte[] protocol,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 80)] byte[] desc,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 6)] byte[] enabled,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 64)] byte[] rHost,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 16)] byte[] duration);
/// <summary>
/// Adds a UPnP port mapping using the given information.
/// </summary>
/// <param name="controlURL"></param>
/// <param name="serviceType"></param>
/// <param name="externalPort"></param>
/// <param name="internalPort"></param>
/// <param name="internalClient"></param>
/// <param name="description"></param>
/// <param name="protocol"></param>
/// <returns>
/// Returns values:
/// Zero: SUCCESS
/// Non-Zero: ERROR. Either a UPnP error code or an unknown error.
///
/// List of possible UPnP errors:
/// 402 - Invalid Args
/// 501 - Action Failed
/// 715 - WildCardNotPermittedInSrcIP
/// 716 - WildCardNotPermittedInExtPort
/// 718 - ConflictInMappingEntry
/// The port mapping entry specified conflicts with a mapping assigned previously to another client
/// 724 - SamePortValueRequired
/// Internal and External port values must be the same
/// 725 - OnlyPermanentLeasesSupported
/// The NAT implementation only supports permanent lease times on port mappings
/// 726 - RemoteHostOnlySupportsWildcard
/// RemoteHost must be a wildcard and cannot be a specific IP address or DNS name
/// 727 - ExternalPortOnlySupportsWildcard
/// ExternalPort must be a wildcard and cannot be a specific port value
/// </returns>
[DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int UPNP_AddPortMapping([In] String controlURL,
[In] String serviceType,
[In] String externalPort,
[In] String internalPort,
[In] String internalClient,
[In] String description,
[In] String protocol);
/// <summary>
/// Deletes a UPnP port mapping with the given information.
/// </summary>
/// <param name="controlURL"></param>
/// <param name="serviceType"></param>
/// <param name="externalPort"></param>
/// <param name="protocol"></param>
/// <returns>
/// Return values:
/// Zero: SUCCESS
/// Non-Zero: ERROR. Either a UPnP error code or an undefined error.
///
/// List of possible UPnP errors:
/// 402 - Invalid Args
/// 714 - NoSuchEntryInArray (Port Mapping doesn't exist)
/// </returns>
[DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int UPNP_DeletePortMapping([In] String controlURL,
[In] String serviceType,
[In] String externalPort,
[In] String protocol);
#region Utility Methods
public static UPNPDev PtrToUPNPDev(IntPtr devlistP)
{
return (UPNPDev)Marshal.PtrToStructure(devlistP, typeof(UPNPDev));
}
public static String NullTerminatedArrayToString(byte[] nullTerminatedStr)
{
for (int i = 0; i < nullTerminatedStr.Length; i++)
{
if (nullTerminatedStr[i] == 0)
{
return System.Text.Encoding.ASCII.GetString(nullTerminatedStr, 0, i);
}
}
return System.Text.Encoding.ASCII.GetString(nullTerminatedStr, 0, nullTerminatedStr.Length);
}
#endregion
}
}

View File

@@ -0,0 +1,111 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace TCMPortMapper
{
class NATPMP
{
public const int RESPTYPE_PUBLICADDRESS = 0;
public const int RESPTYPE_UDPPORTMAPPING = 1;
public const int RESPTYPE_TCPPORTMAPPING = 2;
public const int PROTOCOL_UDP = 1;
public const int PROTOCOL_TCP = 2;
public const int ERR_INVALIDARGS = -1;
public const int ERR_SOCKETERROR = -2;
public const int ERR_CANNOTGETGATEWAY = -3;
public const int ERR_CLOSEERR = -4;
public const int ERR_RECVFROM = -5;
public const int ERR_NOPENDINGREQ = -6;
public const int ERR_NOGATEWAYSUPPORT = -7;
public const int ERR_CONNECTERR = -8;
public const int ERR_WRONGPACKETSOURCE = -9;
public const int ERR_SENDERR = -10;
public const int ERR_FCNTLERROR = -11;
public const int ERR_GETTIMEOFDAYERR = -12;
public const int ERR_UNSUPPORTEDVERSION = -14;
public const int ERR_UNSUPPORTEDOPCODE = -15;
public const int ERR_UNDEFINEDERROR = -49;
public const int ERR_NOTAUTHORIZED = -51;
public const int ERR_NETWORKFAILURE = -52;
public const int ERR_OUTOFRESOURCES = -53;
public const int ERR_TRYAGAIN = -100;
[StructLayout(LayoutKind.Sequential)]
public unsafe struct natpmp_t
{
public Int32 s;
public UInt32 gateway;
public Int32 has_pending_request;
public fixed byte pending_request[12];
public Int32 pending_request_len;
public Int32 try_number;
public Win32.TimeValue retry_time;
}
[StructLayout(LayoutKind.Explicit)]
public struct natpmpresp_t
{
[FieldOffset(0)]
public UInt16 type;
[FieldOffset(2)]
public UInt16 resultcode;
[FieldOffset(4)]
public UInt32 epoch;
[StructLayout(LayoutKind.Sequential)]
public struct publicaddress
{
public UInt32 addr;
}
[FieldOffset(8)]
public publicaddress pnu_publicaddress;
[StructLayout(LayoutKind.Sequential)]
public struct newportmapping
{
public UInt16 privateport;
public UInt16 mappedpublicport;
public UInt32 lifetime;
}
[FieldOffset(8)]
public newportmapping pnu_newportmapping;
}
// [DllImport("natpmp.dll")]
// public static extern int getdefaultgateway([In, Out] ref UInt32 addr);
[DllImport("natpmp.dll")]
public static extern int initnatpmp([In, Out] ref natpmp_t p);
[DllImport("natpmp.dll")]
public static extern int closenatpmp([In, Out] ref natpmp_t p);
[DllImport("natpmp.dll")]
public static extern int sendpublicaddressrequest([In, Out] ref natpmp_t p);
[DllImport("natpmp.dll")]
public static extern int sendnewportmappingrequest([In, Out] ref natpmp_t p,
[In] int protocol,
[In] UInt16 privateport,
[In] UInt16 publicport,
[In] UInt32 lifetime);
[DllImport("natpmp.dll")]
public static extern int getnatpmprequesttimeout([In, Out] ref natpmp_t p,
[In, Out] ref Win32.TimeValue timeout);
[DllImport("natpmp.dll")]
public static extern int readnatpmpresponseorretry([In, Out] ref natpmp_t p,
[In, Out] ref natpmpresp_t response);
[DllImport("natpmp.dll")]
public static extern String strnatpmperr([In] int t);
}
}

View File

@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("TCMPortMapper")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TCMPortMapper")]
[assembly: AssemblyCopyright("Copyright © 2008")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("82c6e020-0de5-4af7-8c12-72d0f169ebf6")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.4.1")]
[assembly: AssemblyFileVersion("1.0.4.1")]

View File

@@ -0,0 +1,569 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
namespace TCMPortMapper
{
class NATPMPPortMapper
{
public delegate void PMDidFail(NATPMPPortMapper sender);
public delegate void PMDidGetExternalIPAddress(NATPMPPortMapper sender, IPAddress ip);
public delegate void PMDidBeginWorking(NATPMPPortMapper sender);
public delegate void PMDidEndWorking(NATPMPPortMapper sender);
public delegate void PMDidReceiveBroadcastExternalIPChange(NATPMPPortMapper sender, IPAddress ip, IPAddress senderIP);
public event PMDidFail DidFail;
public event PMDidGetExternalIPAddress DidGetExternalIPAddress;
public event PMDidBeginWorking DidBeginWorking;
public event PMDidEndWorking DidEndWorking;
public event PMDidReceiveBroadcastExternalIPChange DidReceiveBroadcastExternalIPChange;
private Object multiThreadLock = new Object();
private Object singleThreadLock = new Object();
private volatile ThreadID threadID;
private volatile ThreadFlags refreshExternalIPThreadFlags;
private volatile ThreadFlags updatePortMappingsThreadFlags;
private Timer updateTimer;
private uint updateInterval;
private UdpClient udpClient;
private IPAddress lastBroadcastExternalIP;
private enum ThreadID
{
None = 0,
RefreshExternalIP = 1,
UpdatePortMappings = 2
}
[Flags]
private enum ThreadFlags
{
None = 0,
ShouldQuit = 1,
ShouldRestart = 2
}
// Standard routine:
//
// Refresh -> triggers RefreshExternalIPThread -> Upon completion of thread, UpdatePortMappings is called.
// Case 1: No threads are running -> perfect, trigger thread as planned
// Case 2: RefreshExternalIPThread is running -> this thread is aborted, and then Refresh is called again
// Case 3: UpdatePortMappingsThread is running -> this thread is aborted, and then Refresh is called again
//
// UpdatePortMappings -> triggers UpdatePortMappingsThread -> Upon completion of thread, AdjustUpdateTimer is called
// Case 1: No threads are running -> perfect, trigger thread as planned
// Case 2: RefreshExternalIPInThread is running -> That's fine, we do nothing
// Case 3: UpdatePortMappingsInThread is running -> this thread is aborted, and restarted from beginning
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#region Public API
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
public NATPMPPortMapper()
{
// Until I find a way around this bug, there's no reason to setup the udp client...
// Add UDP listener for public ip update packets
// udpClient = new UdpClient(5351);
// Note: The following code throws an exception for some reason.
// The JoinMulticastGroup works fine for every multicast address except 224.0.0.1
// Another reason why windows sucks.
// udpClient.JoinMulticastGroup(IPAddress.Parse("224.0.0.1"));
// So basically, the udpClient won't be receiving anything.
// I consider this to be a bug in Windows and/or .Net.
// udpClient.BeginReceive(new AsyncCallback(udpClient_DidReceive), null);
}
public void Refresh()
{
updateInterval = 3600 / 2;
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()
{
if (updateTimer != null)
{
updateTimer.Dispose();
updateTimer = null;
}
// Restart update to remove mappings before stopping
UpdatePortMappings();
}
public void StopBlocking()
{
refreshExternalIPThreadFlags = ThreadFlags.ShouldQuit;
updatePortMappingsThreadFlags = ThreadFlags.ShouldQuit;
NATPMP.natpmp_t natpmp = new NATPMP.natpmp_t();
NATPMP.initnatpmp(ref natpmp);
List<PortMapping> mappingsToRemove = PortMapper.SharedInstance.PortMappingsToRemove;
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;
for (int i = 0; i < mappingsToStop.Count; i++)
{
PortMapping pm = mappingsToStop[i];
if (pm.MappingStatus == PortMappingStatus.Mapped)
{
RemovePortMapping(pm, ref natpmp);
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endregion
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#region Delegate Methods
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
protected virtual void OnDidFail()
{
if (DidFail != null)
{
PortMapper.SharedInstance.Invoke(DidFail, this);
}
}
protected virtual void OnDidGetExternalIPAddress(IPAddress ip)
{
if (DidGetExternalIPAddress != null)
{
DidGetExternalIPAddress(this, ip);
//PortMapper.SharedInstance.Invoke(DidGetExternalIPAddress, this, ip);
}
}
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);
}
}
protected virtual void OnDidReceiveBroadcastExternalIPChange(IPAddress externalIP, IPAddress senderIP)
{
if (lastBroadcastExternalIP == null)
{
lastBroadcastExternalIP = externalIP;
}
else
{
if (lastBroadcastExternalIP == externalIP)
{
// To accommodate packet loss, the NAT-PMP protocol may broadcast
// an external IP address change up to 10 times.
// We only need to broadcast it once.
return;
}
}
if (DidReceiveBroadcastExternalIPChange != null)
{
DidReceiveBroadcastExternalIPChange(this, externalIP, senderIP);
//PortMapper.SharedInstance.Invoke(DidReceiveBroadcastExternalIPChange, this, externalIP, senderIP);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endregion
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#region Private API
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
private bool AddPortMapping(PortMapping portMapping, ref NATPMP.natpmp_t natpmp)
{
return ApplyPortMapping(portMapping, false, ref natpmp);
}
private bool RefreshPortMapping(PortMapping portMapping, ref NATPMP.natpmp_t natpmp)
{
return ApplyPortMapping(portMapping, false, ref natpmp);
}
private bool RemovePortMapping(PortMapping portMapping, ref NATPMP.natpmp_t natpmp)
{
return ApplyPortMapping(portMapping, true, 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 Refresh External IP Thread
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void RefreshExternalIPThread()
{
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)");
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);
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();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endregion
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#region Update Port Mappings Thread
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void UpdatePortMappingsThread()
{
OnDidBeginWorking();
NATPMP.natpmp_t natpmp = new NATPMP.natpmp_t();
NATPMP.initnatpmp(ref natpmp);
// 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, 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;
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);
if (PortMapper.SharedInstance.IsRunning)
{
if ((updatePortMappingsThreadFlags & ThreadFlags.ShouldRestart) > 0)
{
UpdatePortMappings();
}
else if ((updatePortMappingsThreadFlags & ThreadFlags.ShouldQuit) > 0)
{
Refresh();
}
else
{
AdjustUpdateTimer();
}
}
OnDidEndWorking();
}
private void AdjustUpdateTimer()
{
if (updateTimer != null)
{
updateTimer.Dispose();
updateTimer = null;
}
updateTimer = new Timer(new TimerCallback(UpdatePortMappings), null, (updateInterval * 1000), Timeout.Infinite);
}
private void UpdatePortMappings(object state)
{
// Called via timer (on background thread)
UpdatePortMappings();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endregion
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void udpClient_DidReceive(IAsyncResult ar)
{
// When the public address changes, the NAT gateway will send a notification on the
// multicast group 224.0.0.1 port 5351 with the format of a public address response.
//
// Public address response:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Vers = 0 | OP = 128 + 0 | Result Code |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Seconds Since Start of Epoch |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Public IPv4 Address (a.b.c.d) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
DebugLog.WriteLine("NAT-PMP: udpClient_DidReceive");
try
{
IPEndPoint ep = new IPEndPoint(IPAddress.Parse("224.0.0.1"), 5351);
byte[] data = udpClient.EndReceive(ar, ref ep);
if (data.Length == 12)
{
byte[] rawIP = new byte[4];
Buffer.BlockCopy(data, 8, rawIP, 0, 4);
IPAddress newIP = new IPAddress(rawIP);
OnDidReceiveBroadcastExternalIPChange(newIP, ep.Address);
}
}
catch (Exception e)
{
DebugLog.WriteLine("NAT-PMP: udpClient_DidReceive: Exception: {0}", e);
}
udpClient.BeginReceive(new AsyncCallback(udpClient_DidReceive), null);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{7D79CD16-563E-470E-8042-58863719544A}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TCMPortMapper</RootNamespace>
<AssemblyName>TCMPortMapper</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>2.0</OldToolsVersion>
<UpgradeBackupLocation />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\Target\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>..\Target\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Windows.Forms" />
<Reference Include="Yaulw">
<HintPath>..\..\3rdParty\Sdaleo\Yaulw.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="DDLog.cs" />
<Compile Include="Helpers\Utility.cs" />
<Compile Include="MiniUPnP.cs" />
<Compile Include="NATPMP.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TCMNATPMPPortMapper.cs" />
<Compile Include="TCMPortMapper.cs" />
<Compile Include="TCMUPnPPortMapper.cs" />
<Compile Include="Win32.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Content Include="oui.txt" />
<Content Include="ouiformatted.txt" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<PropertyGroup>
<PreBuildEvent>copy /y "$(SolutionDir)..\3rdParty\Sdaleo\miniupnp.dll" "$(TargetDir)miniupnp.dll"
copy /y "$(SolutionDir)..\3rdParty\Sdaleo\natpmp.dll" "$(TargetDir)natpmp.dll"</PreBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectView>ShowAllFiles</ProjectView>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,954 @@
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
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,125 @@
using System;
using System.Runtime.InteropServices;
using System.Net.Sockets;
namespace TCMPortMapper
{
class Win32
{
[StructLayout(LayoutKind.Sequential)]
public struct FileDescriptorSet
{
//
// how many are set?
//
public int Count;
//
// an array of Socket handles
//
[MarshalAs(UnmanagedType.ByValArray, SizeConst=MaxCount)]
public IntPtr[] Array;
public static readonly int Size = Marshal.SizeOf(typeof(FileDescriptorSet));
public static readonly FileDescriptorSet Empty = new FileDescriptorSet(0);
public const int MaxCount = 64;
public FileDescriptorSet(int count)
{
Count = count;
Array = count == 0 ? null : new IntPtr[MaxCount];
}
}
//
// Structure used in select() call, taken from the BSD file sys/time.h.
//
[StructLayout(LayoutKind.Sequential)]
public struct TimeValue
{
public int Seconds; // seconds
public int Microseconds; // and microseconds
}
[StructLayout(LayoutKind.Sequential)]
public struct WSAData
{
public short wVersion;
public short wHighVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=257)]
public string szDescription;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=129)]
public string szSystemStatus;
public short iMaxSockets;
public short iMaxUdpDg;
public int lpVendorInfo;
}
[DllImport("wsock32.dll", CharSet=CharSet.Ansi, BestFitMapping=false, ThrowOnUnmappableChar=true, SetLastError=true)]
public static extern SocketError WSAStartup([In] short wVersionRequested, [Out] out WSAData lpWSAData);
[DllImport("wsock32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
public static extern int WSACleanup();
[DllImport("wsock32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
public static extern int select([In] int ignoredParameter,
[In, Out] ref FileDescriptorSet readfds,
[In, Out] ref FileDescriptorSet writefds,
[In, Out] ref FileDescriptorSet exceptfds,
[In] ref TimeValue timeout);
[DllImport("wsock32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
public static extern int select([In] int ignoredParameter,
[In, Out] ref FileDescriptorSet readfds,
[In, Out] ref FileDescriptorSet writefds,
[In, Out] ref FileDescriptorSet exceptfds,
[In] IntPtr nullTimeout);
[DllImport("wsock32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
public static extern int select([In] int ignoredParameter,
[In, Out] ref FileDescriptorSet readfds,
[In] IntPtr ignoredA,
[In] IntPtr ignoredB,
[In] ref TimeValue timeout);
[DllImport("wsock32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
public static extern int select([In] int ignoredParameter,
[In, Out] ref FileDescriptorSet readfds,
[In] IntPtr ignoredA,
[In] IntPtr ignoredB,
[In] IntPtr nullTimeout);
[DllImport("wsock32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
public static extern int select([In] int ignoredParameter,
[In] IntPtr ignoredA,
[In, Out] ref FileDescriptorSet writefds,
[In] IntPtr ignoredB,
[In] ref TimeValue timeout);
[DllImport("wsock32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
public static extern int select([In] int ignoredParameter,
[In] IntPtr ignoredA,
[In, Out] ref FileDescriptorSet writefds,
[In] IntPtr ignoredB,
[In] IntPtr nullTimeout);
[DllImport("wsock32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
public static extern int select([In] int ignoredParameter,
[In] IntPtr ignoredA,
[In] IntPtr ignoredB,
[In, Out] ref FileDescriptorSet exceptfds,
[In] ref TimeValue timeout);
[DllImport("wsock32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
public static extern int select([In] int ignoredParameter,
[In] IntPtr ignoredA,
[In] IntPtr ignoredB,
[In, Out] ref FileDescriptorSet exceptfds,
[In] IntPtr nullTimeout);
[DllImport("iphlpapi.dll")]
public static extern Int32 SendARP([In] UInt32 destIpAddress,
[In] UInt32 srcIpAddress,
[In, Out] byte[] macAddress,
[In, Out] ref Int32 macAddressLength);
}
}

View File

@@ -0,0 +1,5 @@
C:\Users\Administrator\Projects\Pluto\Server\TCMPortMapper\bin\x86\Debug\TCMPortMapper.dll
C:\Users\Administrator\Projects\Pluto\Server\TCMPortMapper\bin\x86\Debug\TCMPortMapper.pdb
C:\Users\Administrator\Projects\Pluto\Server\TCMPortMapper\obj\x86\Debug\ResolveAssemblyReference.cache
C:\Users\Administrator\Projects\Pluto\Server\TCMPortMapper\obj\x86\Debug\TCMPortMapper.dll
C:\Users\Administrator\Projects\Pluto\Server\TCMPortMapper\obj\x86\Debug\TCMPortMapper.pdb

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@