fix: 回退版本

This commit is contained in:
spiritlhl
2025-07-22 14:46:41 +00:00
parent bae8edf888
commit 8e585855fe
5 changed files with 66 additions and 112 deletions

View File

@@ -24,7 +24,7 @@ jobs:
run: | run: |
git config --global user.name 'github-actions' git config --global user.name 'github-actions'
git config --global user.email 'github-actions@github.com' git config --global user.email 'github-actions@github.com'
TAG="v0.0.6-$(date +'%Y%m%d%H%M%S')" TAG="v0.0.5-$(date +'%Y%m%d%H%M%S')"
git tag $TAG git tag $TAG
git push origin $TAG git push origin $TAG
echo "TAG=$TAG" >> $GITHUB_ENV echo "TAG=$TAG" >> $GITHUB_ENV

View File

@@ -83,7 +83,6 @@ func main() {
gostunFlag.StringVar(&model.AddrStr, "server", "stun.voipgate.com:3478", "Specify STUN server address") gostunFlag.StringVar(&model.AddrStr, "server", "stun.voipgate.com:3478", "Specify STUN server address")
gostunFlag.BoolVar(&model.EnableLoger, "e", true, "Enable logging functionality") gostunFlag.BoolVar(&model.EnableLoger, "e", true, "Enable logging functionality")
gostunFlag.StringVar(&model.IPVersion, "type", "ipv4", "Specify ip test version: ipv4, ipv6 or both") gostunFlag.StringVar(&model.IPVersion, "type", "ipv4", "Specify ip test version: ipv4, ipv6 or both")
gostunFlag.StringVar(&model.TransmissionProtocol, "protocol", "udp", "Specify transmission protocol: udp, tcp, or tls")
gostunFlag.Parse(os.Args[1:]) gostunFlag.Parse(os.Args[1:])
if help { if help {
fmt.Printf("Usage: %s [options]\n", os.Args[0]) fmt.Printf("Usage: %s [options]\n", os.Args[0])
@@ -164,4 +163,4 @@ func main() {
model.IPVersion = originalIPVersion model.IPVersion = originalIPVersion
res := stuncheck.CheckType() res := stuncheck.CheckType()
fmt.Printf("NAT Type: %s\n", res) fmt.Printf("NAT Type: %s\n", res)
} }

View File

@@ -2,18 +2,17 @@ package model
import "github.com/pion/logging" import "github.com/pion/logging"
const GoStunVersion = "v0.0.6" const GoStunVersion = "v0.0.5"
var ( var (
AddrStr = "stun.voipgate.com:3478" AddrStr = "stun.voipgate.com:3478"
Timeout = 5 Timeout = 3
Verbose = 0 Verbose = 0
Log logging.LeveledLogger Log logging.LeveledLogger
NatMappingBehavior string NatMappingBehavior string
NatFilteringBehavior string NatFilteringBehavior string
EnableLoger = true EnableLoger = true
IPVersion = "ipv4" IPVersion = "ipv4"
TransmissionProtocol = "udp"
) )
func GetDefaultServers(IPVersion string) []string { func GetDefaultServers(IPVersion string) []string {
@@ -57,4 +56,4 @@ func GetDefaultServers(IPVersion string) []string {
"stun.f.haeder.net:3478", "stun.f.haeder.net:3478",
} }
} }
} }

View File

