i00 NAT 转发器
为支持 UPnP 的路由器自动进行端口转发。
引言
我编写这个应用程序是因为我认为路由器端口转发存在一些不足之处,例如无法将端口转发映射到计算机名称而不是 IP 地址,从而限制计算机必须具有静态 IP。 此外,还有其他优势,例如可移植性、易用性等。
以下列出了优势摘要
- 允许映射到计算机名称以及 IP 地址。
- 允许计算机拥有动态 IP 地址,仍然可以进行端口转发(从而允许轻松连接到多个“其他”网络,而无需更改您的 IP 或在路由器中设置它)。
- 绕过 Netgear 不允许端口重定向的限制 - Netgear 路由器不允许您通过其界面将一个外部端口上的端口转发到正在托管该端口的端口。
- 可移植性 - 允许您将计算机从一个网络移动到另一个网络,并结合 dyndns 客户端,使所有功能在新网络上“即刻生效”,想象一下能够将您的 Web 服务器从一个网络移动到另一个网络而无需更改任何设置!
此应用程序的唯一要求是启用了 UPnP 的路由器。
我已在 Server 2012 上进行了测试,但它应该适用于任何 Vista + 版本……并且可能适用于以前的操作系统。
我已在以下品牌的路由器上测试了该软件:Netgear、Billion。
屏幕截图

