-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathpacket_conn.go
113 lines (102 loc) · 2.68 KB
/
packet_conn.go
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
package cbyge
import (
"bytes"
"io"
"net"
"time"
"github.com/pkg/errors"
)
const DefaultPacketConnHost = "cm.gelighting.com:23778"
const PacketConnTimeout = time.Second * 10
type PacketConn struct {
conn net.Conn
}
// NewPacketConn creates a PacketConn connected to the default server.
func NewPacketConn() (*PacketConn, error) {
remoteAddr, err := net.ResolveTCPAddr("tcp", DefaultPacketConnHost)
if err != nil {
return nil, err
}
conn, err := net.DialTimeout("tcp", remoteAddr.String(), PacketConnTimeout)
if err != nil {
return nil, err
}
return &PacketConn{conn: conn}, nil
}
// NewPacketConnWrap creates a PacketConn on top of an existing socket.
func NewPacketConnWrap(conn net.Conn) *PacketConn {
return &PacketConn{conn: conn}
}
func (p *PacketConn) Read() (*Packet, error) {
header := make([]byte, 5)
_, err := io.ReadFull(p.conn, header)
if err != nil {
return nil, err
}
typeByte := header[0]
length := (int(header[1]) << 24) | (int(header[2]) << 16) | (int(header[3]) << 8) | int(header[4])
if length > 0x100000 {
return nil, errors.New("packet is unreasonably large")
}
data := make([]byte, length)
_, err = io.ReadFull(p.conn, data)
if err != nil {
return nil, err
}
return &Packet{
Type: typeByte >> 4,
IsResponse: (typeByte & 8) != 0,
Data: data,
}, nil
}
func (p *PacketConn) Write(packet *Packet) error {
data := packet.Encode()
for len(data) > 0 {
n, err := p.conn.Write(data)
if err != nil {
return err
}
data = data[n:]
}
return nil
}
func (p *PacketConn) Close() error {
return p.conn.Close()
}
// Auth does an authentication exchange with the server.
//
// Provide an authorization code, as obtained by Login().
// If timeout is non-zero, it is a socket read/write
// timeout; otherwise, no timeout is used.
func (p *PacketConn) Auth(userId uint32, code string, timeout time.Duration) error {
if timeout != 0 {
p.conn.SetDeadline(time.Now().Add(timeout))
defer p.conn.SetDeadline(time.Time{})
}
data := bytes.NewBuffer(nil)
data.Write([]byte{
0x03,
uint8(userId >> 24), uint8(userId >> 16), uint8(userId >> 8), uint8(userId),
0, uint8(len([]byte(code))),
})
data.Write([]byte(code))
data.Write([]byte{0, 0, 0xb4})
packet := &Packet{
Type: PacketTypeAuth,
Data: data.Bytes(),
}
if err := p.Write(packet); err != nil {
return errors.Wrap(err, "authenticate")
}
response, err := p.Read()
if err != nil {
return errors.Wrap(err, "authenticate")
}
if response.Type != PacketTypeAuth || !response.IsResponse {
return errors.New("authenticate: unexpected response packet type")
}
if !bytes.Equal(response.Data, []byte{0, 0}) {
return errors.New("authenticate: credentials not recognized")
}
return nil
}