Initial commit
This commit is contained in:
47
raknet/const.go
Normal file
47
raknet/const.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package raknet
|
||||
|
||||
import "time"
|
||||
|
||||
func RaknetMagic() []byte {
|
||||
return []byte{0x00, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0x12, 0x34, 0x56, 0x78}
|
||||
}
|
||||
|
||||
const (
|
||||
MaxMTU = 1400
|
||||
MinMTU = 400
|
||||
MaxChannels = 32
|
||||
)
|
||||
|
||||
var (
|
||||
SessionTimeout = 10 * time.Second
|
||||
PingSendInterval = 2500 * time.Millisecond
|
||||
DetectionSendInterval = 5 * time.Second
|
||||
SendInterval = 50 * time.Millisecond
|
||||
RecoverySendInterval = 50 * time.Millisecond
|
||||
)
|
||||
|
||||
type Reliability byte
|
||||
|
||||
const (
|
||||
Unreliable Reliability = 0
|
||||
UnreliableSequenced Reliability = 1
|
||||
Reliable Reliability = 2
|
||||
ReliableOrdered Reliability = 3
|
||||
ReliableSequenced Reliability = 4
|
||||
UnreliableWithACKReceipt Reliability = 5
|
||||
ReliableWithACKReceipt Reliability = 6
|
||||
ReliableOrderedWithACKReceipt Reliability = 7
|
||||
)
|
||||
|
||||
func (r Reliability) IsReliable() bool {
|
||||
return r == Reliable || r == ReliableOrdered || r == ReliableSequenced ||
|
||||
r == ReliableWithACKReceipt || r == ReliableOrderedWithACKReceipt
|
||||
}
|
||||
|
||||
func (r Reliability) IsOrdered() bool {
|
||||
return r == ReliableOrdered || r == ReliableOrderedWithACKReceipt
|
||||
}
|
||||
|
||||
func (r Reliability) IsSequenced() bool {
|
||||
return r == UnreliableSequenced || r == ReliableSequenced
|
||||
}
|
||||
122
raknet/datagram.go
Normal file
122
raknet/datagram.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package raknet
|
||||
|
||||
type Datagram struct {
|
||||
SeqNum uint32
|
||||
Frames []*Frame
|
||||
}
|
||||
|
||||
func (d *Datagram) Encode() []byte {
|
||||
s := NewStream()
|
||||
s.WriteByte(0x84)
|
||||
s.WriteUint24LE(d.SeqNum)
|
||||
for _, f := range d.Frames {
|
||||
f.Encode(s)
|
||||
}
|
||||
return s.Bytes()
|
||||
}
|
||||
|
||||
func DecodeDatagram(data []byte) (*Datagram, error) {
|
||||
s := NewStreamBytes(data)
|
||||
_, err := s.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seq, err := s.ReadUint24LE()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d := &Datagram{SeqNum: seq}
|
||||
for s.Len() >= 3 {
|
||||
f, err := DecodeFrame(s)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
d.Frames = append(d.Frames, f)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (d *Datagram) Size() int {
|
||||
size := 4
|
||||
for _, f := range d.Frames {
|
||||
size += f.Size()
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func EncodeACK(seqNums []uint32) []byte {
|
||||
return encodeAckNack(0xc0, seqNums)
|
||||
}
|
||||
|
||||
func EncodeNACK(seqNums []uint32) []byte {
|
||||
return encodeAckNack(0xa0, seqNums)
|
||||
}
|
||||
|
||||
func encodeAckNack(id byte, seqNums []uint32) []byte {
|
||||
if len(seqNums) == 0 {
|
||||
s := NewStream()
|
||||
s.WriteByte(id)
|
||||
s.WriteUint16BE(0)
|
||||
return s.Bytes()
|
||||
}
|
||||
|
||||
type run struct{ start, end uint32 }
|
||||
var runs []run
|
||||
cur := run{seqNums[0], seqNums[0]}
|
||||
for _, n := range seqNums[1:] {
|
||||
if n == cur.end+1 {
|
||||
cur.end = n
|
||||
} else {
|
||||
runs = append(runs, cur)
|
||||
cur = run{n, n}
|
||||
}
|
||||
}
|
||||
runs = append(runs, cur)
|
||||
|
||||
s := NewStream()
|
||||
s.WriteByte(id)
|
||||
s.WriteUint16BE(uint16(len(runs)))
|
||||
for _, r := range runs {
|
||||
if r.start == r.end {
|
||||
s.WriteBool(true)
|
||||
s.WriteUint24LE(r.start)
|
||||
} else {
|
||||
s.WriteBool(false)
|
||||
s.WriteUint24LE(r.start)
|
||||
s.WriteUint24LE(r.end)
|
||||
}
|
||||
}
|
||||
return s.Bytes()
|
||||
}
|
||||
|
||||
func DecodeACKNACK(data []byte) ([]uint32, error) {
|
||||
s := NewStreamBytes(data)
|
||||
s.Skip(1)
|
||||
count, err := s.ReadUint16BE()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var nums []uint32
|
||||
for i := 0; i < int(count); i++ {
|
||||
single, err := s.ReadBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
start, err := s.ReadUint24LE()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if single {
|
||||
nums = append(nums, start)
|
||||
} else {
|
||||
end, err := s.ReadUint24LE()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for n := start; n <= end; n++ {
|
||||
nums = append(nums, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nums, nil
|
||||
}
|
||||
109
raknet/frame.go
Normal file
109
raknet/frame.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package raknet
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Frame struct {
|
||||
Reliability Reliability
|
||||
Split bool
|
||||
MessageIndex uint32
|
||||
OrderIndex uint32
|
||||
OrderChannel byte
|
||||
SplitCount uint32
|
||||
SplitID uint16
|
||||
SplitIndex uint32
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (f *Frame) Encode(s *Stream) {
|
||||
flags := byte(f.Reliability) << 5
|
||||
if f.Split {
|
||||
flags |= 0x10
|
||||
}
|
||||
s.WriteByte(flags)
|
||||
s.WriteUint16BE(uint16(len(f.Payload) * 8))
|
||||
|
||||
if f.Reliability.IsReliable() {
|
||||
s.WriteUint24LE(f.MessageIndex)
|
||||
}
|
||||
if f.Reliability.IsOrdered() || f.Reliability.IsSequenced() {
|
||||
s.WriteUint24LE(f.OrderIndex)
|
||||
s.WriteByte(f.OrderChannel)
|
||||
}
|
||||
if f.Split {
|
||||
s.WriteUint32BE(f.SplitCount)
|
||||
s.WriteUint16BE(f.SplitID)
|
||||
s.WriteUint32BE(f.SplitIndex)
|
||||
}
|
||||
s.WriteBytes(f.Payload)
|
||||
}
|
||||
|
||||
func DecodeFrame(s *Stream) (*Frame, error) {
|
||||
flags, err := s.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f := &Frame{}
|
||||
f.Reliability = Reliability((flags >> 5) & 0x07)
|
||||
f.Split = (flags & 0x10) != 0
|
||||
|
||||
bitLen, err := s.ReadUint16BE()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
length := int(bitLen / 8)
|
||||
|
||||
if f.Reliability.IsReliable() {
|
||||
f.MessageIndex, err = s.ReadUint24LE()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if f.Reliability.IsOrdered() || f.Reliability.IsSequenced() {
|
||||
f.OrderIndex, err = s.ReadUint24LE()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.OrderChannel, err = s.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if f.Split {
|
||||
sc, err := s.ReadUint32BE()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.SplitCount = sc
|
||||
si, err := s.ReadUint16BE()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.SplitID = si
|
||||
idx, err := s.ReadUint32BE()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.SplitIndex = idx
|
||||
}
|
||||
|
||||
f.Payload, err = s.ReadBytes(length)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("frame payload: need %d, %w", length, err)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *Frame) Size() int {
|
||||
size := 3
|
||||
if f.Reliability.IsReliable() {
|
||||
size += 3
|
||||
}
|
||||
if f.Reliability.IsOrdered() || f.Reliability.IsSequenced() {
|
||||
size += 4
|
||||
}
|
||||
if f.Split {
|
||||
size += 10
|
||||
}
|
||||
size += len(f.Payload)
|
||||
return size
|
||||
}
|
||||
166
raknet/packets.go
Normal file
166
raknet/packets.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package raknet
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
)
|
||||
|
||||
func BuildUnconnectedPong(timestamp int64, serverGUID int64, motd string) []byte {
|
||||
s := NewStream()
|
||||
s.WriteByte(0x1c)
|
||||
s.WriteInt64BE(timestamp)
|
||||
s.WriteInt64BE(serverGUID)
|
||||
s.WriteMagic()
|
||||
s.WriteString(motd)
|
||||
return s.Bytes()
|
||||
}
|
||||
|
||||
func BuildOpenConnectionReply1(serverGUID int64, mtu uint16) []byte {
|
||||
s := NewStream()
|
||||
s.WriteByte(0x06)
|
||||
s.WriteMagic()
|
||||
s.WriteInt64BE(serverGUID)
|
||||
s.WriteBool(false)
|
||||
s.WriteUint16BE(mtu)
|
||||
return s.Bytes()
|
||||
}
|
||||
|
||||
func BuildOpenConnectionReply2(serverGUID int64, clientAddr net.UDPAddr, mtu uint16) []byte {
|
||||
s := NewStream()
|
||||
s.WriteByte(0x08)
|
||||
s.WriteMagic()
|
||||
s.WriteInt64BE(serverGUID)
|
||||
s.WriteAddress(clientAddr)
|
||||
s.WriteUint16BE(mtu)
|
||||
s.WriteBool(false)
|
||||
return s.Bytes()
|
||||
}
|
||||
|
||||
func BuildConnectionRequestAccepted(clientAddr net.UDPAddr, clientTimestamp int64, serverTimestamp int64) []byte {
|
||||
s := NewStream()
|
||||
s.WriteByte(0x10)
|
||||
s.WriteAddress(clientAddr)
|
||||
s.WriteUint16BE(0)
|
||||
for i := 0; i < 10; i++ {
|
||||
s.WriteAddress(net.UDPAddr{IP: net.ParseIP("0.0.0.0"), Port: 0})
|
||||
}
|
||||
s.WriteInt64BE(clientTimestamp)
|
||||
s.WriteInt64BE(serverTimestamp)
|
||||
return s.Bytes()
|
||||
}
|
||||
|
||||
func BuildConnectedPong(pingTime int64, pongTime int64) []byte {
|
||||
s := NewStream()
|
||||
s.WriteByte(0x03)
|
||||
s.WriteInt64BE(pingTime)
|
||||
s.WriteInt64BE(pongTime)
|
||||
return s.Bytes()
|
||||
}
|
||||
|
||||
func ParseUnconnectedPing(data []byte) (timestamp int64, guid int64, ok bool) {
|
||||
if len(data) < 1+8+16 {
|
||||
return
|
||||
}
|
||||
timestamp = int64(binary.BigEndian.Uint64(data[1:9]))
|
||||
magic := RaknetMagic()
|
||||
for i, b := range magic {
|
||||
if data[9+i] != b {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(data) >= 1+8+16+8 {
|
||||
guid = int64(binary.BigEndian.Uint64(data[25:33]))
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func ParseOCR1(data []byte) (proto byte, mtu uint16, ok bool) {
|
||||
if len(data) < 1+16+1 {
|
||||
return
|
||||
}
|
||||
magic := RaknetMagic()
|
||||
for i, b := range magic {
|
||||
if data[1+i] != b {
|
||||
return
|
||||
}
|
||||
}
|
||||
proto = data[17]
|
||||
mtu = uint16(len(data) + 28)
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func ParseOCR2(data []byte) (clientGUID int64, clientAddr net.UDPAddr, mtu uint16, ok bool) {
|
||||
s := NewStreamBytes(data)
|
||||
s.Skip(1)
|
||||
magic := RaknetMagic()
|
||||
mb, err := s.ReadBytes(16)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i, b := range magic {
|
||||
if mb[i] != b {
|
||||
return
|
||||
}
|
||||
}
|
||||
addr, err := s.ReadAddress()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
clientAddr = addr
|
||||
mtuV, err := s.ReadUint16BE()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mtu = mtuV
|
||||
guidV, err := s.ReadInt64BE()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
clientGUID = guidV
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func ParseConnectionRequest(payload []byte) (clientGUID int64, timestamp int64, ok bool) {
|
||||
if len(payload) < 17 {
|
||||
return
|
||||
}
|
||||
if payload[0] != 0x09 {
|
||||
return
|
||||
}
|
||||
s := NewStreamBytes(payload[1:])
|
||||
g, err := s.ReadInt64BE()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t, err := s.ReadInt64BE()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
clientGUID = g
|
||||
timestamp = t
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func IsNewIncomingConnection(payload []byte) bool {
|
||||
return len(payload) > 0 && payload[0] == 0x13
|
||||
}
|
||||
|
||||
func IsDisconnect(payload []byte) bool {
|
||||
return len(payload) > 0 && payload[0] == 0x15
|
||||
}
|
||||
|
||||
func ParseConnectedPing(payload []byte) (timestamp int64, ok bool) {
|
||||
if len(payload) < 9 || payload[0] != 0x00 {
|
||||
return
|
||||
}
|
||||
s := NewStreamBytes(payload[1:])
|
||||
t, err := s.ReadInt64BE()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return t, true
|
||||
}
|
||||
352
raknet/server.go
Normal file
352
raknet/server.go
Normal file
@@ -0,0 +1,352 @@
|
||||
package raknet
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Handler interface {
|
||||
OnConnect(s *Session)
|
||||
OnPacket(s *Session, payload []byte)
|
||||
OnDisconnect(s *Session)
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
mu sync.RWMutex
|
||||
conn *net.UDPConn
|
||||
sessions map[string]*Session
|
||||
guid int64
|
||||
motdFunc func() string
|
||||
handler Handler
|
||||
running bool
|
||||
}
|
||||
|
||||
func NewServer(guid int64, motdFunc func() string, handler Handler) *Server {
|
||||
return &Server{
|
||||
guid: guid,
|
||||
motdFunc: motdFunc,
|
||||
handler: handler,
|
||||
sessions: make(map[string]*Session),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Start(ip string, port int) error {
|
||||
addr := &net.UDPAddr{IP: net.ParseIP(ip), Port: port}
|
||||
conn, err := net.ListenUDP("udp4", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.conn = conn
|
||||
s.running = true
|
||||
go s.readLoop()
|
||||
go s.tickLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Shutdown() {
|
||||
s.running = false
|
||||
if s.conn != nil {
|
||||
s.conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) readLoop() {
|
||||
buf := make([]byte, 2048)
|
||||
for s.running {
|
||||
n, addr, err := s.conn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
if !s.running {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
data := make([]byte, n)
|
||||
copy(data, buf[:n])
|
||||
go func(a *net.UDPAddr, d []byte) {
|
||||
defer func() { recover() }()
|
||||
s.handlePacket(a, d)
|
||||
}(addr, data)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) tickLoop() {
|
||||
ticker := time.NewTicker(50 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for s.running {
|
||||
<-ticker.C
|
||||
s.tick()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) tick() {
|
||||
s.mu.RLock()
|
||||
sessions := make([]*Session, 0, len(s.sessions))
|
||||
for _, sess := range s.sessions {
|
||||
sessions = append(sessions, sess)
|
||||
}
|
||||
s.mu.RUnlock()
|
||||
|
||||
for _, sess := range sessions {
|
||||
sess.FlushACKs()
|
||||
if sess.TimedOut() {
|
||||
s.removeSession(sess)
|
||||
if s.handler != nil {
|
||||
s.handler.OnDisconnect(sess)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handlePacket(addr *net.UDPAddr, data []byte) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
id := data[0]
|
||||
|
||||
switch {
|
||||
case id == 0x01 || id == 0x02:
|
||||
ts, _, ok := ParseUnconnectedPing(data)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
pong := BuildUnconnectedPong(ts, s.guid, s.motdFunc())
|
||||
s.conn.WriteToUDP(pong, addr)
|
||||
|
||||
case id == 0x05:
|
||||
proto, mtu, ok := ParseOCR1(data)
|
||||
if !ok || proto != 8 {
|
||||
return
|
||||
}
|
||||
if mtu > MaxMTU {
|
||||
mtu = MaxMTU
|
||||
}
|
||||
s.conn.WriteToUDP(BuildOpenConnectionReply1(s.guid, mtu), addr)
|
||||
|
||||
case id == 0x07:
|
||||
clientGUID, _, mtu, ok := ParseOCR2(data)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if mtu > MaxMTU {
|
||||
mtu = MaxMTU
|
||||
}
|
||||
key := addr.String()
|
||||
s.mu.Lock()
|
||||
if _, exists := s.sessions[key]; exists {
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
sess := NewSession(s.conn, addr, clientGUID, mtu)
|
||||
s.sessions[key] = sess
|
||||
s.mu.Unlock()
|
||||
s.conn.WriteToUDP(BuildOpenConnectionReply2(s.guid, *addr, mtu), addr)
|
||||
|
||||
case id >= 0x80 && id <= 0x8f:
|
||||
sess := s.getSession(addr)
|
||||
if sess == nil || sess.IsDisconnected() {
|
||||
return
|
||||
}
|
||||
s.handleDatagram(sess, data)
|
||||
|
||||
case id == 0xc0:
|
||||
sess := s.getSession(addr)
|
||||
if sess != nil {
|
||||
sess.HandleACK(data)
|
||||
}
|
||||
|
||||
case id == 0xa0:
|
||||
sess := s.getSession(addr)
|
||||
if sess != nil {
|
||||
sess.HandleNACK(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleDatagram(sess *Session, data []byte) {
|
||||
dg, err := DecodeDatagram(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sess.mu.Lock()
|
||||
sess.lastRecv = time.Now()
|
||||
|
||||
if sess.recvSeqs[dg.SeqNum] {
|
||||
sess.mu.Unlock()
|
||||
return
|
||||
}
|
||||
sess.recvSeqs[dg.SeqNum] = true
|
||||
sess.pendingACK = append(sess.pendingACK, dg.SeqNum)
|
||||
|
||||
if dg.SeqNum > sess.recvSeq+1 {
|
||||
for n := sess.recvSeq + 1; n < dg.SeqNum; n++ {
|
||||
if !sess.recvSeqs[n] {
|
||||
sess.pendingNACK = append(sess.pendingNACK, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
if dg.SeqNum >= sess.recvSeq {
|
||||
sess.recvSeq = dg.SeqNum + 1
|
||||
}
|
||||
sess.mu.Unlock()
|
||||
|
||||
for _, f := range dg.Frames {
|
||||
s.handleFrame(sess, f)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleFrame(sess *Session, f *Frame) {
|
||||
if f.Split {
|
||||
sess.mu.Lock()
|
||||
buf, ok := sess.splitMap[f.SplitID]
|
||||
if !ok {
|
||||
buf = &splitBuf{count: f.SplitCount, parts: make(map[uint32][]byte)}
|
||||
sess.splitMap[f.SplitID] = buf
|
||||
}
|
||||
buf.parts[f.SplitIndex] = f.Payload
|
||||
assembled := false
|
||||
var full []byte
|
||||
if uint32(len(buf.parts)) >= buf.count {
|
||||
for i := uint32(0); i < buf.count; i++ {
|
||||
full = append(full, buf.parts[i]...)
|
||||
}
|
||||
delete(sess.splitMap, f.SplitID)
|
||||
assembled = true
|
||||
}
|
||||
sess.mu.Unlock()
|
||||
if assembled {
|
||||
combined := &Frame{
|
||||
Reliability: f.Reliability,
|
||||
MessageIndex: f.MessageIndex,
|
||||
OrderIndex: f.OrderIndex,
|
||||
OrderChannel: f.OrderChannel,
|
||||
Payload: full,
|
||||
}
|
||||
s.handleFrame(sess, combined)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if f.Reliability.IsOrdered() {
|
||||
ch := int(f.OrderChannel)
|
||||
sess.mu.Lock()
|
||||
expected := sess.orderNext[ch]
|
||||
if f.OrderIndex == expected {
|
||||
sess.orderNext[ch]++
|
||||
payload := f.Payload
|
||||
var extras [][]byte
|
||||
for {
|
||||
next := sess.orderQueues[ch][sess.orderNext[ch]]
|
||||
if next == nil {
|
||||
break
|
||||
}
|
||||
delete(sess.orderQueues[ch], sess.orderNext[ch])
|
||||
sess.orderNext[ch]++
|
||||
extras = append(extras, next)
|
||||
}
|
||||
sess.mu.Unlock()
|
||||
s.dispatch(sess, payload)
|
||||
for _, e := range extras {
|
||||
s.dispatch(sess, e)
|
||||
}
|
||||
} else if f.OrderIndex > expected {
|
||||
sess.orderQueues[ch][f.OrderIndex] = f.Payload
|
||||
sess.mu.Unlock()
|
||||
} else {
|
||||
sess.mu.Unlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s.dispatch(sess, f.Payload)
|
||||
}
|
||||
|
||||
func (s *Server) dispatch(sess *Session, payload []byte) {
|
||||
if len(payload) == 0 {
|
||||
return
|
||||
}
|
||||
id := payload[0]
|
||||
|
||||
if id == 0x09 {
|
||||
_, timestamp, ok := ParseConnectionRequest(payload)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
accepted := BuildConnectionRequestAccepted(*sess.Addr, timestamp, sess.Timestamp())
|
||||
sess.Send(accepted, ReliableOrdered, 0)
|
||||
return
|
||||
}
|
||||
|
||||
if id == 0x13 {
|
||||
sess.SetConnected()
|
||||
if s.handler != nil {
|
||||
s.handler.OnConnect(sess)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if id == 0x15 {
|
||||
s.removeSession(sess)
|
||||
if s.handler != nil {
|
||||
s.handler.OnDisconnect(sess)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if id == 0x00 {
|
||||
t, ok := ParseConnectedPing(payload)
|
||||
if ok {
|
||||
pong := BuildConnectedPong(t, sess.Timestamp())
|
||||
sess.Send(pong, Unreliable, 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !sess.IsConnected() {
|
||||
return
|
||||
}
|
||||
|
||||
if s.handler != nil {
|
||||
s.handler.OnPacket(sess, payload)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) getSession(addr *net.UDPAddr) *Session {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.sessions[addr.String()]
|
||||
}
|
||||
|
||||
func (s *Server) removeSession(sess *Session) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
delete(s.sessions, sess.Addr.String())
|
||||
}
|
||||
|
||||
func (s *Server) GetSessionByGUID(guid int64) *Session {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
for _, sess := range s.sessions {
|
||||
if sess.GUID == guid {
|
||||
return sess
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Count() int {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return len(s.sessions)
|
||||
}
|
||||
|
||||
func (s *Server) Sessions() []*Session {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
list := make([]*Session, 0, len(s.sessions))
|
||||
for _, sess := range s.sessions {
|
||||
list = append(list, sess)
|
||||
}
|
||||
return list
|
||||
}
|
||||
244
raknet/session.go
Normal file
244
raknet/session.go
Normal file
@@ -0,0 +1,244 @@
|
||||
package raknet
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SessionState int
|
||||
|
||||
const (
|
||||
StateHandshaking SessionState = iota
|
||||
StateConnected
|
||||
StateDisconnected
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
mu sync.Mutex
|
||||
Addr *net.UDPAddr
|
||||
GUID int64
|
||||
MTU uint16
|
||||
conn *net.UDPConn
|
||||
|
||||
state SessionState
|
||||
|
||||
sendSeq uint32
|
||||
recvSeq uint32
|
||||
msgIndex uint32
|
||||
|
||||
orderIndex [MaxChannels]uint32
|
||||
orderNext [MaxChannels]uint32
|
||||
orderQueues [MaxChannels]map[uint32][]byte
|
||||
|
||||
recvSeqs map[uint32]bool
|
||||
pendingACK []uint32
|
||||
pendingNACK []uint32
|
||||
|
||||
splitMap map[uint16]*splitBuf
|
||||
|
||||
recoveryMap map[uint32]*Datagram
|
||||
|
||||
lastRecv time.Time
|
||||
connTime time.Time
|
||||
}
|
||||
|
||||
type splitBuf struct {
|
||||
count uint32
|
||||
parts map[uint32][]byte
|
||||
}
|
||||
|
||||
func NewSession(conn *net.UDPConn, addr *net.UDPAddr, guid int64, mtu uint16) *Session {
|
||||
s := &Session{
|
||||
conn: conn,
|
||||
Addr: addr,
|
||||
GUID: guid,
|
||||
MTU: mtu,
|
||||
state: StateHandshaking,
|
||||
recvSeqs: make(map[uint32]bool),
|
||||
splitMap: make(map[uint16]*splitBuf),
|
||||
recoveryMap: make(map[uint32]*Datagram),
|
||||
lastRecv: time.Now(),
|
||||
}
|
||||
for i := 0; i < MaxChannels; i++ {
|
||||
s.orderQueues[i] = make(map[uint32][]byte)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Session) IsConnected() bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.state == StateConnected
|
||||
}
|
||||
|
||||
func (s *Session) IsDisconnected() bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.state == StateDisconnected
|
||||
}
|
||||
|
||||
func (s *Session) SetConnected() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.state = StateConnected
|
||||
s.connTime = time.Now()
|
||||
}
|
||||
|
||||
func (s *Session) Timestamp() int64 {
|
||||
if s.connTime.IsZero() {
|
||||
return 0
|
||||
}
|
||||
return time.Since(s.connTime).Milliseconds()
|
||||
}
|
||||
|
||||
func (s *Session) HandleACK(data []byte) {
|
||||
nums, err := DecodeACKNACK(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for _, n := range nums {
|
||||
delete(s.recoveryMap, n)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) HandleNACK(data []byte) {
|
||||
nums, err := DecodeACKNACK(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for _, n := range nums {
|
||||
dg, ok := s.recoveryMap[n]
|
||||
if ok {
|
||||
s.conn.WriteToUDP(dg.Encode(), s.Addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) FlushACKs() {
|
||||
s.mu.Lock()
|
||||
acks := s.pendingACK
|
||||
nacks := s.pendingNACK
|
||||
s.pendingACK = nil
|
||||
s.pendingNACK = nil
|
||||
s.mu.Unlock()
|
||||
|
||||
if len(acks) > 0 {
|
||||
s.conn.WriteToUDP(EncodeACK(acks), s.Addr)
|
||||
}
|
||||
if len(nacks) > 0 {
|
||||
s.conn.WriteToUDP(EncodeNACK(nacks), s.Addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) Send(payload []byte, reliability Reliability, orderChannel byte) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
maxPayload := int(s.MTU) - 4 - 3
|
||||
if reliability.IsReliable() {
|
||||
maxPayload -= 3
|
||||
}
|
||||
if reliability.IsOrdered() || reliability.IsSequenced() {
|
||||
maxPayload -= 4
|
||||
}
|
||||
if maxPayload < 100 {
|
||||
maxPayload = 100
|
||||
}
|
||||
|
||||
if len(payload) <= maxPayload {
|
||||
f := &Frame{Reliability: reliability, Payload: payload}
|
||||
if reliability.IsReliable() {
|
||||
f.MessageIndex = s.msgIndex
|
||||
s.msgIndex++
|
||||
}
|
||||
if reliability.IsOrdered() {
|
||||
f.OrderIndex = s.orderIndex[orderChannel]
|
||||
f.OrderChannel = orderChannel
|
||||
s.orderIndex[orderChannel]++
|
||||
}
|
||||
s.sendDatagram([]*Frame{f})
|
||||
return
|
||||
}
|
||||
|
||||
splitID := s.splitID()
|
||||
count := (len(payload) + maxPayload - 1) / maxPayload
|
||||
orderIdx := s.orderIndex[orderChannel]
|
||||
if reliability.IsOrdered() {
|
||||
s.orderIndex[orderChannel]++
|
||||
}
|
||||
for i := 0; i < count; i++ {
|
||||
start := i * maxPayload
|
||||
end := start + maxPayload
|
||||
if end > len(payload) {
|
||||
end = len(payload)
|
||||
}
|
||||
f := &Frame{
|
||||
Reliability: reliability,
|
||||
Split: true,
|
||||
SplitCount: uint32(count),
|
||||
SplitID: splitID,
|
||||
SplitIndex: uint32(i),
|
||||
Payload: make([]byte, end-start),
|
||||
}
|
||||
copy(f.Payload, payload[start:end])
|
||||
if reliability.IsReliable() {
|
||||
f.MessageIndex = s.msgIndex
|
||||
s.msgIndex++
|
||||
}
|
||||
if reliability.IsOrdered() {
|
||||
f.OrderIndex = orderIdx
|
||||
f.OrderChannel = orderChannel
|
||||
}
|
||||
s.sendDatagram([]*Frame{f})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) splitID() uint16 {
|
||||
id := uint16(s.msgIndex & 0xffff)
|
||||
return id
|
||||
}
|
||||
|
||||
func (s *Session) sendDatagram(frames []*Frame) {
|
||||
seq := s.sendSeq
|
||||
s.sendSeq++
|
||||
dg := &Datagram{SeqNum: seq, Frames: frames}
|
||||
pkt := dg.Encode()
|
||||
s.conn.WriteToUDP(pkt, s.Addr)
|
||||
|
||||
hasReliable := false
|
||||
for _, f := range frames {
|
||||
if f.Reliability.IsReliable() {
|
||||
hasReliable = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasReliable {
|
||||
s.recoveryMap[seq] = dg
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) SendRaw(data []byte) {
|
||||
s.conn.WriteToUDP(data, s.Addr)
|
||||
}
|
||||
|
||||
func (s *Session) TimedOut() bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return time.Since(s.lastRecv) > SessionTimeout
|
||||
}
|
||||
|
||||
func (s *Session) Disconnect() {
|
||||
s.mu.Lock()
|
||||
s.state = StateDisconnected
|
||||
seq := s.sendSeq
|
||||
s.sendSeq++
|
||||
s.mu.Unlock()
|
||||
f := &Frame{Reliability: Unreliable, Payload: []byte{0x15}}
|
||||
dg := &Datagram{SeqNum: seq, Frames: []*Frame{f}}
|
||||
s.conn.WriteToUDP(dg.Encode(), s.Addr)
|
||||
}
|
||||
242
raknet/stream.go
Normal file
242
raknet/stream.go
Normal file
@@ -0,0 +1,242 @@
|
||||
package raknet
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Stream struct {
|
||||
buf []byte
|
||||
pos int
|
||||
}
|
||||
|
||||
func NewStream() *Stream {
|
||||
return &Stream{}
|
||||
}
|
||||
|
||||
func NewStreamBytes(b []byte) *Stream {
|
||||
c := make([]byte, len(b))
|
||||
copy(c, b)
|
||||
return &Stream{buf: c}
|
||||
}
|
||||
|
||||
func (s *Stream) Bytes() []byte {
|
||||
return s.buf
|
||||
}
|
||||
|
||||
func (s *Stream) Len() int {
|
||||
return len(s.buf) - s.pos
|
||||
}
|
||||
|
||||
func (s *Stream) Reset() {
|
||||
s.buf = s.buf[:0]
|
||||
s.pos = 0
|
||||
}
|
||||
|
||||
func (s *Stream) Skip(n int) {
|
||||
s.pos += n
|
||||
}
|
||||
|
||||
func (s *Stream) ReadByte() (byte, error) {
|
||||
if s.pos >= len(s.buf) {
|
||||
return 0, errors.New("stream underflow")
|
||||
}
|
||||
b := s.buf[s.pos]
|
||||
s.pos++
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (s *Stream) ReadBytes(n int) ([]byte, error) {
|
||||
if s.pos+n > len(s.buf) {
|
||||
return nil, fmt.Errorf("stream underflow: need %d have %d", n, len(s.buf)-s.pos)
|
||||
}
|
||||
b := s.buf[s.pos : s.pos+n]
|
||||
s.pos += n
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (s *Stream) ReadUint16BE() (uint16, error) {
|
||||
b, err := s.ReadBytes(2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint16(b), nil
|
||||
}
|
||||
|
||||
func (s *Stream) ReadUint32BE() (uint32, error) {
|
||||
b, err := s.ReadBytes(4)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint32(b), nil
|
||||
}
|
||||
|
||||
func (s *Stream) ReadInt32BE() (int32, error) {
|
||||
v, err := s.ReadUint32BE()
|
||||
return int32(v), err
|
||||
}
|
||||
|
||||
func (s *Stream) ReadInt64BE() (int64, error) {
|
||||
b, err := s.ReadBytes(8)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int64(binary.BigEndian.Uint64(b)), nil
|
||||
}
|
||||
|
||||
func (s *Stream) ReadUint24LE() (uint32, error) {
|
||||
b, err := s.ReadBytes(3)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16, nil
|
||||
}
|
||||
|
||||
func (s *Stream) ReadBool() (bool, error) {
|
||||
b, err := s.ReadByte()
|
||||
return b != 0, err
|
||||
}
|
||||
|
||||
func (s *Stream) ReadString() (string, error) {
|
||||
n, err := s.ReadUint16BE()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b, err := s.ReadBytes(int(n))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func (s *Stream) ReadMagic() (bool, error) {
|
||||
b, err := s.ReadBytes(16)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
expected := RaknetMagic()
|
||||
for i, v := range expected {
|
||||
if b[i] != v {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s *Stream) ReadAddress() (net.UDPAddr, error) {
|
||||
ver, err := s.ReadByte()
|
||||
if err != nil {
|
||||
return net.UDPAddr{}, err
|
||||
}
|
||||
if ver == 4 {
|
||||
ip := make([]byte, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
b, err := s.ReadByte()
|
||||
if err != nil {
|
||||
return net.UDPAddr{}, err
|
||||
}
|
||||
ip[i] = ^b
|
||||
}
|
||||
port, err := s.ReadUint16BE()
|
||||
if err != nil {
|
||||
return net.UDPAddr{}, err
|
||||
}
|
||||
return net.UDPAddr{IP: net.IP(ip), Port: int(port)}, nil
|
||||
}
|
||||
b, err := s.ReadBytes(18)
|
||||
if err != nil {
|
||||
return net.UDPAddr{}, err
|
||||
}
|
||||
port := binary.BigEndian.Uint16(b[0:2])
|
||||
ip := net.IP(b[2:18])
|
||||
return net.UDPAddr{IP: ip, Port: int(port)}, nil
|
||||
}
|
||||
|
||||
func (s *Stream) WriteByte(v byte) {
|
||||
s.buf = append(s.buf, v)
|
||||
}
|
||||
|
||||
func (s *Stream) WriteBytes(v []byte) {
|
||||
s.buf = append(s.buf, v...)
|
||||
}
|
||||
|
||||
func (s *Stream) WriteUint16BE(v uint16) {
|
||||
s.buf = append(s.buf, byte(v>>8), byte(v))
|
||||
}
|
||||
|
||||
func (s *Stream) WriteUint32BE(v uint32) {
|
||||
s.buf = append(s.buf, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
||||
}
|
||||
|
||||
func (s *Stream) WriteInt32BE(v int32) {
|
||||
s.WriteUint32BE(uint32(v))
|
||||
}
|
||||
|
||||
func (s *Stream) WriteInt64BE(v int64) {
|
||||
s.buf = append(s.buf,
|
||||
byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32),
|
||||
byte(v>>24), byte(v>>16), byte(v>>8), byte(v),
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Stream) WriteUint24LE(v uint32) {
|
||||
s.buf = append(s.buf, byte(v), byte(v>>8), byte(v>>16))
|
||||
}
|
||||
|
||||
func (s *Stream) WriteBool(v bool) {
|
||||
if v {
|
||||
s.WriteByte(1)
|
||||
} else {
|
||||
s.WriteByte(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stream) WriteString(v string) {
|
||||
b := []byte(v)
|
||||
s.WriteUint16BE(uint16(len(b)))
|
||||
s.WriteBytes(b)
|
||||
}
|
||||
|
||||
func (s *Stream) WriteMagic() {
|
||||
s.WriteBytes(RaknetMagic())
|
||||
}
|
||||
|
||||
func (s *Stream) WriteAddress(addr net.UDPAddr) {
|
||||
ip := addr.IP.To4()
|
||||
if ip != nil {
|
||||
s.WriteByte(4)
|
||||
for _, b := range ip {
|
||||
s.WriteByte(^b)
|
||||
}
|
||||
s.WriteUint16BE(uint16(addr.Port))
|
||||
} else {
|
||||
s.WriteByte(6)
|
||||
s.WriteUint16BE(uint16(addr.Port))
|
||||
s.WriteUint32BE(0)
|
||||
s.WriteBytes(addr.IP.To16())
|
||||
s.WriteUint32BE(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stream) WriteAddressRaw(ipStr string, port uint16) {
|
||||
if strings.Contains(ipStr, ".") {
|
||||
parts := strings.Split(ipStr, ".")
|
||||
s.WriteByte(4)
|
||||
for _, p := range parts {
|
||||
v, _ := strconv.Atoi(p)
|
||||
s.WriteByte(^byte(v))
|
||||
}
|
||||
s.WriteUint16BE(port)
|
||||
} else {
|
||||
ip := net.ParseIP(ipStr)
|
||||
s.WriteByte(6)
|
||||
s.WriteUint16BE(port)
|
||||
s.WriteUint32BE(0)
|
||||
s.WriteBytes(ip.To16())
|
||||
s.WriteUint32BE(0)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user