From 47c36461629051fa728769eb2a313ea427e3332c Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 6 Feb 2017 13:31:36 +0100 Subject: [PATCH 1/5] refine authentication reader --- common/crypto/auth.go | 46 +++++++++++++++++--------------------- common/crypto/auth_test.go | 15 +++++++++++-- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/common/crypto/auth.go b/common/crypto/auth.go index 334be21e3..b3a1aadf3 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -2,20 +2,16 @@ package crypto import ( "crypto/cipher" - "errors" "io" "v2ray.com/core/common" "v2ray.com/core/common/buf" + "v2ray.com/core/common/errors" "v2ray.com/core/common/serial" ) var ( - ErrAuthenticationFailed = errors.New("Authentication failed.") - errInsufficientBuffer = errors.New("Insufficient buffer.") - errInvalidNonce = errors.New("Invalid nonce.") - errInvalidLength = errors.New("Invalid buffer size.") ) type BytesGenerator interface { @@ -54,7 +50,7 @@ type AEADAuthenticator struct { func (v *AEADAuthenticator) Open(dst, cipherText []byte) ([]byte, error) { iv := v.NonceGenerator.Next() if len(iv) != v.AEAD.NonceSize() { - return nil, errInvalidNonce + return nil, errors.New("Crypto:AEADAuthenticator: Invalid nonce size: ", len(iv)) } additionalData := v.AdditionalDataGenerator.Next() @@ -64,7 +60,7 @@ func (v *AEADAuthenticator) Open(dst, cipherText []byte) ([]byte, error) { func (v *AEADAuthenticator) Seal(dst, plainText []byte) ([]byte, error) { iv := v.NonceGenerator.Next() if len(iv) != v.AEAD.NonceSize() { - return nil, errInvalidNonce + return nil, errors.New("Crypto:AEADAuthenticator: Invalid nonce size: ", len(iv)) } additionalData := v.AdditionalDataGenerator.Next() @@ -100,13 +96,13 @@ func (v *AuthenticationReader) NextChunk() error { return errInsufficientBuffer } if size > readerBufferSize-2 { - return errInvalidLength + return errors.New("Crypto:AuthenticationReader: Size too large: ", size) } if size == v.auth.Overhead() { return io.EOF } if size < v.auth.Overhead() { - return errors.New("AuthenticationReader: invalid packet size.") + return errors.New("AuthenticationReader: invalid packet size:", size) } cipherChunk := v.buffer.BytesRange(2, size+2) plainChunk, err := v.auth.Open(cipherChunk[:0], cipherChunk) @@ -132,26 +128,26 @@ func (v *AuthenticationReader) CopyChunk(b []byte) int { } func (v *AuthenticationReader) EnsureChunk() error { + if v.buffer.IsEmpty() { + v.buffer.Clear() + } + for { err := v.NextChunk() - if err == nil { - return nil + if err != errInsufficientBuffer { + return err } - if err == errInsufficientBuffer { - if v.buffer.IsEmpty() { - v.buffer.Clear() - } else { - leftover := v.buffer.Bytes() - common.Must(v.buffer.Reset(func(b []byte) (int, error) { - return copy(b, leftover), nil - })) - } - err = v.buffer.AppendSupplier(buf.ReadFrom(v.reader)) - if err == nil { - continue - } + + leftover := v.buffer.Bytes() + if len(leftover) > 0 { + common.Must(v.buffer.Reset(func(b []byte) (int, error) { + return copy(b, leftover), nil + })) + } + + if err := v.buffer.AppendSupplier(buf.ReadFrom(v.reader)); err != nil { + return err } - return err } } diff --git a/common/crypto/auth_test.go b/common/crypto/auth_test.go index defaa4b85..b70c7d918 100644 --- a/common/crypto/auth_test.go +++ b/common/crypto/auth_test.go @@ -7,6 +7,8 @@ import ( "io" "testing" + "time" + "v2ray.com/core/common/buf" . "v2ray.com/core/common/crypto" "v2ray.com/core/testing/assert" @@ -77,10 +79,10 @@ func TestAuthenticationReaderWriterPartial(t *testing.T) { payload := make([]byte, 8*1024) rand.Read(payload) - cache := buf.NewLocal(16 * 1024) iv := make([]byte, 12) rand.Read(iv) + cache := buf.NewLocal(16 * 1024) writer := NewAuthenticationWriter(&AEADAuthenticator{ AEAD: aead, NonceGenerator: &StaticBytesGenerator{ @@ -96,13 +98,22 @@ func TestAuthenticationReaderWriterPartial(t *testing.T) { _, err = writer.Write([]byte{}) assert.Error(err).IsNil() + pr, pw := io.Pipe() + go func() { + pw.Write(cache.BytesTo(1024)) + time.Sleep(time.Second * 2) + pw.Write(cache.BytesFrom(1024)) + time.Sleep(time.Second * 2) + pw.Close() + }() + reader := NewAuthenticationReader(&AEADAuthenticator{ AEAD: aead, NonceGenerator: &StaticBytesGenerator{ Content: iv, }, AdditionalDataGenerator: &NoOpBytesGenerator{}, - }, cache) + }, pr) actualPayload := make([]byte, 7*1024) nBytes, err = reader.Read(actualPayload) From ce34a25e66f2b95739a53186a8b0b3c9efe232bf Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 6 Feb 2017 13:49:03 +0100 Subject: [PATCH 2/5] fix build break --- proxy/vmess/encoding/auth.go | 4 ++-- transport/internet/kcp/crypt.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/proxy/vmess/encoding/auth.go b/proxy/vmess/encoding/auth.go index 610c2157e..92ad550c6 100644 --- a/proxy/vmess/encoding/auth.go +++ b/proxy/vmess/encoding/auth.go @@ -2,9 +2,9 @@ package encoding import ( "crypto/md5" + "errors" "hash/fnv" - "v2ray.com/core/common/crypto" "v2ray.com/core/common/serial" ) @@ -58,7 +58,7 @@ func (v *FnvAuthenticator) Seal(dst, nonce, plaintext, additionalData []byte) [] // Open implements AEAD.Open(). func (v *FnvAuthenticator) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { if serial.BytesToUint32(ciphertext[:4]) != Authenticate(ciphertext[4:]) { - return dst, crypto.ErrAuthenticationFailed + return dst, errors.New("VMess|FNV: Invalid authentication.") } return append(dst, ciphertext[4:]...), nil } diff --git a/transport/internet/kcp/crypt.go b/transport/internet/kcp/crypt.go index 7f5f4e2ed..0abc80154 100644 --- a/transport/internet/kcp/crypt.go +++ b/transport/internet/kcp/crypt.go @@ -2,9 +2,9 @@ package kcp import ( "crypto/cipher" + "errors" "hash/fnv" - "v2ray.com/core/common/crypto" "v2ray.com/core/common/serial" ) @@ -64,12 +64,12 @@ func (v *SimpleAuthenticator) Open(dst, nonce, cipherText, extra []byte) ([]byte fnvHash := fnv.New32a() fnvHash.Write(dst[4:]) if serial.BytesToUint32(dst[:4]) != fnvHash.Sum32() { - return nil, crypto.ErrAuthenticationFailed + return nil, errors.New("KCP:SimpleAuthenticator: Invalid auth.") } length := serial.BytesToUint16(dst[4:6]) if len(dst)-6 != int(length) { - return nil, crypto.ErrAuthenticationFailed + return nil, errors.New("KCP:SimpleAuthenticator: Invalid auth.") } return dst[6:], nil From 775b4ef542e4b44902cd733ec764a7e2c147e2d0 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 6 Feb 2017 14:02:17 +0100 Subject: [PATCH 3/5] update auth test --- common/crypto/auth_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/crypto/auth_test.go b/common/crypto/auth_test.go index b70c7d918..1ee2a7a6a 100644 --- a/common/crypto/auth_test.go +++ b/common/crypto/auth_test.go @@ -102,7 +102,11 @@ func TestAuthenticationReaderWriterPartial(t *testing.T) { go func() { pw.Write(cache.BytesTo(1024)) time.Sleep(time.Second * 2) - pw.Write(cache.BytesFrom(1024)) + pw.Write(cache.BytesRange(1024, 2048)) + time.Sleep(time.Second * 2) + pw.Write(cache.BytesRange(2048, 3072)) + time.Sleep(time.Second * 2) + pw.Write(cache.BytesFrom(3072)) time.Sleep(time.Second * 2) pw.Close() }() From 2897df5a7ad98c63579ce658b6fa59fd8cbe3a58 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 6 Feb 2017 14:06:41 +0100 Subject: [PATCH 4/5] don't copy leftoever if at head --- common/crypto/auth.go | 4 +++- common/crypto/auth_test.go | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/common/crypto/auth.go b/common/crypto/auth.go index b3a1aadf3..1f174dcf2 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -128,8 +128,10 @@ func (v *AuthenticationReader) CopyChunk(b []byte) int { } func (v *AuthenticationReader) EnsureChunk() error { + atHead := false if v.buffer.IsEmpty() { v.buffer.Clear() + atHead = true } for { @@ -139,7 +141,7 @@ func (v *AuthenticationReader) EnsureChunk() error { } leftover := v.buffer.Bytes() - if len(leftover) > 0 { + if !atHead && len(leftover) > 0 { common.Must(v.buffer.Reset(func(b []byte) (int, error) { return copy(b, leftover), nil })) diff --git a/common/crypto/auth_test.go b/common/crypto/auth_test.go index 1ee2a7a6a..b104cb864 100644 --- a/common/crypto/auth_test.go +++ b/common/crypto/auth_test.go @@ -91,6 +91,8 @@ func TestAuthenticationReaderWriterPartial(t *testing.T) { AdditionalDataGenerator: &NoOpBytesGenerator{}, }, cache) + writer.Write([]byte{'a', 'b', 'c', 'd'}) + nBytes, err := writer.Write(payload) assert.Error(err).IsNil() assert.Int(nBytes).Equals(len(payload)) @@ -120,6 +122,11 @@ func TestAuthenticationReaderWriterPartial(t *testing.T) { }, pr) actualPayload := make([]byte, 7*1024) + nBytes, err = reader.Read(actualPayload) + assert.Error(err).IsNil() + assert.Int(nBytes).Equals(4) + assert.Bytes(actualPayload[:nBytes]).Equals([]byte{'a', 'b', 'c', 'd'}) + nBytes, err = reader.Read(actualPayload) assert.Error(err).IsNil() assert.Int(nBytes).Equals(len(actualPayload)) From d27743b3b935f26dca61d52fc5d9ddfcdcb27fa2 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 6 Feb 2017 15:36:55 +0100 Subject: [PATCH 5/5] rewrite vmess test --- testing/scenarios/common.go | 2 +- testing/scenarios/data/test_4_client.json | 43 -- testing/scenarios/data/test_4_server.json | 53 -- testing/scenarios/dynamic_vmess_test.go | 51 -- testing/scenarios/vmess_test.go | 662 ++++++++++++++++++++++ 5 files changed, 663 insertions(+), 148 deletions(-) delete mode 100644 testing/scenarios/data/test_4_client.json delete mode 100644 testing/scenarios/data/test_4_server.json delete mode 100644 testing/scenarios/dynamic_vmess_test.go create mode 100644 testing/scenarios/vmess_test.go diff --git a/testing/scenarios/common.go b/testing/scenarios/common.go index 02a1f0f55..4433ec354 100644 --- a/testing/scenarios/common.go +++ b/testing/scenarios/common.go @@ -29,7 +29,7 @@ func xor(b []byte) []byte { } func readFrom(conn net.Conn, timeout time.Duration, length int) []byte { - b := make([]byte, 2048) + b := make([]byte, length) deadline := time.Now().Add(timeout) conn.SetReadDeadline(deadline) n, _ := io.ReadFull(conn, b[:length]) diff --git a/testing/scenarios/data/test_4_client.json b/testing/scenarios/data/test_4_client.json deleted file mode 100644 index 12e696512..000000000 --- a/testing/scenarios/data/test_4_client.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "port": 50030, - "log": { - "loglevel": "debug" - }, - "inbound": { - "listen": "127.0.0.1", - "protocol": "dokodemo-door", - "settings": { - "address": "127.0.0.1", - "port": 50032, - "network": "tcp", - "timeout": 0 - } - }, - "outbound": { - "protocol": "vmess", - "streamSettings": { - "network": "kcp" - }, - "settings": { - "vnext": [ - { - "address": "127.0.0.1", - "port": 50031, - "users": [ - { - "id": "d17a1af7-efa5-42ca-b7e9-6a35282d737f", - "alterId": 10 - } - ] - } - ] - } - }, - "transport": { - "kcpSettings": { - "header": { - "type": "srtp" - } - } - } -} diff --git a/testing/scenarios/data/test_4_server.json b/testing/scenarios/data/test_4_server.json deleted file mode 100644 index 2a1e548fc..000000000 --- a/testing/scenarios/data/test_4_server.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "port": 50031, - "log": { - "loglevel": "debug" - }, - "inbound": { - "listen": "127.0.0.1", - "protocol": "vmess", - "streamSettings": { - "network": "kcp" - }, - "settings": { - "clients": [ - { - "id": "d17a1af7-efa5-42ca-b7e9-6a35282d737f", - "level": 1, - "alterId": 10 - } - ], - "detour": { - "to": "detour" - } - } - }, - "outbound": { - "protocol": "freedom", - "settings": {} - }, - "inboundDetour": [ - { - "protocol": "vmess", - "listen": "127.0.0.1", - "port": "50035-50039", - "tag": "detour", - "settings": {}, - "streamSettings": { - "network": "kcp" - }, - "allocate": { - "strategy": "random", - "concurrency": 2, - "refresh": 5 - } - } - ], - "transport": { - "kcpSettings": { - "header": { - "type": "srtp" - } - } - } -} diff --git a/testing/scenarios/dynamic_vmess_test.go b/testing/scenarios/dynamic_vmess_test.go deleted file mode 100644 index 400400415..000000000 --- a/testing/scenarios/dynamic_vmess_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package scenarios - -import ( - "net" - "testing" - "time" - - v2net "v2ray.com/core/common/net" - "v2ray.com/core/testing/assert" - "v2ray.com/core/testing/servers/tcp" -) - -func TestDynamicVMess(t *testing.T) { - assert := assert.On(t) - - tcpServer := &tcp.Server{ - Port: v2net.Port(50032), - 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(InitializeServerSetOnce("test_4")).IsNil() - - for i := 0; i < 100; i++ { - conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ - IP: []byte{127, 0, 0, 1}, - Port: 50030, - }) - assert.Error(err).IsNil() - - payload := "dokodemo request." - nBytes, err := conn.Write([]byte(payload)) - assert.Error(err).IsNil() - assert.Int(nBytes).Equals(len(payload)) - - expectedResponse := "Processed: " + payload - response := readFrom(conn, time.Second, len(expectedResponse)) - assert.String(string(response)).Equals(expectedResponse) - - conn.Close() - } - - CloseAllServers() -} diff --git a/testing/scenarios/vmess_test.go b/testing/scenarios/vmess_test.go new file mode 100644 index 000000000..83b643aa9 --- /dev/null +++ b/testing/scenarios/vmess_test.go @@ -0,0 +1,662 @@ +package scenarios + +import ( + "crypto/rand" + "net" + "testing" + + "time" + + "sync" + + "v2ray.com/core" + "v2ray.com/core/app/log" + "v2ray.com/core/app/proxyman" + v2net "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" + "v2ray.com/core/common/serial" + "v2ray.com/core/common/uuid" + "v2ray.com/core/proxy/dokodemo" + "v2ray.com/core/proxy/freedom" + "v2ray.com/core/proxy/vmess" + "v2ray.com/core/proxy/vmess/inbound" + "v2ray.com/core/proxy/vmess/outbound" + "v2ray.com/core/testing/assert" + "v2ray.com/core/testing/servers/tcp" + "v2ray.com/core/transport/internet" +) + +func TestVMessDynamicPort(t *testing.T) { + assert := assert.On(t) + + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + assert.Error(err).IsNil() + defer tcpServer.Close() + + userID := protocol.NewID(uuid.New()) + serverPort := pickPort() + serverConfig := &core.Config{ + Inbound: []*proxyman.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + }), + }, + }, + Detour: &inbound.DetourConfig{ + To: "detour", + }, + }), + }, + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: &v2net.PortRange{ + From: uint32(serverPort + 1), + To: uint32(serverPort + 100), + }, + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + AllocationStrategy: &proxyman.AllocationStrategy{ + Type: proxyman.AllocationStrategy_Random, + Concurrency: &proxyman.AllocationStrategy_AllocationStrategyConcurrency{ + Value: 2, + }, + Refresh: &proxyman.AllocationStrategy_AllocationStrategyRefresh{ + Value: 5, + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{}), + Tag: "detour", + }, + }, + Outbound: []*proxyman.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: log.LogLevel_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + } + + clientPort := pickPort() + clientConfig := &core.Config{ + Inbound: []*proxyman.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: v2net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &v2net.NetworkList{ + Network: []v2net.Network{v2net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*proxyman.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: v2net.NewIPOrDomain(v2net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + }), + }, + }, + }, + }, + }), + }, + }, + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: log.LogLevel_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + } + + assert.Error(InitializeServerConfig(serverConfig)).IsNil() + assert.Error(InitializeServerConfig(clientConfig)).IsNil() + + for i := 0; i < 10; i++ { + conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert.Error(err).IsNil() + + payload := "dokodemo request." + nBytes, err := conn.Write([]byte(payload)) + assert.Error(err).IsNil() + assert.Int(nBytes).Equals(len(payload)) + + response := make([]byte, 1024) + nBytes, err = conn.Read(response) + assert.Error(err).IsNil() + assert.Bytes(response[:nBytes]).Equals(xor([]byte(payload))) + assert.Error(conn.Close()).IsNil() + } + + CloseAllServers() +} + +func TestVMessGCM(t *testing.T) { + assert := assert.On(t) + + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + assert.Error(err).IsNil() + defer tcpServer.Close() + + userID := protocol.NewID(uuid.New()) + serverPort := pickPort() + serverConfig := &core.Config{ + Inbound: []*proxyman.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + }), + }, + }, + }), + }, + }, + Outbound: []*proxyman.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: log.LogLevel_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + } + + clientPort := pickPort() + clientConfig := &core.Config{ + Inbound: []*proxyman.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: v2net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &v2net.NetworkList{ + Network: []v2net.Network{v2net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*proxyman.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: v2net.NewIPOrDomain(v2net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + SecuritySettings: &protocol.SecurityConfig{ + Type: protocol.SecurityType_AES128_GCM, + }, + }), + }, + }, + }, + }, + }), + }, + }, + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: log.LogLevel_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + } + + assert.Error(InitializeServerConfig(serverConfig)).IsNil() + assert.Error(InitializeServerConfig(clientConfig)).IsNil() + + var wg sync.WaitGroup + wg.Add(10) + for i := 0; i < 10; i++ { + go func() { + conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert.Error(err).IsNil() + + payload := make([]byte, 10240*1024) + rand.Read(payload) + + nBytes, err := conn.Write([]byte(payload)) + assert.Error(err).IsNil() + assert.Int(nBytes).Equals(len(payload)) + + response := readFrom(conn, time.Second*10, 10240*1024) + assert.Bytes(response).Equals(xor([]byte(payload))) + assert.Error(conn.Close()).IsNil() + wg.Done() + }() + } + wg.Wait() + + CloseAllServers() +} + +func TestVMessChacha20(t *testing.T) { + assert := assert.On(t) + + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + assert.Error(err).IsNil() + defer tcpServer.Close() + + userID := protocol.NewID(uuid.New()) + serverPort := pickPort() + serverConfig := &core.Config{ + Inbound: []*proxyman.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + }), + }, + }, + }), + }, + }, + Outbound: []*proxyman.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: log.LogLevel_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + } + + clientPort := pickPort() + clientConfig := &core.Config{ + Inbound: []*proxyman.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: v2net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &v2net.NetworkList{ + Network: []v2net.Network{v2net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*proxyman.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: v2net.NewIPOrDomain(v2net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + SecuritySettings: &protocol.SecurityConfig{ + Type: protocol.SecurityType_CHACHA20_POLY1305, + }, + }), + }, + }, + }, + }, + }), + }, + }, + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: log.LogLevel_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + } + + assert.Error(InitializeServerConfig(serverConfig)).IsNil() + assert.Error(InitializeServerConfig(clientConfig)).IsNil() + + var wg sync.WaitGroup + wg.Add(10) + for i := 0; i < 10; i++ { + go func() { + conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert.Error(err).IsNil() + + payload := make([]byte, 10240*1024) + rand.Read(payload) + + nBytes, err := conn.Write([]byte(payload)) + assert.Error(err).IsNil() + assert.Int(nBytes).Equals(len(payload)) + + response := readFrom(conn, time.Second*10, 10240*1024) + assert.Bytes(response).Equals(xor([]byte(payload))) + assert.Error(conn.Close()).IsNil() + wg.Done() + }() + } + wg.Wait() + + CloseAllServers() +} + +func TestVMessNone(t *testing.T) { + assert := assert.On(t) + + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + assert.Error(err).IsNil() + defer tcpServer.Close() + + userID := protocol.NewID(uuid.New()) + serverPort := pickPort() + serverConfig := &core.Config{ + Inbound: []*proxyman.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + }), + }, + }, + }), + }, + }, + Outbound: []*proxyman.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: log.LogLevel_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + } + + clientPort := pickPort() + clientConfig := &core.Config{ + Inbound: []*proxyman.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: v2net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &v2net.NetworkList{ + Network: []v2net.Network{v2net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*proxyman.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: v2net.NewIPOrDomain(v2net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + SecuritySettings: &protocol.SecurityConfig{ + Type: protocol.SecurityType_NONE, + }, + }), + }, + }, + }, + }, + }), + }, + }, + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: log.LogLevel_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + } + + assert.Error(InitializeServerConfig(serverConfig)).IsNil() + assert.Error(InitializeServerConfig(clientConfig)).IsNil() + + var wg sync.WaitGroup + wg.Add(10) + for i := 0; i < 10; i++ { + go func() { + conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert.Error(err).IsNil() + + payload := make([]byte, 10240*1024) + rand.Read(payload) + + nBytes, err := conn.Write([]byte(payload)) + assert.Error(err).IsNil() + assert.Int(nBytes).Equals(len(payload)) + + response := readFrom(conn, time.Second*10, 10240*1024) + assert.Bytes(response).Equals(xor([]byte(payload))) + assert.Error(conn.Close()).IsNil() + wg.Done() + }() + } + wg.Wait() + + CloseAllServers() +} + +func TestVMessKCP(t *testing.T) { + assert := assert.On(t) + + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + assert.Error(err).IsNil() + defer tcpServer.Close() + + userID := protocol.NewID(uuid.New()) + serverPort := pickPort() + serverConfig := &core.Config{ + Inbound: []*proxyman.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_MKCP, + }, + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + }), + }, + }, + }), + }, + }, + Outbound: []*proxyman.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: log.LogLevel_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + } + + clientPort := pickPort() + clientConfig := &core.Config{ + Inbound: []*proxyman.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: v2net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &v2net.NetworkList{ + Network: []v2net.Network{v2net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*proxyman.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: v2net.NewIPOrDomain(v2net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + SecuritySettings: &protocol.SecurityConfig{ + Type: protocol.SecurityType_AES128_GCM, + }, + }), + }, + }, + }, + }, + }), + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_MKCP, + }, + }), + }, + }, + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: log.LogLevel_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + } + + assert.Error(InitializeServerConfig(serverConfig)).IsNil() + assert.Error(InitializeServerConfig(clientConfig)).IsNil() + + var wg sync.WaitGroup + wg.Add(10) + for i := 0; i < 10; i++ { + go func() { + conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert.Error(err).IsNil() + + payload := make([]byte, 10240*1024) + rand.Read(payload) + + nBytes, err := conn.Write([]byte(payload)) + assert.Error(err).IsNil() + assert.Int(nBytes).Equals(len(payload)) + + response := readFrom(conn, time.Second*10, 10240*1024) + assert.Bytes(response).Equals(xor([]byte(payload))) + assert.Error(conn.Close()).IsNil() + wg.Done() + }() + } + wg.Wait() + + CloseAllServers() +}