package peer import ( "net/netip" "time" ) type stateServer struct { *peerData lastSeen time.Time synTraceID uint64 // Last syn trace ID. } func enterStateServer(data *peerData) peerState { data.staged.Up = false data.staged.Relay = false data.staged.Direct = false data.staged.DirectAddr = netip.AddrPort{} data.staged.PubSignKey = data.peer.PubSignKey data.staged.ControlCipher = newControlCipher(data.privKey, data.peer.PubKey) data.staged.DataCipher = nil data.publish(data.staged) data.pingTimer.Reset(pingInterval) state := &stateServer{ peerData: data, lastSeen: time.Now(), } state.logf("==> Server") return state } func (s *stateServer) logf(str string, args ...any) { s.peerData.logf("SRVR | "+str, args...) } func (s *stateServer) OnMsg(raw any) peerState { switch msg := raw.(type) { case peerUpdateMsg: return initPeerState(s.peerData, msg.Peer) case controlMsg[packetInit]: return s.onInit(msg) case controlMsg[packetSyn]: return s.onSyn(msg) case controlMsg[packetProbe]: return s.onProbe(msg) case controlMsg[packetLocalDiscovery]: return s case pingTimerMsg: return s.onPingTimer() default: s.logf("Ignoring message: %v", raw) return s } } func (s *stateServer) onInit(msg controlMsg[packetInit]) peerState { s.staged.Up = false s.staged.Direct = msg.Packet.Direct s.staged.DirectAddr = msg.SrcAddr s.publish(s.staged) init := packetInit{ TraceID: msg.Packet.TraceID, Direct: s.staged.Direct, Version: version, } s.Send(s.staged, init) return s } func (s *stateServer) onSyn(msg controlMsg[packetSyn]) peerState { s.lastSeen = time.Now() p := msg.Packet // Before we can respond to this packet, we need to make sure the // route is setup properly. // // The client will update the syn's TraceID whenever there's a change. // The server will follow the client's request. if p.TraceID != s.synTraceID || !s.staged.Up { s.synTraceID = p.TraceID s.staged.Up = true s.staged.Direct = p.Direct s.staged.DataCipher = newDataCipherFromKey(p.SharedKey) s.staged.DirectAddr = msg.SrcAddr s.publish(s.staged) s.logf("Got SYN.") } // Always respond. s.Send(s.staged, packetAck{ TraceID: p.TraceID, ToAddr: s.staged.DirectAddr, PossibleAddrs: s.pubAddrs.Get(), }) if p.Direct { return s } // Send probes if not a direct connection. for _, addr := range msg.Packet.PossibleAddrs { if !addr.IsValid() { break } s.logf("Probing %v...", addr) s.SendTo(packetProbe{TraceID: newTraceID()}, addr) } return s } func (s *stateServer) onProbe(msg controlMsg[packetProbe]) peerState { if msg.SrcAddr.IsValid() { s.logf("Probe response %v...", msg.SrcAddr) s.SendTo(packetProbe{TraceID: msg.Packet.TraceID}, msg.SrcAddr) } return s } func (s *stateServer) onPingTimer() peerState { if time.Since(s.lastSeen) > timeoutInterval && s.staged.Up { s.staged.Up = false s.publish(s.staged) s.logf("Timeout.") } return s }