137 lines
3.0 KiB
Go
137 lines
3.0 KiB
Go
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[packetAck]:
|
|
s.logf("Unexpected ACK")
|
|
return s
|
|
case controlMsg[packetProbe]:
|
|
return s.onProbe(msg)
|
|
case controlMsg[packetLocalDiscovery]:
|
|
return s
|
|
case pingTimerMsg:
|
|
return s.onPingTimer()
|
|
default:
|
|
s.logf("Unexpected 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
|
|
}
|