Skip to content

Commit 2f470fa

Browse files
committed
Add support cs2+tcp protocol for xiaomi source
1 parent 212def9 commit 2f470fa

File tree

3 files changed

+210
-94
lines changed

3 files changed

+210
-94
lines changed

pkg/xiaomi/cs2/conn.go

Lines changed: 200 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cs2
22

33
import (
4+
"bufio"
45
"encoding/binary"
56
"fmt"
67
"io"
@@ -10,33 +11,27 @@ import (
1011
"time"
1112
)
1213

13-
func Dial(host string) (*Conn, error) {
14-
conn, err := net.ListenUDP("udp", nil)
14+
func Dial(host, transport string) (*Conn, error) {
15+
conn, err := handshake(host, transport)
1516
if err != nil {
1617
return nil, err
1718
}
1819

19-
c := &Conn{
20-
conn: conn,
21-
addr: &net.UDPAddr{IP: net.ParseIP(host), Port: 32108},
22-
}
20+
_, isTCP := conn.(*tcpConn)
2321

24-
if err = c.handshake(); err != nil {
25-
_ = conn.Close()
26-
return nil, err
22+
c := &Conn{
23+
conn: conn,
24+
isTCP: isTCP,
25+
rawCh0: make(chan []byte, 10),
26+
rawCh2: make(chan []byte, 100),
2727
}
28-
29-
c.rawCh0 = make(chan []byte, 10)
30-
c.rawCh2 = make(chan []byte, 100)
31-
3228
go c.worker()
33-
3429
return c, nil
3530
}
3631

3732
type Conn struct {
38-
conn *net.UDPConn
39-
addr *net.UDPAddr
33+
conn net.Conn
34+
isTCP bool
4035

4136
err error
4237
seqCh0 uint16
@@ -53,30 +48,58 @@ const (
5348
magicDrw = 0xD1
5449
msgLanSearch = 0x30
5550
msgPunchPkt = 0x41
56-
msgP2PRdy = 0x42
51+
msgP2PRdyUDP = 0x42
52+
msgP2PRdyTCP = 0x43
5753
msgDrw = 0xD0
5854
msgDrwAck = 0xD1
59-
msgAlive = 0xE0
55+
msgPing = 0xE0
56+
msgPong = 0xE1
57+
msgClose = 0xF1
6058
)
6159

62-
func (c *Conn) handshake() error {
63-
_ = c.SetDeadline(time.Now().Add(5 * time.Second))
60+
func handshake(host, transport string) (net.Conn, error) {
61+
conn, err := newUDPConn(host, 32108)
62+
if err != nil {
63+
return nil, err
64+
}
65+
66+
_ = conn.SetDeadline(time.Now().Add(5 * time.Second))
6467

65-
buf, err := c.WriteAndWait([]byte{magic, msgLanSearch, 0, 0}, msgPunchPkt)
68+
req := []byte{magic, msgLanSearch, 0, 0}
69+
res, err := conn.(*udpConn).WriteUntil(req, func(res []byte) bool {
70+
return res[1] == msgPunchPkt
71+
})
6672
if err != nil {
67-
return fmt.Errorf("%s: read punch: %w", "cs2", err)
73+
_ = conn.Close()
74+
return nil, err
6875
}
6976

70-
_, err = c.WriteAndWait(buf, msgP2PRdy)
77+
var msgUDP, msgTCP byte
78+
79+
if transport == "" || transport == "udp" {
80+
msgUDP = msgP2PRdyUDP
81+
}
82+
if transport == "" || transport == "tcp" {
83+
msgTCP = msgP2PRdyTCP
84+
}
85+
86+
res, err = conn.(*udpConn).WriteUntil(res, func(res []byte) bool {
87+
return res[1] == msgUDP || res[1] == msgTCP
88+
})
7189
if err != nil {
72-
return fmt.Errorf("%s: read ready: %w", "cs2", err)
90+
_ = conn.Close()
91+
return nil, err
7392
}
7493

75-
_ = c.Write([]byte{magic, msgAlive, 0, 0})
94+
_ = conn.SetDeadline(time.Time{})
7695

77-
_ = c.SetDeadline(time.Time{})
96+
if res[1] == msgTCP {
97+
_ = conn.Close()
98+
//host := fmt.Sprintf("%d.%d.%d.%d:%d", b[31], b[30], b[29], b[28], uint16(b[27])<<8|uint16(b[26]))
99+
return newTCPConn(conn.RemoteAddr().String())
100+
}
78101

79-
return nil
102+
return conn, nil
80103
}
81104

82105
func (c *Conn) worker() {
@@ -85,38 +108,41 @@ func (c *Conn) worker() {
85108
close(c.rawCh2)
86109
}()
87110

88-
chAck := make([]uint16, 4)
111+
chAck := make([]uint16, 4) // only for UDP
89112
buf := make([]byte, 1200)
90113
var ch2WaitSize int
91114
var ch2WaitData []byte
115+
var keepaliveTS time.Time
92116

93117
for {
94-
n, addr, err := c.conn.ReadFromUDP(buf)
118+
n, err := c.conn.Read(buf)
95119
if err != nil {
96120
c.err = fmt.Errorf("%s: %w", "cs2", err)
97121
return
98122
}
99123

100-
if string(addr.IP) != string(c.addr.IP) || n < 8 || buf[0] != magic {
101-
continue // skip messages from another IP
102-
}
103-
104-
//log.Printf("<- %x", buf[:n])
105-
106124
switch buf[1] {
107125
case msgDrw:
108126
ch := buf[5]
109-
seqHI := buf[6]
110-
seqLO := buf[7]
111127

112-
if chAck[ch] != uint16(seqHI)<<8|uint16(seqLO) {
113-
continue
114-
}
115-
chAck[ch]++
128+
if c.isTCP {
129+
// For TCP we should using ping/pong.
130+
if now := time.Now(); now.After(keepaliveTS) {
131+
_, _ = c.conn.Write([]byte{magic, msgPing, 0, 0})
132+
keepaliveTS = now.Add(5 * time.Second)
133+
}
134+
} else {
135+
// For UDP we should using ack.
136+
seqHI := buf[6]
137+
seqLO := buf[7]
138+
139+
if chAck[ch] != uint16(seqHI)<<8|uint16(seqLO) {
140+
continue
141+
}
142+
chAck[ch]++
116143

117-
ack := []byte{magic, msgDrwAck, 0, 6, magicDrw, ch, 0, 1, seqHI, seqLO}
118-
if _, err = c.conn.WriteToUDP(ack, c.addr); err != nil {
119-
return
144+
ack := []byte{magic, msgDrwAck, 0, 6, magicDrw, ch, 0, 1, seqHI, seqLO}
145+
_, _ = c.conn.Write(ack)
120146
}
121147

122148
switch ch {
@@ -152,9 +178,12 @@ func (c *Conn) worker() {
152178
continue
153179
}
154180

155-
case msgP2PRdy: // skip it
181+
case msgPing:
182+
_, _ = c.conn.Write([]byte{magic, msgPong, 0, 0})
156183
continue
157-
case msgDrwAck:
184+
case msgPong, msgP2PRdyUDP, msgP2PRdyTCP, msgClose:
185+
continue // skip it
186+
case msgDrwAck: // only for UDP
158187
if c.cmdAck != nil {
159188
c.cmdAck()
160189
}
@@ -165,42 +194,15 @@ func (c *Conn) worker() {
165194
}
166195
}
167196

168-
func (c *Conn) Write(req []byte) error {
169-
//log.Printf("-> %x", req)
170-
_, err := c.conn.WriteToUDP(req, c.addr)
171-
return err
172-
}
173-
174-
func (c *Conn) WriteAndWait(req []byte, waitMsg uint8) ([]byte, error) {
175-
var t *time.Timer
176-
t = time.AfterFunc(1, func() {
177-
if err := c.Write(req); err == nil && t != nil {
178-
t.Reset(time.Second)
179-
}
180-
})
181-
defer t.Stop()
182-
183-
buf := make([]byte, 1200)
184-
185-
for {
186-
n, addr, err := c.conn.ReadFromUDP(buf)
187-
if err != nil {
188-
return nil, err
189-
}
190-
191-
if string(addr.IP) != string(c.addr.IP) || n < 16 {
192-
continue // skip messages from another IP
193-
}
194-
195-
if buf[0] == magic && buf[1] == waitMsg {
196-
c.addr.Port = addr.Port
197-
return buf[:n], nil
198-
}
197+
func (c *Conn) Protocol() string {
198+
if c.isTCP {
199+
return "cs2+tcp"
199200
}
201+
return "cs2+udp"
200202
}
201203

202204
func (c *Conn) RemoteAddr() net.Addr {
203-
return c.addr
205+
return c.conn.RemoteAddr()
204206
}
205207

206208
func (c *Conn) SetDeadline(t time.Time) error {
@@ -232,6 +234,14 @@ func (c *Conn) WriteCommand(cmd uint16, data []byte) error {
232234
c.cmdMu.Lock()
233235
defer c.cmdMu.Unlock()
234236

237+
req := marshalCmd(0, c.seqCh0, uint32(cmd), data)
238+
c.seqCh0++
239+
240+
if c.isTCP {
241+
_, err := c.conn.Write(req)
242+
return err
243+
}
244+
235245
var repeat atomic.Int32
236246
repeat.Store(5)
237247

@@ -243,11 +253,8 @@ func (c *Conn) WriteCommand(cmd uint16, data []byte) error {
243253
timeout.Reset(1)
244254
}
245255

246-
req := marshalCmd(0, c.seqCh0, uint32(cmd), data)
247-
c.seqCh0++
248-
249256
for {
250-
if err := c.Write(req); err != nil {
257+
if _, err := c.conn.Write(req); err != nil {
251258
return err
252259
}
253260
<-timeout.C
@@ -285,7 +292,8 @@ func (c *Conn) WritePacket(data []byte) error {
285292
binary.BigEndian.PutUint32(req[8:], n)
286293
copy(req[offset:], data)
287294

288-
return c.Write(req)
295+
_, err := c.conn.Write(req)
296+
return err
289297
}
290298

291299
func marshalCmd(channel byte, seq uint16, cmd uint32, payload []byte) []byte {
@@ -313,3 +321,112 @@ func marshalCmd(channel byte, seq uint16, cmd uint32, payload []byte) []byte {
313321

314322
return req
315323
}
324+
325+
func newUDPConn(host string, port int) (net.Conn, error) {
326+
// We using raw net.UDPConn, because RemoteAddr should be changed during handshake.
327+
conn, err := net.ListenUDP("udp", nil)
328+
if err != nil {
329+
return nil, err
330+
}
331+
332+
addr, err := net.ResolveUDPAddr("udp", host)
333+
if err != nil {
334+
addr = &net.UDPAddr{IP: net.ParseIP(host), Port: port}
335+
}
336+
337+
return &udpConn{UDPConn: conn, addr: addr}, nil
338+
}
339+
340+
type udpConn struct {
341+
*net.UDPConn
342+
addr *net.UDPAddr
343+
}
344+
345+
func (c *udpConn) Read(p []byte) (n int, err error) {
346+
var addr *net.UDPAddr
347+
for {
348+
n, addr, err = c.UDPConn.ReadFromUDP(p)
349+
if err != nil {
350+
return 0, err
351+
}
352+
353+
if string(addr.IP) == string(c.addr.IP) || n >= 8 {
354+
return
355+
}
356+
}
357+
}
358+
359+
func (c *udpConn) Write(req []byte) (n int, err error) {
360+
//log.Printf("-> %x", req)
361+
return c.UDPConn.WriteToUDP(req, c.addr)
362+
}
363+
364+
func (c *udpConn) RemoteAddr() net.Addr {
365+
return c.addr
366+
}
367+
368+
func (c *udpConn) WriteUntil(req []byte, ok func(res []byte) bool) ([]byte, error) {
369+
var t *time.Timer
370+
t = time.AfterFunc(1, func() {
371+
if _, err := c.Write(req); err == nil && t != nil {
372+
t.Reset(time.Second)
373+
}
374+
})
375+
defer t.Stop()
376+
377+
buf := make([]byte, 1200)
378+
379+
for {
380+
n, addr, err := c.UDPConn.ReadFromUDP(buf)
381+
if err != nil {
382+
return nil, err
383+
}
384+
385+
if string(addr.IP) != string(c.addr.IP) || n < 16 {
386+
continue // skip messages from another IP
387+
}
388+
389+
if ok(buf[:n]) {
390+
c.addr.Port = addr.Port
391+
return buf[:n], nil
392+
}
393+
}
394+
}
395+
396+
func newTCPConn(addr string) (net.Conn, error) {
397+
conn, err := net.DialTimeout("tcp", addr, 3*time.Second)
398+
if err != nil {
399+
return nil, err
400+
}
401+
return &tcpConn{conn.(*net.TCPConn), bufio.NewReader(conn)}, nil
402+
}
403+
404+
type tcpConn struct {
405+
*net.TCPConn
406+
rd *bufio.Reader
407+
}
408+
409+
func (c *tcpConn) Read(p []byte) (n int, err error) {
410+
tmp := make([]byte, 8)
411+
if _, err = io.ReadFull(c.rd, tmp); err != nil {
412+
return
413+
}
414+
n = int(binary.BigEndian.Uint16(tmp))
415+
if len(p) < n {
416+
return 0, fmt.Errorf("tcp: buffer too small")
417+
}
418+
_, err = io.ReadFull(c.rd, p[:n])
419+
//log.Printf("<- %x%x", tmp, p[:n])
420+
return
421+
}
422+
423+
func (c *tcpConn) Write(req []byte) (n int, err error) {
424+
n = len(req)
425+
buf := make([]byte, 8+n)
426+
binary.BigEndian.PutUint16(buf, uint16(n))
427+
buf[2] = 0x68
428+
copy(buf[8:], req)
429+
//log.Printf("-> %x", buf)
430+
_, err = c.TCPConn.Write(buf)
431+
return
432+
}

0 commit comments

Comments
 (0)