方法
UPnP 访问是通过随 Windows 提供的 NATUPNPLib.dll 完成的,这需要启用 SSDP 发现服务(NAT 转发器在启动时如果未运行会要求您启动它)。NAT 转发器可以通过界面安装为服务,并自动检查端口是否在设置页面中指定的间隔打开。
该项目的主要功能当然是通过 UPnP 路由器自动执行端口转发。为此使用了“NATUPnP 1.0 类型库”。要实现 UPnP,首先确保“SSDP 发现服务”正在运行,为此,单击“开始”然后“运行”,键入 services.msc 并单击“确定”。
在列表中找到“SSDP 发现服务”,并确保其状态为“正在运行”,如果已停止,则右键单击它并选择“启动”;如果已禁用,则右键单击它,选择“属性”,将启动类型设置为“自动”,单击“确定”,然后右键单击该服务并单击“启动”。
在您的项目中添加对 COM 库的引用:“NATUPnP 1.0 类型库”。
您现在应该能够从您的项目中管理端口转发。为了方便起见,可以使用以下类来管理端口转发
'i00 .Net NAT Forwarder
'©i00 Productions All rights reserved
'Created by Kris Bennett
'----------------------------------------------------------------------------------------------------
'All property in this file is and remains the property of i00 Productions, regardless of its usage,
'unless stated otherwise in writing from i00 Productions.
'
'i00 is not and shall not be held accountable for any damages directly or indirectly caused by the
'use or miss-use of this product.
Public Class UPnP
Implements IDisposable
'Variables for the COM objects we are going to use
Private UPnPNAT As NATUPNPLib.UPnPNAT
Public Mappings As NATUPNPLib.IStaticPortMappingCollection
'Returns true if UPnP is enabled
Dim mc_Enabled As Boolean
Public ReadOnly Property UPnPEnabled() As Boolean
Get
Return mc_Enabled
End Get
End Property
'List of protocols that we can port forward to (used in functions)
Public Enum Protocol
TCP
UDP
End Enum
#Region "Constructors / Destructors"
Public Sub New()
'create our COM object to manage port forwards
UPnPNAT = New NATUPNPLib.UPnPNAT
'check that mappings can be obtained from the router and load the mapping COM object to our Mappings variable
Me.GetMappings()
End Sub
'check that mappings can be obtained from the router and load the mapping COM object to our Mappings variable
Private Sub GetMappings()
Try
'try to get the static port mappings from the router... using the NATUPNPLib.UPnPNAT COM library...
Mappings = UPnPNAT.StaticPortMappingCollection()
If Mappings Is Nothing Then
'no static mapping object returned - UPnP is not enabled
mc_Enabled = False
Else
'UPnP is enabled and working
mc_Enabled = True
End If
Catch ex As NotImplementedException
mc_Enabled = False
End Try
End Sub
Private disposedValue As Boolean = False 'To detect redundant calls
'Remove the com objects from memory...
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
If Mappings IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(Mappings)
If UPnPNAT IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(UPnPNAT)
End If
Me.disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
#Region "Router functions"
'Adds a port forwarding rule to the router...
Public Sub Add(ByVal InternalIP As String, ByVal InternalPort As Integer, ByVal ExternalPort As Integer, ByVal Protocol As Protocol, ByVal Description As String)
'check that UPnP is enabled
If Not UPnPEnabled Then Throw New Exception("Could not find a UPnP enabled router")
'check that the port isnot already mapped
If Exists(ExternalPort, Protocol) Then Throw New Exception("Mapping already exists")
'add the port mapping
Mappings.Add(ExternalPort, Protocol.ToString(), InternalPort, InternalIP, True, Description)
End Sub
'Removes a port mapping rule from the router...
Public Sub Remove(ByVal ExternalPort As Integer, ByVal Protocol As Protocol)
'check that UPnP is enabled
If Not UPnPEnabled Then Throw New Exception("Could not find a UPnP enabled router")
'check that the port isnot already mapped
If Not Exists(ExternalPort, Protocol) Then Throw New ArgumentException("Mapping does not exist")
'remove the port mapping
Mappings.Remove(ExternalPort, Protocol.ToString)
End Sub
'See if the
Public Function Exists(ByVal ExternalPort As Integer, ByVal Protocol As Protocol) As Boolean
'check that UPnP is enabled
If Not UPnPEnabled Then Throw New Exception("Could not find a UPnP enabled router")
'go through each port that is mapped in our UPnP enabled router to see if the port has been mapped...
For Each mapping In Mappings.OfType(Of NATUPNPLib.IStaticPortMapping)()
'if the external port and protocol are what we are looking for then we are mapped...
If mapping.ExternalPort.Equals(ExternalPort) AndAlso mapping.Protocol.ToString.Equals(Protocol.ToString) Then Return True
Next
'we do not have this mapping rule if we got this far
Return False
End Function
Public Function Exists(ByVal ExternalPort As Integer, ByVal Protocol As Protocol, ByVal InternalIP As String, ByVal InternalPort As Integer, ByVal Description As String) As Boolean
'check that UPnP is enabled
If Not UPnPEnabled Then Throw New Exception("Could not find a UPnP enabled router")
' Begin checking
For Each mapping In Mappings.OfType(Of NATUPNPLib.IStaticPortMapping)()
'if the external port, protocol, IP, internal port, and description are what we are looking for then we are mapped...
If mapping.ExternalPort.Equals(ExternalPort) AndAlso mapping.Protocol.ToString.Equals(Protocol.ToString) AndAlso mapping.InternalClient.Equals(InternalIP) AndAlso mapping.InternalPort.Equals(InternalPort) AndAlso mapping.Description.Equals(Description) Then Return True
Next
'we do not have this mapping rule if we got this far
Return False
End Function
#End Region
End Class
现在我们拥有一个易于使用的包装器来轻松进行端口转发,以下是一些如何使用此包装器的示例要检查 UPnP 是否已启用:
Using UPnP As New UPnP
Return UPnP.UPnPEnabled
End Using
要添加端口映射:
Using UPnP As New UPnP
UPnP.Add(localIP As String, Port As Integer, ExternalPort As Integer, prot As Protocol, desc As String)
End Using
要删除端口映射:
Using UPnP As New UPnP
UPnP.Remove(Port As Integer, Prot As Protocol)
End Using
要检查端口是否已映射:
Using UPnP As New UPnP
Return UPnP.Exists(Port As Integer, Prot As Protocol)
End Using
要从路由器加载端口映射列表:
Using UPnP As New UPnP
For Each mapping As NATUPNPLib.IStaticPortMapping In UPnP.staticMapping
Debug.Print "Local IP: " & mapping.InternalClient & vbcrlf & _
"Local Port: " & mapping.InternalPort & vbcrlf & _
"External Port: " & mapping.ExternalPort & vbcrlf & _
"Description: " & mapping.Description & vbcrlf & _
"Portocol: " & mapping.prot.ToString() & vbcrlf & _
"----------"
Next
End Using
更改日志
20130302
初始发布