mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-16 15:26:40 +00:00
160 lines
4.1 KiB
Go
160 lines
4.1 KiB
Go
package relay
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/pion/logging"
|
|
"github.com/pion/turn/v2"
|
|
log "github.com/sirupsen/logrus"
|
|
"net"
|
|
)
|
|
|
|
//Client has no doc yet
|
|
type Client struct {
|
|
TurnC *turn.Client
|
|
// remote peer to reply to
|
|
peerAddr net.Addr
|
|
// local Wireguard connection
|
|
localWgConn net.Conn
|
|
}
|
|
|
|
func (c *Client) Close() error {
|
|
c.TurnC.Close()
|
|
if c.localWgConn != nil {
|
|
err := c.localWgConn.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func NewClient(turnAddr string, user string, pwd string) (*Client, error) {
|
|
// a local UDP proxy to forward Wireguard's packets to the relay server
|
|
// This endpoint should be specified in the Peer's Wireguard config
|
|
proxyConn, err := net.ListenPacket("udp4", "0.0.0.0:0")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cfg := &turn.ClientConfig{
|
|
STUNServerAddr: turnAddr,
|
|
TURNServerAddr: turnAddr,
|
|
Conn: proxyConn,
|
|
Username: user,
|
|
Password: pwd,
|
|
Realm: "wiretrustee.com",
|
|
LoggerFactory: logging.NewDefaultLoggerFactory(),
|
|
}
|
|
|
|
client, err := turn.NewClient(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Both, client and peer needs to listen to Turn packets
|
|
err = client.Listen()
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
log.Infof("local address %s", proxyConn.LocalAddr().String())
|
|
return &Client{
|
|
TurnC: client,
|
|
}, err
|
|
}
|
|
|
|
// Start relaying packets:
|
|
// Incoming traffic from the relay sent by the other peer will be forwarded to local Wireguard
|
|
// Outgoing traffic from local Wireguard will be intercepted and forwarded back to relayed connection
|
|
// returns a relayed address (turn) to be used on the other side (peer)
|
|
func (c *Client) Start(remoteAddr string, wgPort int) (*net.UDPAddr, *net.UDPAddr, error) {
|
|
|
|
udpRemoteAddr, err := net.ResolveUDPAddr("udp", remoteAddr)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Allocate a relay socket on the TURN server
|
|
relayConn, err := c.TurnC.Allocate()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
// create a connection to a local Wireguard port to forward traffic to
|
|
c.localWgConn, err = net.Dial("udp", fmt.Sprintf("127.0.0.1:%d", +wgPort))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
log.Infof("allocated a new relay address [%s]", relayConn.LocalAddr().String())
|
|
|
|
// read from relay and write to local Wireguard
|
|
c.relayPeerToLocalDst(relayConn, c.localWgConn)
|
|
// read from local Wireguard and write to relay
|
|
c.relayLocalDstToPeer(c.localWgConn, relayConn)
|
|
|
|
// Punch a UDP hole for the relayConn by sending a data to the udpRemoteAddr.
|
|
// This will trigger a TURN client to generate a permission request to the
|
|
// TURN server. After this, packets from the IP address will be accepted by
|
|
// the TURN server.
|
|
_, err = relayConn.WriteTo([]byte("Hello"), udpRemoteAddr)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
log.Infof("Punched a hole on [%s:%s]", udpRemoteAddr.IP, udpRemoteAddr.Port)
|
|
|
|
relayAddr, err := net.ResolveUDPAddr("udp", relayConn.LocalAddr().String())
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
wgAddr, err := net.ResolveUDPAddr("udp", c.localWgConn.LocalAddr().String())
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return relayAddr, wgAddr, nil
|
|
}
|
|
|
|
func (c *Client) relayPeerToLocalDst(relayConn net.PacketConn, localConn net.Conn) {
|
|
go func() {
|
|
buf := make([]byte, 1500)
|
|
var n int
|
|
var err error
|
|
for {
|
|
n, c.peerAddr, err = relayConn.ReadFrom(buf)
|
|
if err != nil {
|
|
// log.Warnln("Error reading from peer: ", err.Error())
|
|
continue
|
|
}
|
|
n, err = localConn.Write(buf[:n])
|
|
if err != nil {
|
|
log.Warnln("Error writing to local destination: ", err.Error())
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (c *Client) relayLocalDstToPeer(localConn net.Conn, relayConn net.PacketConn) {
|
|
go func() {
|
|
buf := make([]byte, 1500)
|
|
var n int
|
|
var err error
|
|
for {
|
|
n, err = localConn.Read(buf)
|
|
if err != nil {
|
|
// log.Warnln("Error reading from local destination: ", err.Error())
|
|
continue
|
|
}
|
|
if c.peerAddr == nil {
|
|
log.Warnln("We didn't received any peer connection yet")
|
|
continue
|
|
}
|
|
// log.Infoln("Received message from Local: ", string(buf[:n]))
|
|
_, err = relayConn.WriteTo(buf[:n], c.peerAddr)
|
|
if err != nil {
|
|
log.Warnln("Error writing to peer: ", err.Error())
|
|
}
|
|
}
|
|
}()
|
|
}
|