11package cs2
22
33import (
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
3732type 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
82105func (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
202204func (c * Conn ) RemoteAddr () net.Addr {
203- return c .addr
205+ return c .conn . RemoteAddr ()
204206}
205207
206208func (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
291299func 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