1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
package server
import (
"bufio"
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"time"
)
type pingResponse struct {
Version struct {
Name string `json:"name"`
} `json:"version"`
Players struct {
Online int `json:"online"`
Max int `json:"max"`
} `json:"players"`
Description json.RawMessage `json:"description"`
}
func (p pingResponse) motdText() string {
var text string
if json.Unmarshal(p.Description, &text) == nil {
return text
}
var obj struct {
Text string `json:"text"`
}
if json.Unmarshal(p.Description, &obj) == nil {
return obj.Text
}
return ""
}
func pingServer(host string, port int, timeout time.Duration) (*pingResponse, error) {
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), timeout)
if err != nil {
return nil, err
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(timeout))
var handshake bytes.Buffer
writeVarInt(&handshake, 0x00)
writeVarInt(&handshake, -1)
writeString(&handshake, host)
binary.Write(&handshake, binary.BigEndian, uint16(port))
writeVarInt(&handshake, 1)
if err := writePacket(conn, handshake.Bytes()); err != nil {
return nil, err
}
if err := writePacket(conn, []byte{0x00}); err != nil {
return nil, err
}
reader := bufio.NewReader(conn)
if _, err := readVarInt(reader); err != nil {
return nil, err
}
packetID, err := readVarInt(reader)
if err != nil {
return nil, err
}
if packetID != 0x00 {
return nil, fmt.Errorf("unexpected packet id %d", packetID)
}
jsonLen, err := readVarInt(reader)
if err != nil {
return nil, err
}
payload := make([]byte, jsonLen)
if _, err := io.ReadFull(reader, payload); err != nil {
return nil, err
}
var resp pingResponse
if err := json.Unmarshal(payload, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func writePacket(conn net.Conn, data []byte) error {
var buf bytes.Buffer
writeVarInt(&buf, len(data))
buf.Write(data)
_, err := conn.Write(buf.Bytes())
return err
}
func writeString(buf *bytes.Buffer, s string) {
writeVarInt(buf, len(s))
buf.WriteString(s)
}
func writeVarInt(buf *bytes.Buffer, value int) {
uv := uint32(value)
for {
if uv&^uint32(0x7F) == 0 {
buf.WriteByte(byte(uv))
return
}
buf.WriteByte(byte(uv&0x7F | 0x80))
uv >>= 7
}
}
func readVarInt(r io.ByteReader) (int, error) {
var result uint32
var shift uint
for {
b, err := r.ReadByte()
if err != nil {
return 0, err
}
result |= uint32(b&0x7F) << shift
if b&0x80 == 0 {
break
}
shift += 7
if shift >= 32 {
return 0, errors.New("varint too long")
}
}
return int(result), nil
}
|