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 }