110 lines
2.2 KiB
Go
110 lines
2.2 KiB
Go
package peer
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/netip"
|
|
"strings"
|
|
"time"
|
|
"vppn/m"
|
|
|
|
"git.crumpington.com/lib/go/ratelimiter"
|
|
)
|
|
|
|
type peerState interface {
|
|
OnMsg(raw any) peerState
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
type peerData struct {
|
|
// Output.
|
|
publish func(remotePeer)
|
|
sendControlPacket func(remotePeer, marshaller)
|
|
pingTimer *time.Ticker
|
|
|
|
// Immutable data.
|
|
localIP byte
|
|
remoteIP byte
|
|
privKey []byte
|
|
localAddr netip.AddrPort // If valid, then local peer is publicly accessible.
|
|
|
|
pubAddrs *pubAddrStore
|
|
|
|
// The purpose of this state machine is to manage the RemotePeer object,
|
|
// publishing it as necessary.
|
|
staged remotePeer // Local copy of shared data. See publish().
|
|
|
|
// Mutable peer data.
|
|
peer *m.Peer
|
|
|
|
// We rate limit per remote endpoint because if we don't we tend to lose
|
|
// packets.
|
|
limiter *ratelimiter.Limiter
|
|
}
|
|
|
|
func (s *peerData) logf(format string, args ...any) {
|
|
b := strings.Builder{}
|
|
name := ""
|
|
if s.peer != nil {
|
|
name = s.peer.Name
|
|
}
|
|
b.WriteString(fmt.Sprintf("%03d", s.remoteIP))
|
|
|
|
b.WriteString(fmt.Sprintf("%30s: ", name))
|
|
|
|
if s.staged.Direct {
|
|
b.WriteString("DIRECT | ")
|
|
} else {
|
|
b.WriteString("RELAYED | ")
|
|
}
|
|
|
|
if s.staged.Up {
|
|
b.WriteString("UP | ")
|
|
} else {
|
|
b.WriteString("DOWN | ")
|
|
}
|
|
|
|
log.Printf(b.String()+format, args...)
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
func (s *peerData) SendTo(pkt marshaller, addr netip.AddrPort) {
|
|
if !addr.IsValid() {
|
|
return
|
|
}
|
|
route := s.staged
|
|
route.Direct = true
|
|
route.DirectAddr = addr
|
|
s.Send(route, pkt)
|
|
}
|
|
|
|
func (s *peerData) Send(peer remotePeer, pkt marshaller) {
|
|
if err := s.limiter.Limit(); err != nil {
|
|
s.logf("Rate limited.")
|
|
return
|
|
}
|
|
s.sendControlPacket(peer, pkt)
|
|
}
|
|
|
|
func initPeerState(data *peerData, peer *m.Peer) peerState {
|
|
data.peer = peer
|
|
|
|
if peer == nil {
|
|
return enterStateDisconnected(data)
|
|
}
|
|
|
|
if _, isValid := netip.AddrFromSlice(peer.PublicIP); isValid {
|
|
if data.localAddr.IsValid() && data.localIP < data.remoteIP {
|
|
return enterStateServer(data)
|
|
}
|
|
return enterStateClientInit(data)
|
|
}
|
|
|
|
if data.localAddr.IsValid() || data.localIP < data.remoteIP {
|
|
return enterStateServer(data)
|
|
}
|
|
return enterStateClientInit(data)
|
|
}
|