mc-router/mcproto/read.go

316 lines
6.4 KiB
Go

package mcproto
import (
"bufio"
"bytes"
"encoding/binary"
"io"
"net"
"strings"
"time"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
func ReadPacket(reader io.Reader, addr net.Addr, state State) (*Packet, error) {
logrus.
WithField("client", addr).
Debug("Reading packet")
if state == StateHandshaking {
bufReader := bufio.NewReader(reader)
data, err := bufReader.Peek(1)
if err != nil {
return nil, err
}
if data[0] == PacketIdLegacyServerListPing {
return ReadLegacyServerListPing(bufReader, addr)
} else {
reader = bufReader
}
}
frame, err := ReadFrame(reader, addr)
if err != nil {
return nil, err
}
packet := &Packet{Length: frame.Length}
remainder := bytes.NewBuffer(frame.Payload)
packet.PacketID, err = ReadVarInt(remainder)
if err != nil {
return nil, err
}
packet.Data = remainder.Bytes()
logrus.
WithField("client", addr).
WithField("packet", packet).
Debug("Read packet")
return packet, nil
}
func ReadLegacyServerListPing(reader *bufio.Reader, addr net.Addr) (*Packet, error) {
logrus.
WithField("client", addr).
Debug("Reading legacy server list ping")
packetId, err := reader.ReadByte()
if err != nil {
return nil, err
}
if packetId != PacketIdLegacyServerListPing {
return nil, errors.Errorf("expected legacy server listing ping packet ID, got %x", packetId)
}
payload, err := reader.ReadByte()
if err != nil {
return nil, err
}
if payload != 0x01 {
return nil, errors.Errorf("expected payload=1 from legacy server listing ping, got %x", payload)
}
packetIdForPluginMsg, err := reader.ReadByte()
if err != nil {
return nil, err
}
if packetIdForPluginMsg != 0xFA {
return nil, errors.Errorf("expected packetIdForPluginMsg=0xFA from legacy server listing ping, got %x", packetIdForPluginMsg)
}
messageNameShortLen, err := ReadUnsignedShort(reader)
if err != nil {
return nil, err
}
if messageNameShortLen != 11 {
return nil, errors.Errorf("expected messageNameShortLen=11 from legacy server listing ping, got %d", messageNameShortLen)
}
messageName, err := ReadUTF16BEString(reader, messageNameShortLen)
if messageName != "MC|PingHost" {
return nil, errors.Errorf("expected messageName=MC|PingHost, got %s", messageName)
}
remainingLen, err := ReadUnsignedShort(reader)
remainingReader := io.LimitReader(reader, int64(remainingLen))
protocolVersion, err := ReadByte(remainingReader)
if err != nil {
return nil, err
}
hostnameLen, err := ReadUnsignedShort(remainingReader)
if err != nil {
return nil, err
}
hostname, err := ReadUTF16BEString(remainingReader, hostnameLen)
if err != nil {
return nil, err
}
port, err := ReadUnsignedInt(remainingReader)
if err != nil {
return nil, err
}
return &Packet{
PacketID: PacketIdLegacyServerListPing,
Length: 0,
Data: &LegacyServerListPing{
ProtocolVersion: int(protocolVersion),
ServerAddress: hostname,
ServerPort: uint16(port),
},
}, nil
}
func ReadUTF16BEString(reader io.Reader, symbolLen uint16) (string, error) {
bsUtf16be := make([]byte, symbolLen*2)
_, err := io.ReadFull(reader, bsUtf16be)
if err != nil {
return "", err
}
result, _, err := transform.Bytes(unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM).NewDecoder(), bsUtf16be)
if err != nil {
return "", err
}
return string(result), nil
}
func ReadFrame(reader io.Reader, addr net.Addr) (*Frame, error) {
logrus.
WithField("client", addr).
Debug("Reading frame")
var err error
frame := &Frame{}
frame.Length, err = ReadVarInt(reader)
if err != nil {
return nil, err
}
// Limit frame length to 2^21 - 1
if frame.Length > 2097151 {
return nil, errors.Errorf("frame length %d too large", frame.Length)
}
logrus.
WithField("client", addr).
WithField("length", frame.Length).
Debug("Read frame length")
frame.Payload = make([]byte, frame.Length)
total := 0
for total < frame.Length {
readIntoThis := frame.Payload[total:]
n, err := reader.Read(readIntoThis)
if err != nil {
if err != io.EOF {
return nil, err
}
}
total += n
logrus.
WithField("client", addr).
WithField("total", total).
WithField("length", frame.Length).
Debug("Reading frame content")
if n == 0 {
logrus.
WithField("client", addr).
WithField("frame", frame).
Debug("No progress on frame reading")
time.Sleep(100 * time.Millisecond)
}
}
logrus.
WithField("client", addr).
WithField("frame", frame).
Debug("Read frame")
return frame, nil
}
func ReadVarInt(reader io.Reader) (int, error) {
b := make([]byte, 1)
var numRead uint = 0
result := 0
for numRead <= 5 {
n, err := reader.Read(b)
if err != nil {
return 0, err
}
if n == 0 {
continue
}
value := b[0] & 0x7F
result |= int(value) << (7 * numRead)
numRead++
if b[0]&0x80 == 0 {
return result, nil
}
}
return 0, errors.New("VarInt is too big")
}
func ReadString(reader io.Reader) (string, error) {
length, err := ReadVarInt(reader)
if err != nil {
return "", err
}
b := make([]byte, 1)
var strBuilder strings.Builder
for i := 0; i < length; i++ {
n, err := reader.Read(b)
if err != nil {
return "", err
}
if n == 0 {
continue
}
strBuilder.WriteByte(b[0])
}
return strBuilder.String(), nil
}
func ReadByte(reader io.Reader) (byte, error) {
buf := make([]byte, 1)
_, err := reader.Read(buf)
if err != nil {
return 0, err
} else {
return buf[0], nil
}
}
func ReadUnsignedShort(reader io.Reader) (uint16, error) {
var value uint16
err := binary.Read(reader, binary.BigEndian, &value)
if err != nil {
return 0, err
}
return value, nil
}
func ReadUnsignedInt(reader io.Reader) (uint32, error) {
var value uint32
err := binary.Read(reader, binary.BigEndian, &value)
if err != nil {
return 0, err
}
return value, nil
}
func ReadHandshake(data interface{}) (*Handshake, error) {
dataBytes, ok := data.([]byte)
if !ok {
return nil, errors.New("data is not expected byte slice")
}
handshake := &Handshake{}
buffer := bytes.NewBuffer(dataBytes)
var err error
handshake.ProtocolVersion, err = ReadVarInt(buffer)
if err != nil {
return nil, err
}
handshake.ServerAddress, err = ReadString(buffer)
if err != nil {
return nil, err
}
handshake.ServerPort, err = ReadUnsignedShort(buffer)
if err != nil {
return nil, err
}
nextState, err := ReadVarInt(buffer)
if err != nil {
return nil, err
}
handshake.NextState = nextState
return handshake, nil
}