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; } /// /// This structure is used to avoid a SystemAccessViolation, /// and to prevent corruption of the process' memory. /// See issue #2 for further discussion. /// [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; } } } /// /// 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. /// /// /// Delay (in milliseconds) is the maximum time for waiting any device response. /// /// /// If NULL, default multicast interface for sending SSDP discover packets will be used. /// /// /// If null, default path for minissdpd socket will be used. /// /// /// A pointer to a UPNPDev structure. Free this when done. /// Use the PtrToUPNPDev method to convert to a UPNPDev structure. /// [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); /// /// frees list returned by upnpDiscover() /// /// /// IntPtr (pointer to UPNPDev structure) as returned by upnpDiscover(). /// [DllImport("miniupnp.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void freeUPNPDevlist([In] IntPtr devlistP); /// /// Used when skipping the discovery process. /// Return value: /// 0 - Not OK /// 1 - OK /// /// /// /// /// /// /// [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); /// /// Extracts the external IP address. /// /// /// /// /// The array to copy the external IP address bytes into. /// The array must be 16 bytes in length. /// /// /// 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. /// [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); /// /// Description forthcoming... /// /// /// /// /// /// /// /// /// /// /// /// /// /// 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 /// [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); /// /// Adds a UPnP port mapping using the given information. /// /// /// /// /// /// /// /// /// /// 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 /// [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); /// /// Deletes a UPnP port mapping with the given information. /// /// /// /// /// /// /// 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) /// [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 } }