198 lines
4.1 KiB
Go
198 lines
4.1 KiB
Go
package node
|
|
|
|
import (
|
|
"net/netip"
|
|
"sync/atomic"
|
|
"time"
|
|
"vppn/m"
|
|
)
|
|
|
|
const (
|
|
connectTimeout = 6 * time.Second
|
|
pingInterval = 6 * time.Second
|
|
timeoutInterval = 20 * time.Second
|
|
)
|
|
|
|
type stateFunc func() stateFunc
|
|
|
|
type peerSuper struct {
|
|
*remotePeer
|
|
|
|
peer *m.Peer
|
|
remotePublic bool
|
|
peerData peerData
|
|
|
|
pktBuf []byte
|
|
encBuf []byte
|
|
}
|
|
|
|
func newPeerSuper(rp *remotePeer) *peerSuper {
|
|
return &peerSuper{
|
|
remotePeer: rp,
|
|
peer: nil,
|
|
pktBuf: make([]byte, bufferSize),
|
|
encBuf: make([]byte, bufferSize),
|
|
}
|
|
}
|
|
|
|
func (rp *peerSuper) Run() {
|
|
defer panicHandler()
|
|
state := rp.stateInit
|
|
for {
|
|
state = state()
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
func (rp *peerSuper) stateInit() stateFunc {
|
|
//rp.logf("STATE: Init")
|
|
x := peerData{}
|
|
rp.shared.Store(&x)
|
|
|
|
rp.peerData.controlCipher = nil
|
|
rp.peerData.dataCipher = nil
|
|
rp.peerData.remoteAddr = zeroAddrPort
|
|
|
|
if rp.peer == nil {
|
|
return rp.stateDisconnected
|
|
}
|
|
|
|
var addr netip.Addr
|
|
addr, rp.remotePublic = netip.AddrFromSlice(rp.peer.PublicIP)
|
|
if rp.remotePublic {
|
|
rp.peerData.remoteAddr = netip.AddrPortFrom(addr, rp.peer.Port)
|
|
}
|
|
|
|
rp.peerData.controlCipher = newControlCipher(rp.privKey, rp.peer.EncPubKey)
|
|
|
|
return rp.stateSelectRole()
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
func (rp *peerSuper) stateDisconnected() stateFunc {
|
|
//rp.logf("STATE: Disconnected")
|
|
for {
|
|
select {
|
|
case <-rp.controlPackets:
|
|
// Drop
|
|
case rp.peer = <-rp.peerUpdates:
|
|
return rp.stateInit
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
func (rp *peerSuper) stateSelectRole() stateFunc {
|
|
rp.logf("STATE: SelectRole")
|
|
|
|
if !rp.localPublic && !rp.remotePublic {
|
|
// TODO!
|
|
return rp.stateDisconnected
|
|
}
|
|
|
|
if !rp.localPublic {
|
|
return rp.stateServer
|
|
} else if !rp.remotePublic {
|
|
return rp.stateClient
|
|
}
|
|
|
|
if rp.localIP < rp.peer.PeerIP {
|
|
return rp.stateClient
|
|
}
|
|
return rp.stateServer
|
|
}
|
|
|
|
// The remote is a server.
|
|
func (rp *peerSuper) stateServer() stateFunc {
|
|
rp.logf("STATE: Server")
|
|
rp.peerData.dataCipher = newDataCipher()
|
|
rp.updateShared()
|
|
|
|
var (
|
|
pingTimer = time.NewTimer(pingInterval)
|
|
ping = pingPacket{SharedKey: ([32]byte)(rp.peerData.dataCipher.Key())}
|
|
)
|
|
defer pingTimer.Stop()
|
|
|
|
ping.SentAt = time.Now().UnixMilli()
|
|
rp.sendControlPacket(ping)
|
|
|
|
for {
|
|
select {
|
|
case <-pingTimer.C:
|
|
ping.SentAt = time.Now().UnixMilli()
|
|
rp.sendControlPacket(ping)
|
|
pingTimer.Reset(pingInterval)
|
|
|
|
case <-rp.controlPackets:
|
|
// Ignore
|
|
|
|
case rp.peer = <-rp.peerUpdates:
|
|
return rp.stateInit
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// The remote is a client.
|
|
func (rp *peerSuper) stateClient() stateFunc {
|
|
rp.logf("STATE: Client")
|
|
rp.updateShared()
|
|
|
|
// TODO: Could use timeout to set dataCipher to nil.
|
|
var currentKey = [32]byte{}
|
|
|
|
for {
|
|
select {
|
|
case cPkt := <-rp.controlPackets:
|
|
if cPkt.RemoteAddr != rp.peerData.remoteAddr {
|
|
rp.peerData.remoteAddr = cPkt.RemoteAddr
|
|
rp.logf("Got new remote address: %v", cPkt.RemoteAddr)
|
|
rp.updateShared()
|
|
}
|
|
|
|
ping, ok := cPkt.Payload.(pingPacket)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if ping.SharedKey != currentKey {
|
|
rp.logf("Connected with new shared key")
|
|
currentKey = ping.SharedKey
|
|
rp.peerData.dataCipher = newDataCipherFromKey(currentKey)
|
|
rp.updateShared()
|
|
}
|
|
|
|
rp.sendControlPacket(newPongPacket(ping.SentAt))
|
|
|
|
case rp.peer = <-rp.peerUpdates:
|
|
return rp.stateInit
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
func (rp *peerSuper) updateShared() {
|
|
data := rp.peerData
|
|
rp.shared.Store(&data)
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
func (rp *peerSuper) sendControlPacket(pkt interface{ Marshal([]byte) []byte }) {
|
|
buf := pkt.Marshal(rp.pktBuf)
|
|
h := xHeader{
|
|
StreamID: controlStreamID,
|
|
Counter: atomic.AddUint64(&rp.counter, 1),
|
|
SourceIP: rp.localIP,
|
|
DestIP: rp.remoteIP,
|
|
}
|
|
buf = rp.peerData.controlCipher.Encrypt(h, buf, rp.encBuf)
|
|
rp.conn.WriteTo(buf, rp.peerData.remoteAddr)
|
|
}
|