mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-19 15:19:55 +00:00
Initial code
This commit is contained in:
129
client/internal/relay/turn.go
Normal file
129
client/internal/relay/turn.go
Normal file
@@ -0,0 +1,129 @@
|
||||
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)
|
||||
}
|
||||
36
client/internal/relay/turn_test.go
Normal file
36
client/internal/relay/turn_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/pion/stun/v2"
|
||||
|
||||
"github.com/netbirdio/netbird/util"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
_ = util.InitLog("trace", "console")
|
||||
code := m.Run()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func TestNewPermanentTurn(t *testing.T) {
|
||||
turnURI, err := stun.ParseURI("turns:turn.netbird.io:443?transport=tcp")
|
||||
if err != nil {
|
||||
t.Errorf("failed to parse stun url: %v", err)
|
||||
}
|
||||
turnURI.Username = "1713006060"
|
||||
turnURI.Password = "pO5Pfx15luZ92mW+FHPa6/LtJ7Y="
|
||||
|
||||
stunURI, err := stun.ParseURI("stun:stun.netbird.io:5555")
|
||||
if err != nil {
|
||||
t.Errorf("failed to parse stun url: %v", err)
|
||||
}
|
||||
turnRelay := NewPermanentTurn(stunURI, turnURI)
|
||||
err = turnRelay.Open()
|
||||
if err != nil {
|
||||
t.Errorf("failed to open turn relay: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user