Skip to content

Commit 72d7850

Browse files
committed
add tests for shadowsocks UDP relay
1 parent a7d49f0 commit 72d7850

File tree

4 files changed

+197
-47
lines changed

4 files changed

+197
-47
lines changed

forward.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -617,15 +617,15 @@ func (c *udpServerConn) RemoteAddr() net.Addr {
617617
}
618618

619619
func (c *udpServerConn) SetDeadline(t time.Time) error {
620-
return nil
620+
return c.conn.SetDeadline(t)
621621
}
622622

623623
func (c *udpServerConn) SetReadDeadline(t time.Time) error {
624-
return nil
624+
return c.conn.SetReadDeadline(t)
625625
}
626626

627627
func (c *udpServerConn) SetWriteDeadline(t time.Time) error {
628-
return nil
628+
return c.conn.SetWriteDeadline(t)
629629
}
630630

631631
type tcpRemoteForwardListener struct {

ss.go

Lines changed: 119 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,6 @@ import (
1616
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
1717
)
1818

19-
// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write,
20-
// we wrap around it to make io.Copy happy.
21-
type shadowConn struct {
22-
conn net.Conn
23-
}
24-
25-
func (c *shadowConn) Read(b []byte) (n int, err error) {
26-
return c.conn.Read(b)
27-
}
28-
29-
func (c *shadowConn) Write(b []byte) (n int, err error) {
30-
n = len(b) // force byte length consistent
31-
_, err = c.conn.Write(b)
32-
return
33-
}
34-
35-
func (c *shadowConn) Close() error {
36-
return c.conn.Close()
37-
}
38-
39-
func (c *shadowConn) LocalAddr() net.Addr {
40-
return c.conn.LocalAddr()
41-
}
42-
43-
func (c *shadowConn) RemoteAddr() net.Addr {
44-
return c.conn.RemoteAddr()
45-
}
46-
47-
func (c *shadowConn) SetDeadline(t time.Time) error {
48-
return c.conn.SetDeadline(t)
49-
}
50-
51-
func (c *shadowConn) SetReadDeadline(t time.Time) error {
52-
return c.conn.SetReadDeadline(t)
53-
}
54-
55-
func (c *shadowConn) SetWriteDeadline(t time.Time) error {
56-
return c.conn.SetWriteDeadline(t)
57-
}
58-
5919
type shadowConnector struct {
6020
Cipher *url.Userinfo
6121
}
@@ -102,7 +62,7 @@ func (c *shadowConnector) Connect(conn net.Conn, addr string, options ...Connect
10262
if _, err := sc.Write(rawaddr); err != nil {
10363
return nil, err
10464
}
105-
return &shadowConn{conn: sc}, nil
65+
return &shadowConn{sc}, nil
10666
}
10767

10868
type shadowHandler struct {
@@ -142,7 +102,7 @@ func (h *shadowHandler) Handle(conn net.Conn) {
142102
conn.RemoteAddr(), conn.LocalAddr(), err)
143103
return
144104
}
145-
conn = &shadowConn{conn: ss.NewConn(conn, cipher)}
105+
conn = &shadowConn{ss.NewConn(conn, cipher)}
146106

147107
conn.SetReadDeadline(time.Now().Add(ReadTimeout))
148108
host, err := h.getRequest(conn)
@@ -284,6 +244,55 @@ func (h *shadowHandler) getRequest(r io.Reader) (host string, err error) {
284244
return
285245
}
286246

247+
type shadowUDPConnector struct {
248+
Cipher *url.Userinfo
249+
}
250+
251+
// ShadowUDPConnector creates a Connector for shadowsocks UDP client.
252+
// It accepts a cipher info for shadowsocks data encryption/decryption.
253+
// The cipher must not be nil.
254+
func ShadowUDPConnector(cipher *url.Userinfo) Connector {
255+
return &shadowUDPConnector{Cipher: cipher}
256+
}
257+
258+
func (c *shadowUDPConnector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) {
259+
opts := &ConnectOptions{}
260+
for _, option := range options {
261+
option(opts)
262+
}
263+
264+
timeout := opts.Timeout
265+
if timeout <= 0 {
266+
timeout = ConnectTimeout
267+
}
268+
269+
conn.SetDeadline(time.Now().Add(timeout))
270+
defer conn.SetDeadline(time.Time{})
271+
272+
rawaddr, err := ss.RawAddr(addr)
273+
if err != nil {
274+
return nil, err
275+
}
276+
277+
var method, password string
278+
if c.Cipher != nil {
279+
method = c.Cipher.Username()
280+
password, _ = c.Cipher.Password()
281+
}
282+
283+
cipher, err := ss.NewCipher(method, password)
284+
if err != nil {
285+
return nil, err
286+
}
287+
288+
sc := ss.NewSecurePacketConn(&shadowPacketConn{conn}, cipher, false)
289+
return &shadowUDPConn{
290+
PacketConn: sc,
291+
raddr: conn.RemoteAddr(),
292+
header: rawaddr,
293+
}, nil
294+
}
295+
287296
type shadowUDPListener struct {
288297
ln net.PacketConn
289298
conns map[string]*udpServerConn
@@ -434,7 +443,9 @@ func (h *shadowUDPdHandler) transportUDP(sc net.Conn, cc net.PacketConn) error {
434443
errc := make(chan error, 1)
435444
go func() {
436445
for {
437-
b := make([]byte, mediumBufferSize)
446+
b := mPool.Get().([]byte)
447+
defer mPool.Put(b)
448+
438449
n, err := sc.Read(b[3:]) // add rsv and frag fields to make it the standard SOCKS5 UDP datagram
439450
if err != nil {
440451
// log.Logf("[ssu] %s - %s : %s", sc.RemoteAddr(), sc.LocalAddr(), err)
@@ -468,7 +479,9 @@ func (h *shadowUDPdHandler) transportUDP(sc net.Conn, cc net.PacketConn) error {
468479

469480
go func() {
470481
for {
471-
b := make([]byte, mediumBufferSize)
482+
b := mPool.Get().([]byte)
483+
defer mPool.Put(b)
484+
472485
n, addr, err := cc.ReadFrom(b)
473486
if err != nil {
474487
errc <- err
@@ -501,3 +514,65 @@ func (h *shadowUDPdHandler) transportUDP(sc net.Conn, cc net.PacketConn) error {
501514
}
502515
return err
503516
}
517+
518+
// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write,
519+
// we wrap around it to make io.Copy happy.
520+
type shadowConn struct {
521+
net.Conn
522+
}
523+
524+
func (c *shadowConn) Write(b []byte) (n int, err error) {
525+
n = len(b) // force byte length consistent
526+
_, err = c.Conn.Write(b)
527+
return
528+
}
529+
530+
type shadowUDPConn struct {
531+
net.PacketConn
532+
raddr net.Addr
533+
header []byte
534+
}
535+
536+
func (c *shadowUDPConn) Write(b []byte) (n int, err error) {
537+
n = len(b) // force byte length consistent
538+
if len(c.header) > 0 {
539+
b = append(c.header, b...)
540+
}
541+
_, err = c.PacketConn.WriteTo(b, c.raddr)
542+
return
543+
}
544+
545+
func (c *shadowUDPConn) Read(b []byte) (n int, err error) {
546+
buf := mPool.Get().([]byte)
547+
defer mPool.Put(buf)
548+
549+
n, _, err = c.PacketConn.ReadFrom(buf[3:])
550+
if err != nil {
551+
return
552+
}
553+
554+
dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(buf[:n+3]))
555+
if err != nil {
556+
return
557+
}
558+
n = copy(b, dgram.Data)
559+
return
560+
}
561+
562+
func (c *shadowUDPConn) RemoteAddr() net.Addr {
563+
return c.raddr
564+
}
565+
566+
type shadowPacketConn struct {
567+
net.Conn
568+
}
569+
570+
func (c *shadowPacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
571+
n, err = c.Conn.Read(b)
572+
addr = c.Conn.RemoteAddr()
573+
return
574+
}
575+
576+
func (c *shadowPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
577+
return c.Conn.Write(b)
578+
}

ss_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@ import (
55
"net/http/httptest"
66
"net/url"
77
"testing"
8+
"time"
89
)
910

11+
func init() {
12+
// ss.Debug = true
13+
}
14+
1015
var ssTests = []struct {
1116
clientCipher *url.Userinfo
1217
serverCipher *url.Userinfo
@@ -289,3 +294,72 @@ func BenchmarkSSProxyParallel(b *testing.B) {
289294
}
290295
})
291296
}
297+
298+
func shadowUDPRoundtrip(t *testing.T, host string, data []byte) error {
299+
ln, err := ShadowUDPListener("localhost:0", url.UserPassword("chacha20-ietf", "123456"), 0)
300+
if err != nil {
301+
return err
302+
}
303+
304+
client := &Client{
305+
Connector: ShadowUDPConnector(url.UserPassword("chacha20-ietf", "123456")),
306+
Transporter: UDPTransporter(),
307+
}
308+
309+
server := &Server{
310+
Handler: ShadowUDPdHandler(),
311+
Listener: ln,
312+
}
313+
314+
go server.Run()
315+
defer server.Close()
316+
317+
return udpRoundtrip(client, server, host, data)
318+
}
319+
320+
func TestShadowUDP(t *testing.T) {
321+
udpSrv := newUDPTestServer(udpTestHandler)
322+
udpSrv.Start()
323+
defer udpSrv.Close()
324+
325+
sendData := make([]byte, 128)
326+
rand.Read(sendData)
327+
err := shadowUDPRoundtrip(t, udpSrv.Addr(), sendData)
328+
if err != nil {
329+
t.Error(err)
330+
}
331+
}
332+
333+
// TODO: fix shadowsocks UDP relay benchmark.
334+
func BenchmarkShadowUDP(b *testing.B) {
335+
udpSrv := newUDPTestServer(udpTestHandler)
336+
udpSrv.Start()
337+
defer udpSrv.Close()
338+
339+
sendData := make([]byte, 128)
340+
rand.Read(sendData)
341+
342+
ln, err := ShadowUDPListener("localhost:0", url.UserPassword("chacha20-ietf", "123456"), 1000*time.Millisecond)
343+
if err != nil {
344+
b.Error(err)
345+
}
346+
347+
client := &Client{
348+
Connector: ShadowUDPConnector(url.UserPassword("chacha20-ietf", "123456")),
349+
Transporter: UDPTransporter(),
350+
}
351+
352+
server := &Server{
353+
Handler: ShadowUDPdHandler(),
354+
Listener: ln,
355+
}
356+
357+
go server.Run()
358+
defer server.Close()
359+
360+
for i := 0; i < b.N; i++ {
361+
if err := udpRoundtrip(client, server, udpSrv.Addr(), sendData); err != nil {
362+
b.Error(err)
363+
}
364+
}
365+
}

ssh_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ func sshRemoteForwardRoundtrip(t *testing.T, targetURL string, data []byte) (err
166166
return httpRoundtrip(c, targetURL, data)
167167
}
168168

169+
// TODO: fix this test
169170
func _TestSSHRemoteForward(t *testing.T) {
170171
httpSrv := httptest.NewServer(httpTestHandler)
171172
defer httpSrv.Close()

0 commit comments

Comments
 (0)