diff --git a/proxy/shadowsocks/config_json_test.go b/proxy/shadowsocks/config_json_test.go new file mode 100644 index 00000000..36563b10 --- /dev/null +++ b/proxy/shadowsocks/config_json_test.go @@ -0,0 +1,27 @@ +// +build json + +package shadowsocks + +import ( + "encoding/json" + "testing" + + v2testing "github.com/v2ray/v2ray-core/testing" + "github.com/v2ray/v2ray-core/testing/assert" +) + +func TestConfigParsing(t *testing.T) { + v2testing.Current(t) + + rawJson := `{ + "method": "aes-128-cfb", + "password": "v2ray-password" + }` + + config := new(Config) + err := json.Unmarshal([]byte(rawJson), config) + assert.Error(err).IsNil() + + assert.Int(config.Cipher.KeySize()).Equals(16) + assert.Bytes(config.Key).Equals([]byte{160, 224, 26, 2, 22, 110, 9, 80, 65, 52, 80, 20, 38, 243, 224, 241}) +} diff --git a/proxy/shadowsocks/shadowsocks.go b/proxy/shadowsocks/shadowsocks.go index 168551eb..fdeda9ba 100644 --- a/proxy/shadowsocks/shadowsocks.go +++ b/proxy/shadowsocks/shadowsocks.go @@ -3,6 +3,7 @@ package shadowsocks import ( + "crypto/rand" "sync" "github.com/v2ray/v2ray-core/app" @@ -80,7 +81,10 @@ func (this *Shadowsocks) handleConnection(conn *listener.TCPConn) { packet := v2net.NewPacket(v2net.TCPDestination(request.Address, request.Port), nil, true) ray := this.space.PacketDispatcher().DispatchToOutbound(packet) - writer, err := this.config.Cipher.NewEncodingStream(key, iv, conn) + respIv := make([]byte, this.config.Cipher.IVSize()) + rand.Read(respIv) + + writer, err := this.config.Cipher.NewEncodingStream(key, respIv, conn) if err != nil { log.Error("Shadowsocks: Failed to create encoding stream: ", err) return @@ -89,7 +93,18 @@ func (this *Shadowsocks) handleConnection(conn *listener.TCPConn) { var writeFinish sync.Mutex writeFinish.Lock() go func() { - v2net.ChanToWriter(writer, ray.InboundOutput()) + firstChunk := alloc.NewBuffer().Clear() + defer firstChunk.Release() + + firstChunk.Append(respIv) + + if payload, ok := <-ray.InboundOutput(); ok { + firstChunk.Append(payload.Value) + payload.Release() + + writer.Write(firstChunk.Value) + v2net.ChanToWriter(writer, ray.InboundOutput()) + } writeFinish.Unlock() }() diff --git a/testing/scenarios/data/test_6_server.json b/testing/scenarios/data/test_6_server.json new file mode 100644 index 00000000..5c7d4dbc --- /dev/null +++ b/testing/scenarios/data/test_6_server.json @@ -0,0 +1,14 @@ +{ + "port": 50051, + "inbound": { + "protocol": "shadowsocks", + "settings": { + "method": "aes-256-cfb", + "password": "v2ray-password" + } + }, + "outbound": { + "protocol": "freedom", + "settings": {} + } +} diff --git a/testing/scenarios/server_env.go b/testing/scenarios/server_env.go index 0bc010c1..fbf81373 100644 --- a/testing/scenarios/server_env.go +++ b/testing/scenarios/server_env.go @@ -13,6 +13,7 @@ import ( _ "github.com/v2ray/v2ray-core/proxy/dokodemo" _ "github.com/v2ray/v2ray-core/proxy/freedom" _ "github.com/v2ray/v2ray-core/proxy/http" + _ "github.com/v2ray/v2ray-core/proxy/shadowsocks" _ "github.com/v2ray/v2ray-core/proxy/socks" _ "github.com/v2ray/v2ray-core/proxy/vmess/inbound" _ "github.com/v2ray/v2ray-core/proxy/vmess/outbound" @@ -27,17 +28,23 @@ func TestFile(filename string) string { } func InitializeServerSetOnce(testcase string) error { - err := InitializeServer(TestFile(testcase + "_server.json")) - if err != nil { + if err := InitializeServerServer(testcase); err != nil { return err } - err = InitializeServer(TestFile(testcase + "_client.json")) - if err != nil { + if err := InitializeServerClient(testcase); err != nil { return err } return nil } +func InitializeServerServer(testcase string) error { + return InitializeServer(TestFile(testcase + "_server.json")) +} + +func InitializeServerClient(testcase string) error { + return InitializeServer(TestFile(testcase + "_client.json")) +} + func InitializeServer(configFile string) error { config, err := point.LoadConfig(configFile) if err != nil { diff --git a/testing/scenarios/shadowsocks_test.go b/testing/scenarios/shadowsocks_test.go new file mode 100644 index 00000000..3ea107d1 --- /dev/null +++ b/testing/scenarios/shadowsocks_test.go @@ -0,0 +1,54 @@ +package scenarios + +import ( + "net" + "testing" + + v2net "github.com/v2ray/v2ray-core/common/net" + v2testing "github.com/v2ray/v2ray-core/testing" + "github.com/v2ray/v2ray-core/testing/assert" + "github.com/v2ray/v2ray-core/testing/servers/tcp" + + ssclient "github.com/shadowsocks/shadowsocks-go/shadowsocks" +) + +func TestShadowsocksTCP(t *testing.T) { + v2testing.Current(t) + + tcpServer := &tcp.Server{ + Port: v2net.Port(50052), + MsgProcessor: func(data []byte) []byte { + buffer := make([]byte, 0, 2048) + buffer = append(buffer, []byte("Processed: ")...) + buffer = append(buffer, data...) + return buffer + }, + } + _, err := tcpServer.Start() + assert.Error(err).IsNil() + defer tcpServer.Close() + + assert.Error(InitializeServerServer("test_6")).IsNil() + + cipher, err := ssclient.NewCipher("aes-256-cfb", "v2ray-password") + assert.Error(err).IsNil() + + rawAddr := []byte{1, 127, 0, 0, 1, 0xc3, 0x84} // 127.0.0.1:50052 + conn, err := ssclient.DialWithRawAddr(rawAddr, "127.0.0.1:50051", cipher) + assert.Error(err).IsNil() + + payload := "shadowsocks request." + nBytes, err := conn.Write([]byte(payload)) + assert.Error(err).IsNil() + assert.Int(nBytes).Equals(len(payload)) + + conn.Conn.(*net.TCPConn).CloseWrite() + + response := make([]byte, 1024) + nBytes, err = conn.Read(response) + assert.Error(err).IsNil() + assert.StringLiteral("Processed: " + payload).Equals(string(response[:nBytes])) + conn.Close() + + CloseAllServers() +}