104 lines
2.3 KiB
Go
104 lines
2.3 KiB
Go
package node
|
|
|
|
import (
|
|
"errors"
|
|
"net/netip"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
var (
|
|
errMalformedPacket = errors.New("malformed packet")
|
|
errUnknownPacketType = errors.New("unknown packet type")
|
|
)
|
|
|
|
const (
|
|
packetTypePing = iota + 1
|
|
packetTypePong
|
|
)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
type controlPacket struct {
|
|
SrcIP byte
|
|
RemoteAddr netip.AddrPort
|
|
Payload any
|
|
}
|
|
|
|
func (p *controlPacket) ParsePayload(buf []byte) (err error) {
|
|
switch buf[0] {
|
|
case packetTypePing:
|
|
p.Payload, err = parsePingPacket(buf)
|
|
case packetTypePong:
|
|
p.Payload, err = parsePongPacket(buf)
|
|
default:
|
|
return errUnknownPacketType
|
|
}
|
|
return err
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// A pingPacket is sent from a node acting as a client, to a node acting
|
|
// as a server. It always contains the shared key the client is expecting
|
|
// to use for data encryption with the server.
|
|
type pingPacket struct {
|
|
SentAt int64 // UnixMilli.
|
|
SharedKey [32]byte
|
|
}
|
|
|
|
func newPingPacket(sharedKey [32]byte) (pp pingPacket) {
|
|
pp.SentAt = time.Now().UnixMilli()
|
|
copy(pp.SharedKey[:], sharedKey[:])
|
|
return
|
|
}
|
|
|
|
func (p pingPacket) Marshal(buf []byte) []byte {
|
|
buf = buf[:41]
|
|
buf[0] = packetTypePing
|
|
*(*uint64)(unsafe.Pointer(&buf[1])) = uint64(p.SentAt)
|
|
copy(buf[9:41], p.SharedKey[:])
|
|
return buf
|
|
}
|
|
|
|
func parsePingPacket(buf []byte) (p pingPacket, err error) {
|
|
if len(buf) != 41 {
|
|
return p, errMalformedPacket
|
|
}
|
|
p.SentAt = *(*int64)(unsafe.Pointer(&buf[1]))
|
|
copy(p.SharedKey[:], buf[9:41])
|
|
return
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// A pongPacket is sent by a node in a server role in response to a pingPacket.
|
|
type pongPacket struct {
|
|
SentAt int64 // UnixMilli.
|
|
RecvdAt int64 // UnixMilli.
|
|
}
|
|
|
|
func newPongPacket(sentAt int64) (pp pongPacket) {
|
|
pp.SentAt = sentAt
|
|
pp.RecvdAt = time.Now().UnixMilli()
|
|
return
|
|
}
|
|
|
|
func (p pongPacket) Marshal(buf []byte) []byte {
|
|
buf = buf[:17]
|
|
buf[0] = packetTypePong
|
|
*(*uint64)(unsafe.Pointer(&buf[1])) = uint64(p.SentAt)
|
|
*(*uint64)(unsafe.Pointer(&buf[9])) = uint64(p.RecvdAt)
|
|
|
|
return buf
|
|
}
|
|
|
|
func parsePongPacket(buf []byte) (p pongPacket, err error) {
|
|
if len(buf) != 17 {
|
|
return p, errMalformedPacket
|
|
}
|
|
p.SentAt = *(*int64)(unsafe.Pointer(&buf[1]))
|
|
p.RecvdAt = *(*int64)(unsafe.Pointer(&buf[9]))
|
|
return
|
|
}
|