Phase8
This commit is contained in:
174
apps/edge-gateway/internal/mc/handshake.go
Normal file
174
apps/edge-gateway/internal/mc/handshake.go
Normal file
@@ -0,0 +1,174 @@
|
||||
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
|
||||
}
|
||||
59
apps/edge-gateway/internal/mc/handshake_test.go
Normal file
59
apps/edge-gateway/internal/mc/handshake_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package mc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExtractSlug(t *testing.T) {
|
||||
if got := ExtractSlug("abc.play.example.net", "play.example.net"); got != "abc" {
|
||||
t.Fatalf("got %q want abc", got)
|
||||
}
|
||||
if got := ExtractSlug("other.net", "play.example.net"); got != "" {
|
||||
t.Fatalf("expected empty slug")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadHandshake(t *testing.T) {
|
||||
var payload bytes.Buffer
|
||||
writeVarInt(&payload, 0)
|
||||
writeVarInt(&payload, 763)
|
||||
writeString(&payload, "demo.play.example.net")
|
||||
payload.WriteByte(0x63)
|
||||
payload.WriteByte(0xdd)
|
||||
writeVarInt(&payload, 2)
|
||||
|
||||
var packet bytes.Buffer
|
||||
writeVarInt(&packet, int32(payload.Len()))
|
||||
packet.Write(payload.Bytes())
|
||||
|
||||
hs, err := ReadHandshake(&packet)
|
||||
if err != nil {
|
||||
t.Fatalf("read handshake: %v", err)
|
||||
}
|
||||
if hs.ServerAddress != "demo.play.example.net" {
|
||||
t.Fatalf("address %q", hs.ServerAddress)
|
||||
}
|
||||
if hs.ServerPort != 25565 {
|
||||
t.Fatalf("port %d", hs.ServerPort)
|
||||
}
|
||||
}
|
||||
|
||||
func writeVarInt(buf *bytes.Buffer, value int32) {
|
||||
for {
|
||||
temp := byte(value & 0x7F)
|
||||
value >>= 7
|
||||
if value != 0 {
|
||||
temp |= 0x80
|
||||
}
|
||||
buf.WriteByte(temp)
|
||||
if value == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeString(buf *bytes.Buffer, value string) {
|
||||
writeVarInt(buf, int32(len(value)))
|
||||
buf.WriteString(value)
|
||||
}
|
||||
Reference in New Issue
Block a user