mirror of
https://github.com/netbirdio/netbird.git
synced 2025-07-13 12:55:53 +02:00
130 lines
2.7 KiB
Go
130 lines
2.7 KiB
Go
package relay
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"net"
|
|
"sync"
|
|
|
|
"github.com/pion/logging"
|
|
"github.com/pion/stun/v2"
|
|
"github.com/pion/turn/v3"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type PermanentTurn struct {
|
|
stunURI *stun.URI
|
|
turnURI *stun.URI
|
|
|
|
stunConn net.PacketConn
|
|
turnClient *turn.Client
|
|
turnClientListenLock sync.Mutex
|
|
relayConn net.PacketConn // represents the remote socket.
|
|
srvReflexiveAddress *net.UDPAddr
|
|
}
|
|
|
|
func NewPermanentTurn(stunURL, turnURL *stun.URI) *PermanentTurn {
|
|
return &PermanentTurn{
|
|
stunURI: stunURL,
|
|
turnURI: turnURL,
|
|
}
|
|
}
|
|
|
|
func (r *PermanentTurn) Open() error {
|
|
stunConn, err := net.ListenPacket("udp4", "0.0.0.0:0")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.stunConn = stunConn
|
|
|
|
cfg := &turn.ClientConfig{
|
|
STUNServerAddr: toURL(r.stunURI),
|
|
TURNServerAddr: toURL(r.turnURI),
|
|
Conn: stunConn,
|
|
Username: r.turnURI.Username,
|
|
Password: r.turnURI.Password,
|
|
LoggerFactory: logging.NewDefaultLoggerFactory(),
|
|
}
|
|
|
|
client, err := turn.NewClient(cfg)
|
|
if err != nil {
|
|
log.Errorf("failed to create turn client: %v", err)
|
|
return err
|
|
}
|
|
r.turnClient = client
|
|
r.listen()
|
|
|
|
relayConn, err := client.Allocate()
|
|
if err != nil {
|
|
log.Errorf("failed to allocate relay connection: %v", err)
|
|
return err
|
|
}
|
|
r.relayConn = relayConn
|
|
|
|
srvReflexiveAddress, err := r.discoverPublicIP()
|
|
if err != nil {
|
|
log.Errorf("failed to discover public IP: %v", err)
|
|
return err
|
|
}
|
|
r.srvReflexiveAddress = srvReflexiveAddress
|
|
return nil
|
|
}
|
|
|
|
func (r *PermanentTurn) RelayedAddress() net.Addr {
|
|
return r.relayConn.LocalAddr()
|
|
}
|
|
|
|
func (r *PermanentTurn) SrvRefAddr() net.Addr {
|
|
return r.srvReflexiveAddress
|
|
}
|
|
|
|
func (r *PermanentTurn) discoverPublicIP() (*net.UDPAddr, error) {
|
|
addr, err := r.turnClient.SendBindingRequest()
|
|
if err != nil {
|
|
log.Errorf("failed to send binding request: %v", err)
|
|
return nil, err
|
|
|
|
}
|
|
|
|
udpAddr, ok := addr.(*net.UDPAddr)
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to cast addr to udp addr")
|
|
}
|
|
|
|
return udpAddr, nil
|
|
}
|
|
|
|
func (r *PermanentTurn) listen() {
|
|
if !r.turnClientListenLock.TryLock() {
|
|
return
|
|
}
|
|
|
|
go func() {
|
|
defer r.turnClientListenLock.Unlock()
|
|
|
|
buf := make([]byte, math.MaxUint16)
|
|
for {
|
|
n, from, err := r.stunConn.ReadFrom(buf)
|
|
if err != nil {
|
|
log.Errorf("Failed to read from stun conn. Exiting loop %v", err)
|
|
break
|
|
}
|
|
|
|
_, err = r.turnClient.HandleInbound(buf[:n], from)
|
|
if err != nil {
|
|
log.Errorf("Failed to handle inbound turn message: %s. Exiting loop", err)
|
|
break
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (r *PermanentTurn) PunchHole(mappedAddr net.Addr) error {
|
|
_, err := r.relayConn.WriteTo([]byte("Hello"), mappedAddr)
|
|
return err
|
|
}
|
|
|
|
func toURL(uri *stun.URI) string {
|
|
return fmt.Sprintf("%s:%d", uri.Host, uri.Port)
|
|
}
|