Initial Commit
This commit is contained in:
81
TomcatServer/TCMPortMapper/DDLog.cs
Normal file
81
TomcatServer/TCMPortMapper/DDLog.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
80
TomcatServer/TCMPortMapper/Helpers/Utility.cs
Normal file
80
TomcatServer/TCMPortMapper/Helpers/Utility.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
321
TomcatServer/TCMPortMapper/MiniUPnP.cs
Normal file
321
TomcatServer/TCMPortMapper/MiniUPnP.cs
Normal 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
|
||||
}
|
||||
}
|
||||
111
TomcatServer/TCMPortMapper/NATPMP.cs
Normal file
111
TomcatServer/TCMPortMapper/NATPMP.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
35
TomcatServer/TCMPortMapper/Properties/AssemblyInfo.cs
Normal file
35
TomcatServer/TCMPortMapper/Properties/AssemblyInfo.cs
Normal 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")]
|
||||
569
TomcatServer/TCMPortMapper/TCMNATPMPPortMapper.cs
Normal file
569
TomcatServer/TCMPortMapper/TCMNATPMPPortMapper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
1531
TomcatServer/TCMPortMapper/TCMPortMapper.cs
Normal file
1531
TomcatServer/TCMPortMapper/TCMPortMapper.cs
Normal file
File diff suppressed because it is too large
Load Diff
133
TomcatServer/TCMPortMapper/TCMPortMapper.csproj
Normal file
133
TomcatServer/TCMPortMapper/TCMPortMapper.csproj
Normal 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>
|
||||
6
TomcatServer/TCMPortMapper/TCMPortMapper.csproj.user
Normal file
6
TomcatServer/TCMPortMapper/TCMPortMapper.csproj.user
Normal 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>
|
||||
954
TomcatServer/TCMPortMapper/TCMUPnPPortMapper.cs
Normal file
954
TomcatServer/TCMPortMapper/TCMUPnPPortMapper.cs
Normal 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
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
}
|
||||
125
TomcatServer/TCMPortMapper/Win32.cs
Normal file
125
TomcatServer/TCMPortMapper/Win32.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
BIN
TomcatServer/TCMPortMapper/bin/x86/Debug/TCMPortMapper.dll
Normal file
BIN
TomcatServer/TCMPortMapper/bin/x86/Debug/TCMPortMapper.dll
Normal file
Binary file not shown.
BIN
TomcatServer/TCMPortMapper/bin/x86/Debug/TCMPortMapper.pdb
Normal file
BIN
TomcatServer/TCMPortMapper/bin/x86/Debug/TCMPortMapper.pdb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
BIN
TomcatServer/TCMPortMapper/obj/x86/Debug/TCMPortMapper.dll
Normal file
BIN
TomcatServer/TCMPortMapper/obj/x86/Debug/TCMPortMapper.dll
Normal file
Binary file not shown.
BIN
TomcatServer/TCMPortMapper/obj/x86/Debug/TCMPortMapper.pdb
Normal file
BIN
TomcatServer/TCMPortMapper/obj/x86/Debug/TCMPortMapper.pdb
Normal file
Binary file not shown.
Binary file not shown.
107464
TomcatServer/TCMPortMapper/oui.txt
Normal file
107464
TomcatServer/TCMPortMapper/oui.txt
Normal file
File diff suppressed because it is too large
Load Diff
1
TomcatServer/TCMPortMapper/ouiformatted.txt
Normal file
1
TomcatServer/TCMPortMapper/ouiformatted.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
Reference in New Issue
Block a user