diff --git a/cmd/main.go b/cmd/main.go index 39bdb24..244e96e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -25,6 +25,13 @@ func main() { go handleShutdownSignals() libs.Connect() grpcClient.UserClientInit() + // 创建 STUN 服务器实例 + stunServer := libs.NewSTUNServer(appConfig.StunPort) + // 启动 STUN 服务器 + if err1 := stunServer.Start(); err1 != nil { + log.Fatalf("Failed to start STUN server: %v", err1) + } + defer stunServer.Close() app := gin.Default() router.Init(app) app.Run(":" + strconv.Itoa(appConfig.Port)) diff --git a/config/index.go b/config/index.go index 4d96eff..c94a41a 100644 --- a/config/index.go +++ b/config/index.go @@ -12,6 +12,7 @@ type AppConfig struct { WorkerID int64 `yaml:"workerID"` DatacenterID int64 `yaml:"datacenterID"` GrpcUrl string `yaml:"grpcUrl"` + StunPort string `yaml:"stunPort"` } type DatabaseConfig struct { diff --git a/go.mod b/go.mod index 2bfa85a..63d8ae8 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,10 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pion/dtls/v2 v2.2.7 // indirect + github.com/pion/logging v0.2.2 // indirect + github.com/pion/stun v0.6.1 // indirect + github.com/pion/transport/v2 v2.2.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.0 // indirect golang.org/x/arch v0.20.0 // indirect diff --git a/libs/stun_service.go b/libs/stun_service.go new file mode 100644 index 0000000..f127bc0 --- /dev/null +++ b/libs/stun_service.go @@ -0,0 +1,132 @@ +package libs + +import ( + "fmt" + "log" + "net" + "sync" + + "github.com/pion/stun" +) + +// STUNServer 表示 STUN 服务器实例 +type STUNServer struct { + conn net.PacketConn + isRunning bool + mu sync.RWMutex + port string +} + +// NewSTUNServer 创建新的 STUN 服务器实例 +func NewSTUNServer(port string) *STUNServer { + return &STUNServer{ + port: port, + } +} + +// Start 启动 STUN 服务器 +func (s *STUNServer) Start() error { + s.mu.Lock() + defer s.mu.Unlock() + + if s.isRunning { + return fmt.Errorf("STUN server is already running") + } + + // 创建 UDP 监听器 + conn, err := net.ListenPacket("udp4", ":"+s.port) + if err != nil { + return fmt.Errorf("failed to listen on port %s: %v", s.port, err) + } + + s.conn = conn + s.isRunning = true + + // 启动 STUN 服务器 goroutine + go s.run() + + log.Printf("STUN server started on port %s", s.port) + return nil +} + +// GetSTUNAddress 获取 STUN 服务器地址 +func (s *STUNServer) GetSTUNAddress() string { + return fmt.Sprintf("stun:%s", s.port) +} + +// run 运行 STUN 服务器的主循环 +func (s *STUNServer) run() { + buf := make([]byte, 1024) + + for { + // 读取数据包 + n, addr, err := s.conn.ReadFrom(buf) + if err != nil { + log.Printf("Error reading from connection: %v", err) + continue + } + + // 处理 STUN 消息 + s.handleSTUNMessage(addr, buf[:n]) + } +} + +// handleSTUNMessage 处理 STUN 消息 +func (s *STUNServer) handleSTUNMessage(addr net.Addr, buf []byte) { + // 解析 STUN 消息 + msg := &stun.Message{Raw: buf} + if err := msg.Decode(); err != nil { + // 不是有效的 STUN 消息,可能是其他协议 + return + } + + // 只处理绑定请求 + if msg.Type != stun.BindingRequest { + return + } + + // 创建响应消息 + response, err := stun.Build(stun.BindingSuccess, stun.TransactionID) + if err != nil { + log.Printf("Error building STUN response: %v", err) + return + } + + // 添加 XOR-MAPPED-ADDRESS 属性 - 使用正确的方法 + udpAddr := addr.(*net.UDPAddr) + xorAddr := stun.XORMappedAddress{ + IP: udpAddr.IP, + Port: udpAddr.Port, + } + + // 使用 AddTo 方法而不是 Add 方法 + if err := xorAddr.AddTo(response); err != nil { + log.Printf("Error adding XOR-MAPPED-ADDRESS: %v", err) + return + } + + // 发送响应 + if _, err := s.conn.WriteTo(response.Raw, addr); err != nil { + log.Printf("Error sending response: %v", err) + } + + log.Printf("Handled STUN request from %s", addr.String()) +} + +// Close 关闭 STUN 服务器 +func (s *STUNServer) Close() error { + s.mu.Lock() + defer s.mu.Unlock() + + if !s.isRunning { + return nil + } + + if err := s.conn.Close(); err != nil { + return fmt.Errorf("error closing STUN server: %v", err) + } + + s.isRunning = false + log.Println("STUN server stopped") + return nil +}