Initial commit

This commit is contained in:
SantianDev
2026-03-03 15:05:55 +03:00
commit a9c89f027a
37 changed files with 3256 additions and 0 deletions

47
raknet/const.go Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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)
}
}