package turn import ( "fmt" "net" pionturn "github.com/pion/turn/v4" ) type Server struct { udpConn net.PacketConn tcpListener net.Listener srv *pionturn.Server } type Config struct { ListenAddr string // "0.0.0.0:3478" PublicIP string Realm string Username string Password string MinRelayPort uint16 MaxRelayPort uint16 } func relayGen(ip net.IP, minPort, maxPort uint16) pionturn.RelayAddressGenerator { if minPort != 0 && maxPort != 0 && minPort <= maxPort { return &pionturn.RelayAddressGeneratorPortRange{ RelayAddress: ip, Address: "0.0.0.0", MinPort: minPort, MaxPort: maxPort, } } return &pionturn.RelayAddressGeneratorStatic{ RelayAddress: ip, Address: "0.0.0.0", } } func Start(cfg Config) (*Server, error) { ip := net.ParseIP(cfg.PublicIP) if ip == nil { return nil, fmt.Errorf("turn: invalid PublicIP: %s", cfg.PublicIP) } udpConn, err := net.ListenPacket("udp4", cfg.ListenAddr) if err != nil { return nil, err } tcpListener, err := net.Listen("tcp4", cfg.ListenAddr) if err != nil { _ = udpConn.Close() return nil, err } rg := relayGen(ip, cfg.MinRelayPort, cfg.MaxRelayPort) srv, err := pionturn.NewServer(pionturn.ServerConfig{ Realm: cfg.Realm, AuthHandler: func(username, realm string, srcAddr net.Addr) ([]byte, bool) { if username != cfg.Username { return nil, false } return pionturn.GenerateAuthKey(username, realm, cfg.Password), true }, PacketConnConfigs: []pionturn.PacketConnConfig{ { PacketConn: udpConn, RelayAddressGenerator: rg, }, }, ListenerConfigs: []pionturn.ListenerConfig{ { Listener: tcpListener, RelayAddressGenerator: rg, }, }, }) if err != nil { _ = tcpListener.Close() _ = udpConn.Close() return nil, err } return &Server{ udpConn: udpConn, tcpListener: tcpListener, srv: srv, }, nil } func (s *Server) Close() error { if s == nil { return nil } if s.srv != nil { _ = s.srv.Close() } if s.tcpListener != nil { _ = s.tcpListener.Close() } if s.udpConn != nil { _ = s.udpConn.Close() } return nil }