Files
Frodding/raknet/stream.go
2026-03-03 15:05:55 +03:00

243 lines
4.4 KiB
Go

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)
}
}