src/server/internal/direct/turn.go
2,079 bytes · 80 lines · capsule://quake0day/[email protected]
raw on github
package direct
import (
"fmt"
"net"
"github.com/pion/turn/v4"
)
// TURNServer is an embedded TURN server that listens on TCP.
// Browser clients connect via turn:<host>:<port>?transport=tcp.
type TURNServer struct {
server *turn.Server
listener net.Listener
port int
publicIP string
realm string
username string
password string
}
// NewTURNServer creates an embedded TURN server listening on the given TCP port.
// publicIP is the relay address advertised to clients.
func NewTURNServer(port int, publicIP, realm, username, password string) (*TURNServer, error) {
listener, err := net.Listen("tcp4", fmt.Sprintf("0.0.0.0:%d", port))
if err != nil {
return nil, fmt.Errorf("listen TURN tcp :%d: %w", port, err)
}
authKey := turn.GenerateAuthKey(username, realm, password)
authHandler := func(u string, r string, srcAddr net.Addr) ([]byte, bool) {
if u == username {
return authKey, true
}
return nil, false
}
s, err := turn.NewServer(turn.ServerConfig{
Realm: realm,
AuthHandler: authHandler,
ListenerConfigs: []turn.ListenerConfig{
{
Listener: listener,
RelayAddressGenerator: &turn.RelayAddressGeneratorStatic{
RelayAddress: net.ParseIP(publicIP),
Address: "0.0.0.0",
},
},
},
})
if err != nil {
listener.Close()
return nil, fmt.Errorf("create TURN server: %w", err)
}
return &TURNServer{
server: s, listener: listener, port: port,
publicIP: publicIP, realm: realm,
username: username, password: password,
}, nil
}
// Close shuts down the TURN server.
func (ts *TURNServer) Close() error {
if ts.server != nil {
return ts.server.Close()
}
return nil
}
// ICEServerConfig returns the ICE server configuration for browser clients.
// host is the address the browser uses to reach this TURN server (e.g. 127.0.0.1 via SSH tunnel).
func (ts *TURNServer) ICEServerConfig(host string) map[string]any {
return map[string]any{
"urls": []string{fmt.Sprintf("turn:%s:%d?transport=tcp", host, ts.port)},
"username": ts.username,
"credential": ts.password,
}
}