@@ -6,6 +6,8 @@ import (
"github.com/oneclickvirt/gostun/model" "github.com/oneclickvirt/gostun/model"
) )
// CheckType
// Summarize the NAT type
func CheckType() string { func CheckType() string {
var result string var result string
if model.NatMappingBehavior != "" && model.NatFilteringBehavior != "" { if model.NatMappingBehavior != "" && model.NatFilteringBehavior != "" {
@@ -28,4 +30,4 @@ func CheckType() string {
result = "Inconclusive" result = "Inconclusive"
} }
return result return result
} }

View File

@@ -1,7 +1,6 @@
package stuncheck package stuncheck
import ( import (
"crypto/tls"
"errors" "errors"
"net" "net"
"time" "time"
@@ -10,13 +9,14 @@ import (
"github.com/pion/stun/v2" "github.com/pion/stun/v2"
) )
// From https://github.com/pion/stun/blob/master/cmd/stun-nat-behaviour/main.go
type stunServerConn struct { type stunServerConn struct {
conn net.Conn conn net.PacketConn
LocalAddr net.Addr LocalAddr net.Addr
RemoteAddr *net.UDPAddr RemoteAddr *net.UDPAddr
OtherAddr *net.UDPAddr OtherAddr *net.UDPAddr
messageChan chan *stun.Message messageChan chan *stun.Message
protocol string
} }
func (c *stunServerConn) Close() error { func (c *stunServerConn) Close() error {
@@ -45,30 +45,15 @@ func isIPv6Address(addr string) bool {
func getNetworkType(addrStr string) string { func getNetworkType(addrStr string) string {
switch model.IPVersion { switch model.IPVersion {
case "ipv6": case "ipv6":
if model.TransmissionProtocol == "tcp" {
return "tcp6"
}
return "udp6" return "udp6"
case "ipv4": case "ipv4":
if model.TransmissionProtocol == "tcp" {
return "tcp4"
}
return "udp4" return "udp4"
case "both": case "both":
if isIPv6Address(addrStr) { if isIPv6Address(addrStr) {
if model.TransmissionProtocol == "tcp" {
return "tcp6"
}
return "udp6" return "udp6"
} }
if model.TransmissionProtocol == "tcp" {
return "tcp4"
}
return "udp4" return "udp4"
} }
if model.TransmissionProtocol == "tcp" {
return "tcp4"
}
return "udp4" return "udp4"
} }
@@ -83,7 +68,8 @@ func getCurrentProtocol(addrStr string) string {
return "ipv4" return "ipv4"
} }
func MappingTests(addrStr string) error { // RFC 5780 implementation (current)
func MappingTests(addrStr string) error { //nolint:cyclop
currentProtocol := getCurrentProtocol(addrStr) currentProtocol := getCurrentProtocol(addrStr)
mapTestConn, err := connect(addrStr) mapTestConn, err := connect(addrStr)
if err != nil { if err != nil {
@@ -171,7 +157,8 @@ func MappingTests(addrStr string) error {
return mapTestConn.Close() return mapTestConn.Close()
} }
func FilteringTests(addrStr string) error { // RFC 5780 implementation (current)
func FilteringTests(addrStr string) error { //nolint:cyclop
currentProtocol := getCurrentProtocol(addrStr) currentProtocol := getCurrentProtocol(addrStr)
mapTestConn, err := connect(addrStr) mapTestConn, err := connect(addrStr)
if err != nil { if err != nil {
@@ -241,6 +228,7 @@ func FilteringTests(addrStr string) error {
return mapTestConn.Close() return mapTestConn.Close()
} }
// RFC 5389/8489 implementation - basic STUN binding request
func MappingTestsRFC5389(addrStr string) error { func MappingTestsRFC5389(addrStr string) error {
currentProtocol := getCurrentProtocol(addrStr) currentProtocol := getCurrentProtocol(addrStr)
mapTestConn, err := connect(addrStr) mapTestConn, err := connect(addrStr)
@@ -269,10 +257,12 @@ func MappingTestsRFC5389(addrStr string) error {
if model.EnableLoger { if model.EnableLoger {
model.Log.Infof("[%s] RFC5389: Received XOR-MAPPED-ADDRESS: %v", currentProtocol, resps.xorAddr) model.Log.Infof("[%s] RFC5389: Received XOR-MAPPED-ADDRESS: %v", currentProtocol, resps.xorAddr)
} }
// Simple classification based on whether we're behind NAT
if resps.xorAddr.String() == mapTestConn.LocalAddr.String() { if resps.xorAddr.String() == mapTestConn.LocalAddr.String() {
model.NatMappingBehavior = "endpoint independent (no NAT)" model.NatMappingBehavior = "endpoint independent (no NAT)"
model.NatFilteringBehavior = "endpoint independent" model.NatFilteringBehavior = "endpoint independent"
} else { } else {
// Can't determine exact type with RFC5389, so use conservative estimate
model.NatMappingBehavior = "address and port dependent" model.NatMappingBehavior = "address and port dependent"
model.NatFilteringBehavior = "address and port dependent" model.NatFilteringBehavior = "address and port dependent"
} }
@@ -283,6 +273,7 @@ func MappingTestsRFC5389(addrStr string) error {
return nil return nil
} }
// RFC 3489 implementation - classic STUN
func MappingTestsRFC3489(addrStr string) error { func MappingTestsRFC3489(addrStr string) error {
currentProtocol := getCurrentProtocol(addrStr) currentProtocol := getCurrentProtocol(addrStr)
mapTestConn, err := connect(addrStr) mapTestConn, err := connect(addrStr)
@@ -296,6 +287,7 @@ func MappingTestsRFC3489(addrStr string) error {
if model.EnableLoger { if model.EnableLoger {
model.Log.Infof("[%s] RFC3489: Test I - Basic binding request", currentProtocol) model.Log.Infof("[%s] RFC3489: Test I - Basic binding request", currentProtocol)
} }
// Test I: Basic binding request
request := stun.MustBuild(stun.TransactionID, stun.BindingRequest) request := stun.MustBuild(stun.TransactionID, stun.BindingRequest)
resp, err := mapTestConn.roundTrip(request, mapTestConn.RemoteAddr) resp, err := mapTestConn.roundTrip(request, mapTestConn.RemoteAddr)
if err != nil { if err != nil {
@@ -303,6 +295,7 @@ func MappingTestsRFC3489(addrStr string) error {
} }
resps1 := parse(resp) resps1 := parse(resp)
var mappedAddr *net.UDPAddr var mappedAddr *net.UDPAddr
// Try XOR-MAPPED-ADDRESS first, then MAPPED-ADDRESS
if resps1.xorAddr != nil { if resps1.xorAddr != nil {
mappedAddr, _ = net.ResolveUDPAddr("udp", resps1.xorAddr.String()) mappedAddr, _ = net.ResolveUDPAddr("udp", resps1.xorAddr.String())
} else if resps1.mappedAddr != nil { } else if resps1.mappedAddr != nil {
@@ -317,35 +310,26 @@ func MappingTestsRFC3489(addrStr string) error {
if model.EnableLoger { if model.EnableLoger {
model.Log.Infof("[%s] RFC3489: Received mapped address: %v", currentProtocol, mappedAddr) model.Log.Infof("[%s] RFC3489: Received mapped address: %v", currentProtocol, mappedAddr)
} }
localAddr := mapTestConn.LocalAddr // Check if we're behind NAT
if model.TransmissionProtocol == "tcp" || model.TransmissionProtocol == "tls" { localUDP, _ := mapTestConn.LocalAddr.(*net.UDPAddr)
localTCP := localAddr.(*net.TCPAddr) if mappedAddr.IP.Equal(localUDP.IP) && mappedAddr.Port == localUDP.Port {
if mappedAddr.IP.Equal(localTCP.IP) && mappedAddr.Port == localTCP.Port { // No NAT
model.NatMappingBehavior = "endpoint independent (no NAT)" model.NatMappingBehavior = "endpoint independent (no NAT)"
model.NatFilteringBehavior = "endpoint independent" model.NatFilteringBehavior = "endpoint independent"
if model.EnableLoger { if model.EnableLoger {
model.Log.Warnf("[%s] RFC3489: No NAT detected", currentProtocol) model.Log.Warnf("[%s] RFC3489: No NAT detected", currentProtocol)
}
return nil
}
} else {
localUDP := localAddr.(*net.UDPAddr)
if mappedAddr.IP.Equal(localUDP.IP) && mappedAddr.Port == localUDP.Port {
model.NatMappingBehavior = "endpoint independent (no NAT)"
model.NatFilteringBehavior = "endpoint independent"
if model.EnableLoger {
model.Log.Warnf("[%s] RFC3489: No NAT detected", currentProtocol)
}
return nil
} }
return nil
} }
// Test II: Binding request with change IP and Port
if model.EnableLoger { if model.EnableLoger {
model.Log.Infof("[%s] RFC3489: Test II - Request with change IP and Port", currentProtocol) model.Log.Infof("[%s] RFC3489: Test II - Request with change IP and Port", currentProtocol)
} }
request2 := stun.MustBuild(stun.TransactionID, stun.BindingRequest) request2 := stun.MustBuild(stun.TransactionID, stun.BindingRequest)
request2.Add(stun.AttrChangeRequest, []byte{0x00, 0x00, 0x00, 0x06}) request2.Add(stun.AttrChangeRequest, []byte{0x00, 0x00, 0x00, 0x06}) // Change both IP and port
resp2, err2 := mapTestConn.roundTrip(request2, mapTestConn.RemoteAddr) resp2, err2 := mapTestConn.roundTrip(request2, mapTestConn.RemoteAddr)
if err2 == nil && resp2 != nil { if err2 == nil && resp2 != nil {
// Full cone NAT
model.NatMappingBehavior = "endpoint independent" model.NatMappingBehavior = "endpoint independent"
model.NatFilteringBehavior = "endpoint independent" model.NatFilteringBehavior = "endpoint independent"
if model.EnableLoger { if model.EnableLoger {
@@ -353,13 +337,15 @@ func MappingTestsRFC3489(addrStr string) error {
} }
return nil return nil
} }
// Test III: Binding request with change port only
if model.EnableLoger { if model.EnableLoger {
model.Log.Infof("[%s] RFC3489: Test III - Request with change Port only", currentProtocol) model.Log.Infof("[%s] RFC3489: Test III - Request with change Port only", currentProtocol)
} }
request3 := stun.MustBuild(stun.TransactionID, stun.BindingRequest) request3 := stun.MustBuild(stun.TransactionID, stun.BindingRequest)
request3.Add(stun.AttrChangeRequest, []byte{0x00, 0x00, 0x00, 0x02}) request3.Add(stun.AttrChangeRequest, []byte{0x00, 0x00, 0x00, 0x02}) // Change port only
resp3, err3 := mapTestConn.roundTrip(request3, mapTestConn.RemoteAddr) resp3, err3 := mapTestConn.roundTrip(request3, mapTestConn.RemoteAddr)
if err3 == nil && resp3 != nil { if err3 == nil && resp3 != nil {
// Restricted cone NAT
model.NatMappingBehavior = "endpoint independent" model.NatMappingBehavior = "endpoint independent"
model.NatFilteringBehavior = "address dependent" model.NatFilteringBehavior = "address dependent"
if model.EnableLoger { if model.EnableLoger {
@@ -367,6 +353,8 @@ func MappingTestsRFC3489(addrStr string) error {
} }
return nil return nil
} }
// If we get here, we need to do additional tests for symmetric vs port restricted
// For simplicity in RFC3489, we'll classify remaining as Port Restricted or Symmetric
model.NatMappingBehavior = "address and port dependent" model.NatMappingBehavior = "address and port dependent"
model.NatFilteringBehavior = "address and port dependent" model.NatFilteringBehavior = "address and port dependent"
if model.EnableLoger { if model.EnableLoger {
@@ -413,7 +401,12 @@ func parse(msg *stun.Message) (ret struct {
} }
for _, attr := range msg.Attributes { for _, attr := range msg.Attributes {
switch attr.Type { switch attr.Type {
case stun.AttrXORMappedAddress, stun.AttrOtherAddress, stun.AttrResponseOrigin, stun.AttrMappedAddress, stun.AttrSoftware: case
stun.AttrXORMappedAddress,
stun.AttrOtherAddress,
stun.AttrResponseOrigin,
stun.AttrMappedAddress,
stun.AttrSoftware:
break //nolint:staticcheck break //nolint:staticcheck
default: default:
if model.EnableLoger { if model.EnableLoger {
@@ -430,50 +423,27 @@ func connect(addrStr string) (*stunServerConn, error) {
model.Log.Infof("[%s] Connecting to STUN server: %s", currentProtocol, addrStr) model.Log.Infof("[%s] Connecting to STUN server: %s", currentProtocol, addrStr)
} }
networkType := getNetworkType(addrStr) networkType := getNetworkType(addrStr)
var conn net.Conn addr, err := net.ResolveUDPAddr(networkType, addrStr)
var localAddr net.Addr if err != nil {
var err error if model.EnableLoger {
switch model.TransmissionProtocol { model.Log.Warnf("[%s] Error resolving address: %s", currentProtocol, err)
case "tcp":
conn, err = net.Dial(networkType, addrStr)
if err != nil {
return nil, err
} }
localAddr = conn.LocalAddr() return nil, err
case "tls": }
config := &tls.Config{InsecureSkipVerify: true} c, err := net.ListenUDP(networkType, nil)
conn, err = tls.Dial(networkType[:3], addrStr, config) if err != nil {
if err != nil { return nil, err
return nil, err
}
localAddr = conn.LocalAddr()
default:
_, err := net.ResolveUDPAddr(networkType, addrStr)
if err != nil {
if model.EnableLoger {
model.Log.Warnf("[%s] Error resolving address: %s", currentProtocol, err)
}
return nil, err
}
udpConn, err := net.ListenUDP(networkType, nil)
if err != nil {
return nil, err
}
conn = udpConn
localAddr = udpConn.LocalAddr()
} }
if model.EnableLoger { if model.EnableLoger {
model.Log.Infof("[%s] Local address: %s", currentProtocol, localAddr.String()) model.Log.Infof("[%s] Local address: %s", currentProtocol, c.LocalAddr())
model.Log.Infof("[%s] Remote address: %s", currentProtocol, addrStr) model.Log.Infof("[%s] Remote address: %s", currentProtocol, addr.String())
} }
remoteAddr, _ := net.ResolveUDPAddr("udp", addrStr) mChan := listen(c)
mChan := listen(conn)
return &stunServerConn{ return &stunServerConn{
conn: conn, conn: c,
LocalAddr: localAddr, LocalAddr: c.LocalAddr(),
RemoteAddr: remoteAddr, RemoteAddr: addr,
messageChan: mChan, messageChan: mChan,
protocol: model.TransmissionProtocol,
}, nil }, nil
} }
@@ -486,17 +456,7 @@ func (c *stunServerConn) roundTrip(msg *stun.Message, addr net.Addr) (*stun.Mess
model.Log.Debugf("\t%v (l=%v)", attr, attr.Length) model.Log.Debugf("\t%v (l=%v)", attr, attr.Length)
} }
} }
var err error _, err := c.conn.WriteTo(msg.Raw, addr)
switch c.protocol {
case "tcp", "tls":
_, err = c.conn.Write(msg.Raw)
default:
if udpConn, ok := c.conn.(*net.UDPConn); ok {
_, err = udpConn.WriteTo(msg.Raw, addr)
} else {
_, err = c.conn.Write(msg.Raw)
}
}
if err != nil { if err != nil {
if model.EnableLoger { if model.EnableLoger {
model.Log.Warnf("Error sending request to %v", addr) model.Log.Warnf("Error sending request to %v", addr)
@@ -517,22 +477,15 @@ func (c *stunServerConn) roundTrip(msg *stun.Message, addr net.Addr) (*stun.Mess
} }
} }
func listen(conn net.Conn) (messages chan *stun.Message) { // taken from https://github.com/pion/stun/blob/master/cmd/stun-traversal/main.go
func listen(conn *net.UDPConn) (messages chan *stun.Message) {
messages = make(chan *stun.Message) messages = make(chan *stun.Message)
go func() { go func() {
defer close(messages)
for { for {
buf := make([]byte, 1024) buf := make([]byte, 1024)
var n int n, addr, err := conn.ReadFromUDP(buf)
var addr net.Addr
var err error
if udpConn, ok := conn.(*net.UDPConn); ok {
n, addr, err = udpConn.ReadFromUDP(buf)
} else {
n, err = conn.Read(buf)
addr = conn.RemoteAddr()
}
if err != nil { if err != nil {
close(messages)
return return
} }
if model.EnableLoger { if model.EnableLoger {
@@ -546,10 +499,11 @@ func listen(conn net.Conn) (messages chan *stun.Message) {
if model.EnableLoger { if model.EnableLoger {
model.Log.Infof("Error decoding message: %v", err) model.Log.Infof("Error decoding message: %v", err)
} }
close(messages)
return return
} }
messages <- m messages <- m
} }
}() }()
return return
} }