175 lines
3.3 KiB
Go
175 lines
3.3 KiB
Go
package mc
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
)
|
|
|
|
type Handshake struct {
|
|
ProtocolVersion int32
|
|
ServerAddress string
|
|
ServerPort uint16
|
|
NextState int32
|
|
Raw []byte
|
|
}
|
|
|
|
func ReadHandshake(r io.Reader) (*Handshake, error) {
|
|
raw, err := readPacket(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
reader := newReader(raw)
|
|
packetID, err := reader.readVarInt()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if packetID != 0 {
|
|
return nil, fmt.Errorf("unexpected packet id %d", packetID)
|
|
}
|
|
|
|
protocolVersion, err := reader.readVarInt()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
serverAddress, err := reader.readString()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
portBytes, err := reader.readBytes(2)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
serverPort := binary.BigEndian.Uint16(portBytes)
|
|
|
|
nextState, err := reader.readVarInt()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Handshake{
|
|
ProtocolVersion: protocolVersion,
|
|
ServerAddress: serverAddress,
|
|
ServerPort: serverPort,
|
|
NextState: nextState,
|
|
Raw: raw,
|
|
}, nil
|
|
}
|
|
|
|
func ExtractSlug(hostname, playDomain string) string {
|
|
host := strings.ToLower(strings.Split(hostname, "\x00")[0])
|
|
domain := strings.ToLower(playDomain)
|
|
suffix := "." + domain
|
|
if !strings.HasSuffix(host, suffix) {
|
|
return ""
|
|
}
|
|
slug := strings.TrimSuffix(host, suffix)
|
|
if slug == "" || strings.Contains(slug, ".") {
|
|
return ""
|
|
}
|
|
return slug
|
|
}
|
|
|
|
func readPacket(r io.Reader) ([]byte, error) {
|
|
length, err := readVarIntFrom(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if length <= 0 || length > 1<<20 {
|
|
return nil, fmt.Errorf("invalid packet length %d", length)
|
|
}
|
|
|
|
payload := make([]byte, length)
|
|
if _, err := io.ReadFull(r, payload); err != nil {
|
|
return nil, err
|
|
}
|
|
return payload, nil
|
|
}
|
|
|
|
type packetReader struct {
|
|
buf *bytes.Reader
|
|
}
|
|
|
|
func newReader(data []byte) *packetReader {
|
|
return &packetReader{buf: bytes.NewReader(data)}
|
|
}
|
|
|
|
func (p *packetReader) readVarInt() (int32, error) {
|
|
return readVarIntFrom(p.buf)
|
|
}
|
|
|
|
func (p *packetReader) readString() (string, error) {
|
|
length, err := p.readVarInt()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if length < 0 || length > 255 {
|
|
return "", fmt.Errorf("invalid string length %d", length)
|
|
}
|
|
data, err := p.readBytes(int(length))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(data), nil
|
|
}
|
|
|
|
func (p *packetReader) readBytes(n int) ([]byte, error) {
|
|
out := make([]byte, n)
|
|
if _, err := io.ReadFull(p.buf, out); err != nil {
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func readVarIntFrom(r io.Reader) (int32, error) {
|
|
var numRead int
|
|
var result int32
|
|
|
|
for {
|
|
var value [1]byte
|
|
if _, err := io.ReadFull(r, value[:]); err != nil {
|
|
return 0, err
|
|
}
|
|
result |= int32(value[0]&0x7F) << (7 * numRead)
|
|
numRead++
|
|
if numRead > 5 {
|
|
return 0, fmt.Errorf("varint too big")
|
|
}
|
|
if value[0]&0x80 == 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func WritePacket(w io.Writer, payload []byte) error {
|
|
if err := writeVarIntTo(w, int32(len(payload))); err != nil {
|
|
return err
|
|
}
|
|
_, err := w.Write(payload)
|
|
return err
|
|
}
|
|
|
|
func writeVarIntTo(w io.Writer, value int32) error {
|
|
for {
|
|
temp := byte(value & 0x7F)
|
|
value >>= 7
|
|
if value != 0 {
|
|
temp |= 0x80
|
|
}
|
|
if _, err := w.Write([]byte{temp}); err != nil {
|
|
return err
|
|
}
|
|
if value == 0 {
|
|
break
|
|
}
|
|
}
|
|
return nil
|
|
}
|