netbird/client/internal/peer/conn.go

720 lines
20 KiB
Go
Raw Normal View History

package peer
import (
"context"
2024-06-25 15:13:08 +02:00
"math/rand"
"net"
2024-06-26 15:25:32 +02:00
"os"
Feature/add iOS support (#1244) * starting engine by passing file descriptor on engine start * inject logger that does not compile * logger and first client * first working connection * support for routes and working connection * small refactor for better code quality in swift * trying to add DNS * fix * updated * fix route deletion * trying to bind the DNS resolver dialer to an interface * use dns.Client.Exchange * fix metadata send on startup * switching between client to query upstream * fix panic on no dns response * fix after merge changes * add engine ready listener * replace engine listener with connection listener * disable relay connection for iOS until proxy is refactored into bind * Extract private upstream for iOS and fix function headers for other OS * Update mock Server * Fix dns server and upstream tests * Fix engine null pointer with mobile dependencies for other OS * Revert back to disabling upstream on no response * Fix some of the remarks from the linter * Fix linter * re-arrange duration calculation * revert exported HostDNSConfig * remove unused engine listener * remove development logs * refactor dns code and interface name propagation * clean dns server test * disable upstream deactivation for iOS * remove files after merge * fix dns server darwin * fix server mock * fix build flags * move service listen back to initialize * add wgInterface to hostManager initialization on android * fix typo and remove unused function * extract upstream exchange for ios and rest * remove todo * separate upstream logic to ios file * Fix upstream test * use interface and embedded struct for upstream * set properly upstream client * remove placeholder * remove ios specific attributes * fix upstream test * merge ipc parser and wg configurer for mobile * fix build annotation * use json for DNS settings handover through gomobile * add logs for DNS json string * bring back check on ios for private upstream * remove wrong (and unused) line * fix wrongly updated comments on DNSSetting export --------- Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
2023-12-18 11:46:58 +01:00
"runtime"
2022-06-04 20:15:41 +02:00
"strings"
"sync"
"time"
2024-07-11 14:37:22 +02:00
"github.com/cenkalti/backoff/v4"
"github.com/pion/ice/v3"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/internal/stdnet"
"github.com/netbirdio/netbird/client/internal/wgproxy"
"github.com/netbirdio/netbird/iface"
relayClient "github.com/netbirdio/netbird/relay/client"
"github.com/netbirdio/netbird/route"
nbnet "github.com/netbirdio/netbird/util/net"
)
type ConnPriority int
const (
defaultWgKeepAlive = 25 * time.Second
connPriorityRelay ConnPriority = 1
2024-06-25 15:13:08 +02:00
connPriorityICETurn ConnPriority = 1
connPriorityICEP2P ConnPriority = 2
2023-04-28 16:26:54 +02:00
)
type WgConfig struct {
WgListenPort int
RemoteKey string
2024-07-08 16:12:08 +02:00
WgInterface iface.IWGIface
AllowedIps string
PreSharedKey *wgtypes.Key
}
// ConnConfig is a peer Connection configuration
type ConnConfig struct {
// Key is a public key of a remote peer
Key string
// LocalKey is a public key of a local peer
LocalKey string
Timeout time.Duration
WgConfig WgConfig
LocalWgPort int
// RosenpassPubKey is this peer's Rosenpass public key
RosenpassPubKey []byte
// RosenpassPubKey is this peer's RosenpassAddr server address (IP:port)
RosenpassAddr string
// ICEConfig ICE protocol configuration
ICEConfig ICEConfig
}
2024-06-19 11:52:40 +02:00
type WorkerCallbacks struct {
OnRelayReadyCallback func(info RelayConnInfo)
OnRelayStatusChanged func(ConnStatus)
OnICEConnReadyCallback func(ConnPriority, ICEConnInfo)
OnICEStatusChanged func(ConnStatus)
}
type Conn struct {
log *log.Entry
mu sync.Mutex
ctx context.Context
ctxCancel context.CancelFunc
config ConnConfig
statusRecorder *Status
wgProxyFactory *wgproxy.Factory
2024-06-20 18:17:30 +02:00
wgProxyICE wgproxy.Proxy
wgProxyRelay wgproxy.Proxy
2024-06-17 18:02:52 +02:00
signaler *Signaler
2024-06-25 15:13:08 +02:00
relayManager *relayClient.Manager
allowedIPsIP string
handshaker *Handshaker
onConnected func(remoteWireGuardKey string, remoteRosenpassPubKey []byte, wireGuardIP string, remoteRosenpassAddr string)
onDisconnected func(remotePeer string, wgIP string)
2024-06-21 19:13:41 +02:00
statusRelay ConnStatus
statusICE ConnStatus
currentConnType ConnPriority
2024-07-08 16:12:08 +02:00
opened bool // this flag is used to prevent close in case of not opened connection
2024-06-18 11:22:40 +02:00
workerICE *WorkerICE
workerRelay *WorkerRelay
connID nbnet.ConnectionID
beforeAddPeerHooks []nbnet.AddHookFunc
afterRemovePeerHooks []nbnet.RemoveHookFunc
2024-06-20 18:17:30 +02:00
endpointRelay *net.UDPAddr
2024-06-25 15:13:08 +02:00
2024-07-11 14:37:22 +02:00
// for reconnection operations
iCEDisconnected chan bool
relayDisconnected chan bool
}
// NewConn creates a new not opened Conn to the remote peer.
// To establish a connection run Conn.Open
2024-06-17 18:02:52 +02:00
func NewConn(engineCtx context.Context, config ConnConfig, statusRecorder *Status, wgProxyFactory *wgproxy.Factory, signaler *Signaler, iFaceDiscover stdnet.ExternalIFaceDiscover, relayManager *relayClient.Manager) (*Conn, error) {
_, allowedIPsIP, err := net.ParseCIDR(config.WgConfig.AllowedIps)
if err != nil {
log.Errorf("failed to parse allowedIPS: %v", err)
return nil, err
}
ctx, ctxCancel := context.WithCancel(engineCtx)
connLog := log.WithField("peer", config.Key)
var conn = &Conn{
2024-06-25 15:13:08 +02:00
log: connLog,
ctx: ctx,
ctxCancel: ctxCancel,
config: config,
statusRecorder: statusRecorder,
wgProxyFactory: wgProxyFactory,
signaler: signaler,
relayManager: relayManager,
allowedIPsIP: allowedIPsIP.String(),
statusRelay: StatusDisconnected,
statusICE: StatusDisconnected,
2024-07-11 14:37:22 +02:00
iCEDisconnected: make(chan bool, 1),
relayDisconnected: make(chan bool, 1),
}
2024-06-19 11:52:40 +02:00
rFns := WorkerRelayCallbacks{
2024-06-25 15:13:08 +02:00
OnConnReady: conn.relayConnectionIsReady,
OnDisconnected: conn.onWorkerRelayStateDisconnected,
2024-06-19 11:52:40 +02:00
}
wFns := WorkerICECallbacks{
OnConnReady: conn.iCEConnectionIsReady,
2024-06-25 15:13:08 +02:00
OnStatusChanged: conn.onWorkerICEStateDisconnected,
2024-06-19 11:52:40 +02:00
}
2024-06-21 15:35:15 +02:00
conn.handshaker = NewHandshaker(ctx, connLog, config, signaler)
conn.workerRelay = NewWorkerRelay(ctx, connLog, config, relayManager, rFns)
2024-06-25 15:13:08 +02:00
relayIsSupportedLocally := conn.workerRelay.RelayIsSupportedLocally()
conn.workerICE, err = NewWorkerICE(ctx, connLog, config, config.ICEConfig, signaler, iFaceDiscover, statusRecorder, relayIsSupportedLocally, wFns)
2024-06-21 12:35:28 +02:00
if err != nil {
return nil, err
}
2024-06-21 15:35:15 +02:00
conn.handshaker.AddOnNewOfferListener(conn.workerRelay.OnNewOffer)
2024-06-26 15:25:32 +02:00
if os.Getenv("NB_FORCE_RELAY") != "true" {
conn.handshaker.AddOnNewOfferListener(conn.workerICE.OnNewOffer)
}
2024-06-21 15:35:15 +02:00
go conn.handshaker.Listen()
return conn, nil
}
// Open opens connection to the remote peer
// It will try to establish a connection using ICE and in parallel with relay. The higher priority connection type will
// be used.
func (conn *Conn) Open() {
conn.log.Debugf("open connection to peer")
2024-07-08 16:12:08 +02:00
conn.mu.Lock()
defer conn.mu.Unlock()
conn.opened = true
peerState := State{
PubKey: conn.config.Key,
IP: strings.Split(conn.config.WgConfig.AllowedIps, "/")[0],
ConnStatusUpdate: time.Now(),
ConnStatus: StatusDisconnected,
Mux: new(sync.RWMutex),
}
err := conn.statusRecorder.UpdatePeerState(peerState)
if err != nil {
conn.log.Warnf("error while updating the state err: %v", err)
}
2024-07-11 14:37:22 +02:00
conn.waitInitialRandomSleepTime()
2024-06-25 15:13:08 +02:00
err = conn.doHandshake()
if err != nil {
conn.log.Errorf("failed to send offer: %v", err)
}
2024-07-11 14:37:22 +02:00
if conn.workerRelay.IsController() {
go conn.reconnectLoopWithRetry()
} else {
go conn.reconnectLoopForOnDisconnectedEvent()
}
}
// Close closes this peer Conn issuing a close event to the Conn closeCh
func (conn *Conn) Close() {
conn.mu.Lock()
2024-06-18 11:27:18 +02:00
defer conn.mu.Unlock()
2024-06-25 15:13:08 +02:00
conn.ctxCancel()
2024-07-08 16:12:08 +02:00
if !conn.opened {
log.Infof("IGNORE close connection to peer")
return
}
2024-06-20 18:17:30 +02:00
if conn.wgProxyRelay != nil {
err := conn.wgProxyRelay.CloseConn()
if err != nil {
conn.log.Errorf("failed to close wg proxy for relay: %v", err)
}
conn.wgProxyRelay = nil
}
if conn.wgProxyICE != nil {
err := conn.wgProxyICE.CloseConn()
if err != nil {
2024-06-20 18:17:30 +02:00
conn.log.Errorf("failed to close wg proxy for ice: %v", err)
}
2024-06-20 18:17:30 +02:00
conn.wgProxyICE = nil
}
err := conn.config.WgConfig.WgInterface.RemovePeer(conn.config.WgConfig.RemoteKey)
if err != nil {
conn.log.Errorf("failed to remove wg endpoint: %v", err)
}
if conn.connID != "" {
for _, hook := range conn.afterRemovePeerHooks {
if err := hook(conn.connID); err != nil {
conn.log.Errorf("After remove peer hook failed: %v", err)
}
}
conn.connID = ""
}
if conn.evalStatus() == StatusConnected && conn.onDisconnected != nil {
conn.onDisconnected(conn.config.WgConfig.RemoteKey, conn.config.WgConfig.AllowedIps)
}
conn.statusRelay = StatusDisconnected
conn.statusICE = StatusDisconnected
peerState := State{
PubKey: conn.config.Key,
ConnStatus: StatusDisconnected,
ConnStatusUpdate: time.Now(),
Mux: new(sync.RWMutex),
}
err = conn.statusRecorder.UpdatePeerState(peerState)
if err != nil {
// pretty common error because by that time Engine can already remove the peer and status won't be available.
// todo rethink status updates
conn.log.Debugf("error while updating peer's state, err: %v", err)
}
if err := conn.statusRecorder.UpdateWireGuardPeerState(conn.config.Key, iface.WGStats{}); err != nil {
conn.log.Debugf("failed to reset wireguard stats for peer: %s", err)
}
}
// OnRemoteAnswer handles an offer from the remote peer and returns true if the message was accepted, false otherwise
// doesn't block, discards the message if connection wasn't ready
func (conn *Conn) OnRemoteAnswer(answer OfferAnswer) bool {
conn.log.Debugf("OnRemoteAnswer, status ICE: %s, status relay: %s", conn.statusICE, conn.statusRelay)
return conn.handshaker.OnRemoteAnswer(answer)
}
// OnRemoteCandidate Handles ICE connection Candidate provided by the remote peer.
func (conn *Conn) OnRemoteCandidate(candidate ice.Candidate, haRoutes route.HAMap) {
2024-06-18 11:22:40 +02:00
conn.workerICE.OnRemoteCandidate(candidate, haRoutes)
}
func (conn *Conn) AddBeforeAddPeerHook(hook nbnet.AddHookFunc) {
conn.beforeAddPeerHooks = append(conn.beforeAddPeerHooks, hook)
}
func (conn *Conn) AddAfterRemovePeerHook(hook nbnet.RemoveHookFunc) {
conn.afterRemovePeerHooks = append(conn.afterRemovePeerHooks, hook)
}
// SetOnConnected sets a handler function to be triggered by Conn when a new connection to a remote peer established
func (conn *Conn) SetOnConnected(handler func(remoteWireGuardKey string, remoteRosenpassPubKey []byte, wireGuardIP string, remoteRosenpassAddr string)) {
conn.onConnected = handler
}
// SetOnDisconnected sets a handler function to be triggered by Conn when a connection to a remote disconnected
func (conn *Conn) SetOnDisconnected(handler func(remotePeer string, wgIP string)) {
conn.onDisconnected = handler
}
func (conn *Conn) OnRemoteOffer(offer OfferAnswer) bool {
2024-06-25 15:13:08 +02:00
conn.log.Debugf("OnRemoteOffer, on status ICE: %s, status Relay: %s", conn.statusICE, conn.statusRelay)
return conn.handshaker.OnRemoteOffer(offer)
}
// WgConfig returns the WireGuard config
func (conn *Conn) WgConfig() WgConfig {
return conn.config.WgConfig
}
// Status returns current status of the Conn
func (conn *Conn) Status() ConnStatus {
conn.mu.Lock()
defer conn.mu.Unlock()
return conn.evalStatus()
}
func (conn *Conn) GetKey() string {
return conn.config.Key
}
2024-07-11 14:37:22 +02:00
func (conn *Conn) reconnectLoopWithRetry() {
2024-07-11 15:37:34 +02:00
// give chance to the peer to establish the initial connection
2024-07-11 14:37:22 +02:00
select {
case <-conn.ctx.Done():
case <-time.After(3 * time.Second):
2024-06-25 15:13:08 +02:00
}
2024-07-11 14:37:22 +02:00
bo := backoff.WithContext(&backoff.ExponentialBackOff{
InitialInterval: 800 * time.Millisecond,
RandomizationFactor: 0,
2024-07-11 15:46:07 +02:00
Multiplier: 1.99,
2024-07-11 14:37:22 +02:00
MaxInterval: conn.config.Timeout * time.Second,
MaxElapsedTime: 0,
Stop: backoff.Stop,
Clock: backoff.SystemClock,
}, conn.ctx)
ticker := backoff.NewTicker(bo)
defer ticker.Stop()
2024-07-11 15:46:07 +02:00
<-ticker.C // consume the initial tick what is happening right after the ticker has been created
2024-07-11 14:37:22 +02:00
no := time.Now()
2024-06-25 15:13:08 +02:00
for {
select {
2024-07-11 15:46:07 +02:00
case t := <-ticker.C:
if t.IsZero() {
// in case if the ticker has been canceled by context then avoid the temporary loop
return
}
2024-06-25 15:13:08 +02:00
// checks if there is peer connection is established via relay or ice and that it has a wireguard handshake and skip offer
// todo check wg handshake
2024-07-11 14:37:22 +02:00
conn.log.Tracef("ticker timedout, relay state: %s, ice state: %s, elapsed time: %s", conn.statusRelay, conn.statusICE, time.Since(no))
no = time.Now()
2024-06-25 15:13:08 +02:00
if conn.statusRelay == StatusConnected && conn.statusICE == StatusConnected {
continue
}
2024-07-11 14:37:22 +02:00
log.Debugf("ticker timed out, retry to do handshake")
err := conn.doHandshake()
if err != nil {
conn.log.Errorf("failed to do handshake: %v", err)
}
case changed := <-conn.relayDisconnected:
if !changed {
continue
}
conn.log.Debugf("Relay state changed, reset reconnect timer")
bo.Reset()
case changed := <-conn.iCEDisconnected:
if !changed {
continue
}
conn.log.Debugf("ICE state changed, reset reconnect timer")
bo.Reset()
case <-conn.ctx.Done():
return
}
}
}
2024-07-11 15:37:07 +02:00
// reconnectLoopForOnDisconnectedEvent is used when the peer is not a controller and it should reconnect to the peer
// when the connection is lost. It will try to establish a connection only once time if before the connection was established
// It track separately the ice and relay connection status. Just because a lover priority connection reestablished it does not
// mean that to switch to it. We always force to use the higher priority connection.
2024-07-11 14:37:22 +02:00
func (conn *Conn) reconnectLoopForOnDisconnectedEvent() {
for {
select {
case changed := <-conn.relayDisconnected:
if !changed {
continue
}
conn.log.Debugf("Relay state changed, try to send new offer")
case changed := <-conn.iCEDisconnected:
if !changed {
continue
}
conn.log.Debugf("ICE state changed, try to send new offer")
2024-06-25 15:13:08 +02:00
case <-conn.ctx.Done():
return
}
err := conn.doHandshake()
if err != nil {
conn.log.Errorf("failed to do handshake: %v", err)
}
}
}
// configureConnection starts proxying traffic from/to local Wireguard and sets connection status to StatusConnected
func (conn *Conn) iCEConnectionIsReady(priority ConnPriority, iceConnInfo ICEConnInfo) {
conn.mu.Lock()
defer conn.mu.Unlock()
2024-06-25 15:13:08 +02:00
if conn.ctx.Err() != nil {
return
}
2024-06-25 15:13:08 +02:00
conn.log.Debugf("ICE connection is ready")
conn.statusICE = StatusConnected
if conn.currentConnType > priority {
return
}
2024-06-25 15:13:08 +02:00
if conn.currentConnType != 0 {
conn.log.Infof("update connection to ICE type")
} else {
conn.log.Infof("set ICE to active connection")
}
var (
endpoint net.Addr
wgProxy wgproxy.Proxy
)
if iceConnInfo.RelayedOnLocal {
conn.log.Debugf("setup ice turn connection")
wgProxy = conn.wgProxyFactory.GetProxy(conn.ctx)
2024-07-10 16:51:38 +02:00
ep, err := wgProxy.AddTurnConn(iceConnInfo.RemoteConn)
2024-06-20 18:17:30 +02:00
if err != nil {
2024-06-25 15:13:08 +02:00
conn.log.Errorf("failed to add turn net.Conn to local proxy: %v", err)
2024-07-10 16:51:38 +02:00
err = wgProxy.CloseConn()
if err != nil {
conn.log.Warnf("failed to close turn proxy connection: %v", err)
}
2024-06-25 15:13:08 +02:00
return
2024-06-20 18:17:30 +02:00
}
2024-06-25 15:13:08 +02:00
endpoint = ep
} else {
endpoint = iceConnInfo.RemoteConn.RemoteAddr()
}
endpointUdpAddr, _ := net.ResolveUDPAddr(endpoint.Network(), endpoint.String())
conn.log.Debugf("Conn resolved IP is %s for endopint %s", endpoint, endpointUdpAddr.IP)
conn.connID = nbnet.GenerateConnID()
for _, hook := range conn.beforeAddPeerHooks {
if err := hook(conn.connID, endpointUdpAddr.IP); err != nil {
conn.log.Errorf("Before add peer hook failed: %v", err)
}
}
err := conn.config.WgConfig.WgInterface.UpdatePeer(conn.config.WgConfig.RemoteKey, conn.config.WgConfig.AllowedIps, defaultWgKeepAlive, endpointUdpAddr, conn.config.WgConfig.PreSharedKey)
if err != nil {
if wgProxy != nil {
if err := wgProxy.CloseConn(); err != nil {
conn.log.Warnf("Failed to close turn connection: %v", err)
}
}
conn.log.Warnf("Failed to update wg peer configuration: %v", err)
2024-06-20 18:17:30 +02:00
return
}
2024-06-25 15:13:08 +02:00
if conn.wgProxyICE != nil {
if err := conn.wgProxyICE.CloseConn(); err != nil {
conn.log.Warnf("failed to close depracated wg proxy conn: %v", err)
}
}
2024-06-25 15:13:08 +02:00
conn.wgProxyICE = wgProxy
conn.currentConnType = priority
peerState := State{
LocalIceCandidateType: iceConnInfo.LocalIceCandidateType,
RemoteIceCandidateType: iceConnInfo.RemoteIceCandidateType,
LocalIceCandidateEndpoint: iceConnInfo.LocalIceCandidateEndpoint,
RemoteIceCandidateEndpoint: iceConnInfo.RemoteIceCandidateEndpoint,
Direct: iceConnInfo.Direct,
Relayed: iceConnInfo.Relayed,
}
conn.updateStatus(peerState, iceConnInfo.RosenpassPubKey, iceConnInfo.RosenpassAddr)
}
2024-06-25 15:13:08 +02:00
// todo review to make sense to handle connection and disconnected status also?
func (conn *Conn) onWorkerICEStateDisconnected(newState ConnStatus) {
conn.mu.Lock()
defer conn.mu.Unlock()
2024-06-25 15:13:08 +02:00
conn.log.Tracef("ICE connection state changed to %s", newState)
defer func() {
2024-07-11 14:37:22 +02:00
changed := conn.statusICE != newState && newState != StatusConnecting
2024-06-25 15:13:08 +02:00
conn.statusICE = newState
select {
2024-07-11 14:37:22 +02:00
case conn.iCEDisconnected <- changed:
2024-06-25 15:13:08 +02:00
default:
}
}()
2024-06-25 15:13:08 +02:00
// switch back to relay connection
if conn.endpointRelay != nil {
conn.log.Debugf("ICE disconnected, set Relay to active connection")
err := conn.configureWGEndpoint(conn.endpointRelay)
if err != nil {
conn.log.Errorf("failed to switch to relay conn: %v", err)
}
// todo update status to relay related things
return
}
2024-06-20 18:17:30 +02:00
2024-06-25 15:13:08 +02:00
if conn.statusRelay == StatusConnected {
return
}
if conn.evalStatus() == newState {
return
}
2024-06-25 15:13:08 +02:00
if newState > conn.statusICE {
peerState := State{
PubKey: conn.config.Key,
ConnStatus: newState,
ConnStatusUpdate: time.Now(),
Mux: new(sync.RWMutex),
}
_ = conn.statusRecorder.UpdatePeerState(peerState)
}
}
func (conn *Conn) relayConnectionIsReady(rci RelayConnInfo) {
conn.mu.Lock()
defer conn.mu.Unlock()
if conn.ctx.Err() != nil {
return
}
2024-06-25 15:13:08 +02:00
conn.log.Debugf("Relay connection is ready to use")
conn.statusRelay = StatusConnected
wgProxy := conn.wgProxyFactory.GetProxy(conn.ctx)
endpoint, err := wgProxy.AddTurnConn(rci.relayedConn)
2022-07-02 12:02:17 +02:00
if err != nil {
conn.log.Errorf("failed to add relayed net.Conn to local proxy: %v", err)
return
2022-07-02 12:02:17 +02:00
}
endpointUdpAddr, _ := net.ResolveUDPAddr(endpoint.Network(), endpoint.String())
2024-06-25 15:13:08 +02:00
conn.endpointRelay = endpointUdpAddr
conn.log.Debugf("conn resolved IP for %s: %s", endpoint, endpointUdpAddr.IP)
2024-06-25 15:13:08 +02:00
if conn.currentConnType > connPriorityRelay {
if conn.statusICE == StatusConnected {
log.Debugf("do not switch to relay because current priority is: %v", conn.currentConnType)
return
}
}
conn.connID = nbnet.GenerateConnID()
for _, hook := range conn.beforeAddPeerHooks {
if err := hook(conn.connID, endpointUdpAddr.IP); err != nil {
conn.log.Errorf("Before add peer hook failed: %v", err)
}
}
2024-06-20 18:17:30 +02:00
err = conn.configureWGEndpoint(endpointUdpAddr)
if err != nil {
if err := wgProxy.CloseConn(); err != nil {
conn.log.Warnf("Failed to close relay connection: %v", err)
}
conn.log.Errorf("Failed to update wg peer configuration: %v", err)
return
}
2024-06-20 18:17:30 +02:00
if conn.wgProxyRelay != nil {
if err := conn.wgProxyRelay.CloseConn(); err != nil {
conn.log.Warnf("failed to close depracated wg proxy conn: %v", err)
}
}
2024-06-20 18:17:30 +02:00
conn.wgProxyRelay = wgProxy
conn.currentConnType = connPriorityRelay
peerState := State{
Direct: false,
Relayed: true,
}
2024-06-25 15:13:08 +02:00
conn.log.Infof("start to communicate with peer via relay")
conn.updateStatus(peerState, rci.rosenpassPubKey, rci.rosenpassAddr)
}
2024-06-25 15:13:08 +02:00
func (conn *Conn) onWorkerRelayStateDisconnected() {
conn.mu.Lock()
defer conn.mu.Unlock()
2024-06-25 15:13:08 +02:00
defer func() {
2024-07-11 14:37:22 +02:00
changed := conn.statusRelay != StatusDisconnected
2024-06-25 15:13:08 +02:00
conn.statusRelay = StatusDisconnected
2024-06-25 15:13:08 +02:00
select {
2024-07-11 14:37:22 +02:00
case conn.relayDisconnected <- changed:
2024-06-25 15:13:08 +02:00
default:
}
2024-06-25 15:13:08 +02:00
}()
2024-06-25 15:13:08 +02:00
if conn.wgProxyRelay != nil {
conn.endpointRelay = nil
_ = conn.wgProxyRelay.CloseConn()
conn.wgProxyRelay = nil
}
2024-06-25 15:13:08 +02:00
if conn.statusICE == StatusConnected {
return
}
2024-06-25 15:13:08 +02:00
if conn.evalStatus() == StatusDisconnected {
return
}
2024-06-25 15:13:08 +02:00
if StatusDisconnected > conn.statusRelay {
peerState := State{
PubKey: conn.config.Key,
ConnStatus: StatusDisconnected,
ConnStatusUpdate: time.Now(),
Mux: new(sync.RWMutex),
}
_ = conn.statusRecorder.UpdatePeerState(peerState)
}
}
2024-06-20 18:17:30 +02:00
func (conn *Conn) configureWGEndpoint(addr *net.UDPAddr) error {
return conn.config.WgConfig.WgInterface.UpdatePeer(
conn.config.WgConfig.RemoteKey,
conn.config.WgConfig.AllowedIps,
defaultWgKeepAlive,
addr,
conn.config.WgConfig.PreSharedKey,
)
}
func (conn *Conn) updateStatus(peerState State, remoteRosenpassPubKey []byte, remoteRosenpassAddr string) {
peerState.PubKey = conn.config.Key
peerState.ConnStatus = StatusConnected
peerState.ConnStatusUpdate = time.Now()
peerState.RosenpassEnabled = isRosenpassEnabled(remoteRosenpassPubKey)
peerState.Mux = new(sync.RWMutex)
err := conn.statusRecorder.UpdatePeerState(peerState)
if err != nil {
conn.log.Warnf("unable to save peer's state, got error: %v", err)
}
if runtime.GOOS == "ios" {
runtime.GC()
}
if conn.onConnected != nil {
conn.onConnected(conn.config.Key, remoteRosenpassPubKey, conn.allowedIPsIP, remoteRosenpassAddr)
}
}
2024-06-21 12:35:28 +02:00
func (conn *Conn) doHandshake() error {
if !conn.signaler.Ready() {
2024-06-21 12:35:28 +02:00
return ErrSignalIsNotReady
}
var (
ha HandshakeArgs
err error
)
2024-06-21 12:35:28 +02:00
ha.IceUFrag, ha.IcePwd = conn.workerICE.GetLocalUserCredentials()
2024-07-02 11:57:17 +02:00
addr, err := conn.workerRelay.RelayInstanceAddress()
if err == nil {
2024-07-02 11:57:17 +02:00
ha.RelayAddr = addr
}
2024-07-11 14:37:22 +02:00
conn.log.Tracef("do handshake with args: %v", ha)
2024-06-21 12:35:28 +02:00
return conn.handshaker.SendOffer(ha)
}
2024-07-11 14:37:22 +02:00
func (conn *Conn) waitInitialRandomSleepTime() {
2024-07-11 15:37:07 +02:00
minWait := 100
maxWait := 800
2024-06-25 15:13:08 +02:00
duration := time.Duration(rand.Intn(maxWait-minWait)+minWait) * time.Millisecond
timeout := time.NewTimer(duration)
defer timeout.Stop()
select {
case <-conn.ctx.Done():
case <-timeout.C:
}
}
func (conn *Conn) evalStatus() ConnStatus {
if conn.statusRelay == StatusConnected || conn.statusICE == StatusConnected {
return StatusConnected
}
if conn.statusRelay == StatusConnecting || conn.statusICE == StatusConnecting {
return StatusConnecting
}
return StatusDisconnected
}
func isRosenpassEnabled(remoteRosenpassPubKey []byte) bool {
return remoteRosenpassPubKey != nil
}