vppn/peer/connstate.go
2024-12-14 07:38:06 +01:00

255 lines
5.7 KiB
Go

package peer
import (
"log"
"net/netip"
"time"
"vppn/m"
)
func logState(s connState, msg string, args ...any) {
log.Printf("["+s.Name()+"] "+msg, args...)
}
// The connection state corresponds to what we're connected TO.
type connState interface {
Name() string
HandlePacket(wrapper) connState
HandleTimeout() connState
}
// Helper functions.
func newStateFromPeerUpdate(update peerUpdate, data *connData) connState {
if update.Peer != nil {
return newStateFromPeer(update.Peer, data)
}
return newConnNull(data)
}
func newStateFromPeer(peer *m.Peer, data *connData) connState {
if _, isPublic := netip.AddrFromSlice(peer.PublicIP); isPublic {
return newStateServerDown(data, peer)
} else if data.server {
return newStateClientDown(data, peer)
} else {
return newStateMediated(data, peer)
}
}
/////////////////////
// Null Connection //
/////////////////////
type connNull struct{ *connData }
func newConnNull(data *connData) connState {
c := connNull{data}
c.peer = nil
c.encSharedKey = nil
c.publicAddr = netip.AddrPort{}
c.pingTimer.Stop()
c.timeoutTimer.Stop()
c.addr = c.publicAddr
c.useMediator = false
c.up = false
c.route.Store(nil)
return c
}
func (c connNull) Name() string { return "NoPeer" }
func (c connNull) HandlePacket(w wrapper) connState { return c }
func (c connNull) HandleTimeout() connState {
logState(c, "Unexpected timeout.")
return c
}
////////////////////////
// Unconnected Server //
////////////////////////
type stateServerDown struct{ *connData }
func newStateServerDown(data *connData, peer *m.Peer) connState {
addr, _ := netip.AddrFromSlice(peer.PublicIP)
pubAddr := netip.AddrPortFrom(addr, peer.Port)
c := stateServerDown{data}
c.peer = peer
c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey)
c.publicAddr = pubAddr
c.pingTimer.Reset(time.Second) // Ping right away to bring up.
c.timeoutTimer.Stop() // No timeouts yet.
c.addr = c.publicAddr
c.useMediator = false
c.up = false
c.route.Store(c.Route())
return c
}
func (c stateServerDown) Name() string { return "Server:DOWN" }
func (c stateServerDown) HandlePacket(w wrapper) connState {
switch p := w.Packet.(type) {
case *Pong:
return newStateServerUp(c.connData, w, p)
}
return c
}
func (c stateServerDown) HandleTimeout() connState {
logState(c, "Unexpected timeout.")
return c
}
//////////////////////
// Connected Server //
//////////////////////
type stateServerUp struct{ *connData }
func newStateServerUp(data *connData, w wrapper, pong *Pong) connState {
c := stateServerUp{data}
c.pingTimer.Reset(pingInterval)
c.timeoutTimer.Reset(timeoutInterval)
c.addr = w.SrcAddr
c.useMediator = false
c.up = true
c.route.Store(c.Route())
return c
}
func (c stateServerUp) Name() string { return "Server:UP" }
func (c stateServerUp) HandlePacket(w wrapper) connState {
switch w.Packet.(type) {
case *Pong:
c.timeoutTimer.Reset(timeoutInterval)
}
return c
}
func (c stateServerUp) HandleTimeout() connState {
return newStateFromPeer(c.peer, c.connData)
}
////////////////////////
// Unconnected Client //
////////////////////////
type stateClientDown struct{ *connData }
func newStateClientDown(data *connData, peer *m.Peer) connState {
addr, _ := netip.AddrFromSlice(peer.PublicIP)
pubAddr := netip.AddrPortFrom(addr, peer.Port)
c := stateClientDown{data}
c.peer = peer
c.publicAddr = pubAddr
c.encPrivKey = data.encPrivKey
c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey)
c.addr = c.publicAddr
c.useMediator = false
c.up = false
c.route.Store(c.Route())
c.pingTimer.Stop() // Conncection is from client => pings incoming.
c.timeoutTimer.Stop() // No timeouts yet.
return c
}
func (c stateClientDown) Name() string { return "Client:DOWN" }
func (c stateClientDown) HandlePacket(w wrapper) connState {
switch w.Packet.(type) {
case *Ping:
next := newStateClientUp(c.connData, w)
c.sendPong(w)
return next
}
return c
}
func (c stateClientDown) HandleTimeout() connState {
logState(c, "Unexpected timeout.")
return c
}
//////////////////////
// Connected Client //
//////////////////////
type stateClientUp struct{ *connData }
func newStateClientUp(data *connData, w wrapper) connState {
c := stateClientUp{data}
c.addr = w.SrcAddr
c.useMediator = false
c.up = true
c.route.Store(c.Route())
c.pingTimer.Stop() // Conncection is from client => pings incoming.
c.timeoutTimer.Reset(timeoutInterval)
return c
}
func (c stateClientUp) Name() string { return "Client:UP" }
func (c stateClientUp) HandlePacket(w wrapper) connState {
switch w.Packet.(type) {
case *Ping:
// The connection is from a client. If the client's address changes, we
// should follow that change.
if c.addr != w.SrcAddr {
c.addr = w.SrcAddr
c.route.Store(c.Route())
}
c.sendPong(w)
c.timeoutTimer.Reset(timeoutInterval)
}
return c
}
func (c stateClientUp) HandleTimeout() connState {
return newStateFromPeer(c.peer, c.connData)
}
//////////////
// Mediated //
//////////////
type stateMediated struct{ *connData }
func newStateMediated(data *connData, peer *m.Peer) connState {
addr, _ := netip.AddrFromSlice(peer.PublicIP)
pubAddr := netip.AddrPortFrom(addr, peer.Port)
c := stateMediated{data}
c.peer = peer
c.publicAddr = pubAddr
c.encPrivKey = data.encPrivKey
c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey)
c.addr = c.publicAddr
c.useMediator = true
c.up = true
c.route.Store(c.Route())
c.pingTimer.Stop() // No pings for mediators.
c.timeoutTimer.Stop() // No timeouts yet.
return c
}
func (c stateMediated) Name() string { return "Mediated:UP" }
func (c stateMediated) HandlePacket(w wrapper) connState { return c }
func (c stateMediated) HandleTimeout() connState {
logState(c, "Unexpected timeout.")
return c
}