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