Files
netbird/relay/client.go
2021-04-14 14:20:25 +02:00

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())
}
}
}()
}