From b811d81f4f59f759d8528608b2586715fc1f732a Mon Sep 17 00:00:00 2001 From: jesus Date: Mon, 28 Jul 2025 13:44:53 +0400 Subject: [PATCH] chore: remove vmess from core. --- common/platform/platform.go | 1 - common/protocol/server_spec_test.go | 10 +- core/xray_test.go | 8 +- infra/conf/vmess.go | 165 --- infra/conf/vmess_test.go | 111 -- infra/conf/xray.go | 2 - infra/conf/xray_test.go | 32 +- main/commands/all/api/inbound_user_add.go | 3 - main/distro/all/all.go | 3 +- proxy/vmess/account.go | 67 - proxy/vmess/account.pb.go | 163 --- proxy/vmess/account.proto | 19 - proxy/vmess/aead/authid.go | 119 -- proxy/vmess/aead/authid_test.go | 127 -- proxy/vmess/aead/consts.go | 14 - proxy/vmess/aead/encrypt.go | 168 --- proxy/vmess/aead/encrypt_test.go | 104 -- proxy/vmess/aead/kdf.go | 33 - proxy/vmess/encoding/auth.go | 99 -- proxy/vmess/encoding/client.go | 344 ----- proxy/vmess/encoding/commands.go | 145 -- proxy/vmess/encoding/commands_test.go | 55 - proxy/vmess/encoding/encoding.go | 17 - proxy/vmess/encoding/encoding_test.go | 153 --- proxy/vmess/encoding/server.go | 451 ------- proxy/vmess/inbound/config.go | 9 - proxy/vmess/inbound/config.pb.go | 263 ---- proxy/vmess/inbound/config.proto | 24 - proxy/vmess/inbound/inbound.go | 365 ----- proxy/vmess/outbound/command.go | 41 - proxy/vmess/outbound/config.go | 1 - proxy/vmess/outbound/config.pb.go | 142 -- proxy/vmess/outbound/config.proto | 13 - proxy/vmess/outbound/outbound.go | 249 ---- proxy/vmess/validator.go | 127 -- proxy/vmess/validator_test.go | 34 - proxy/vmess/vmess.go | 6 - testing/scenarios/command_test.go | 34 +- testing/scenarios/dokodemo_test.go | 22 +- testing/scenarios/feature_test.go | 49 +- testing/scenarios/policy_test.go | 125 +- testing/scenarios/reverse_test.go | 29 +- testing/scenarios/tls_test.go | 86 +- testing/scenarios/transport_test.go | 15 +- testing/scenarios/vmess_test.go | 1475 --------------------- 45 files changed, 145 insertions(+), 5377 deletions(-) delete mode 100644 infra/conf/vmess.go delete mode 100644 infra/conf/vmess_test.go delete mode 100644 proxy/vmess/account.go delete mode 100644 proxy/vmess/account.pb.go delete mode 100644 proxy/vmess/account.proto delete mode 100644 proxy/vmess/aead/authid.go delete mode 100644 proxy/vmess/aead/authid_test.go delete mode 100644 proxy/vmess/aead/consts.go delete mode 100644 proxy/vmess/aead/encrypt.go delete mode 100644 proxy/vmess/aead/encrypt_test.go delete mode 100644 proxy/vmess/aead/kdf.go delete mode 100644 proxy/vmess/encoding/auth.go delete mode 100644 proxy/vmess/encoding/client.go delete mode 100644 proxy/vmess/encoding/commands.go delete mode 100644 proxy/vmess/encoding/commands_test.go delete mode 100644 proxy/vmess/encoding/encoding.go delete mode 100644 proxy/vmess/encoding/encoding_test.go delete mode 100644 proxy/vmess/encoding/server.go delete mode 100644 proxy/vmess/inbound/config.go delete mode 100644 proxy/vmess/inbound/config.pb.go delete mode 100644 proxy/vmess/inbound/config.proto delete mode 100644 proxy/vmess/inbound/inbound.go delete mode 100644 proxy/vmess/outbound/command.go delete mode 100644 proxy/vmess/outbound/config.go delete mode 100644 proxy/vmess/outbound/config.pb.go delete mode 100644 proxy/vmess/outbound/config.proto delete mode 100644 proxy/vmess/outbound/outbound.go delete mode 100644 proxy/vmess/validator.go delete mode 100644 proxy/vmess/validator_test.go delete mode 100644 proxy/vmess/vmess.go delete mode 100644 testing/scenarios/vmess_test.go diff --git a/common/platform/platform.go b/common/platform/platform.go index b865dc0db0fe..0b9db98bf754 100644 --- a/common/platform/platform.go +++ b/common/platform/platform.go @@ -17,7 +17,6 @@ const ( UseReadV = "xray.buf.readv" UseFreedomSplice = "xray.buf.splice" - UseVmessPadding = "xray.vmess.padding" UseCone = "xray.cone.disabled" BufferSize = "xray.ray.buffer.size" diff --git a/common/protocol/server_spec_test.go b/common/protocol/server_spec_test.go index 10dfb8c577f2..8236d21a344a 100644 --- a/common/protocol/server_spec_test.go +++ b/common/protocol/server_spec_test.go @@ -1,6 +1,7 @@ package protocol_test import ( + "github.com/xtls/xray-core/proxy/vless" "strings" "testing" "time" @@ -9,7 +10,6 @@ import ( "github.com/xtls/xray-core/common/net" . "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/uuid" - "github.com/xtls/xray-core/proxy/vmess" ) func TestAlwaysValidStrategy(t *testing.T) { @@ -44,7 +44,7 @@ func TestUserInServerSpec(t *testing.T) { uuid1 := uuid.New() uuid2 := uuid.New() - toAccount := func(a *vmess.Account) Account { + toAccount := func(a *vless.Account) Account { account, err := a.AsAccount() common.Must(err) return account @@ -52,11 +52,11 @@ func TestUserInServerSpec(t *testing.T) { spec := NewServerSpec(net.Destination{}, AlwaysValid(), &MemoryUser{ Email: "test1@example.com", - Account: toAccount(&vmess.Account{Id: uuid1.String()}), + Account: toAccount(&vless.Account{Id: uuid1.String()}), }) if spec.HasUser(&MemoryUser{ Email: "test1@example.com", - Account: toAccount(&vmess.Account{Id: uuid2.String()}), + Account: toAccount(&vless.Account{Id: uuid2.String()}), }) { t.Error("has user: ", uuid2) } @@ -64,7 +64,7 @@ func TestUserInServerSpec(t *testing.T) { spec.AddUser(&MemoryUser{Email: "test2@example.com"}) if !spec.HasUser(&MemoryUser{ Email: "test1@example.com", - Account: toAccount(&vmess.Account{Id: uuid1.String()}), + Account: toAccount(&vless.Account{Id: uuid1.String()}), }) { t.Error("not having user: ", uuid1) } diff --git a/core/xray_test.go b/core/xray_test.go index f4cb11abe7a0..03cf302a5090 100644 --- a/core/xray_test.go +++ b/core/xray_test.go @@ -1,6 +1,8 @@ package core_test import ( + "github.com/xtls/xray-core/proxy/vless" + "github.com/xtls/xray-core/proxy/vless/outbound" "testing" "github.com/xtls/xray-core/app/dispatcher" @@ -15,8 +17,6 @@ import ( "github.com/xtls/xray-core/features/dns/localdns" _ "github.com/xtls/xray-core/main/distro/all" "github.com/xtls/xray-core/proxy/dokodemo" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/outbound" "github.com/xtls/xray-core/testing/servers/tcp" "google.golang.org/protobuf/proto" ) @@ -63,13 +63,13 @@ func TestXrayClose(t *testing.T) { Outbound: []*OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(0), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, diff --git a/infra/conf/vmess.go b/infra/conf/vmess.go deleted file mode 100644 index 90ab92d421fd..000000000000 --- a/infra/conf/vmess.go +++ /dev/null @@ -1,165 +0,0 @@ -package conf - -import ( - "encoding/json" - "strings" - - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/serial" - "github.com/xtls/xray-core/common/uuid" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/inbound" - "github.com/xtls/xray-core/proxy/vmess/outbound" - "google.golang.org/protobuf/proto" -) - -type VMessAccount struct { - ID string `json:"id"` - Security string `json:"security"` - Experiments string `json:"experiments"` -} - -// Build implements Buildable -func (a *VMessAccount) Build() *vmess.Account { - var st protocol.SecurityType - switch strings.ToLower(a.Security) { - case "aes-128-gcm": - st = protocol.SecurityType_AES128_GCM - case "chacha20-poly1305": - st = protocol.SecurityType_CHACHA20_POLY1305 - case "auto": - st = protocol.SecurityType_AUTO - case "none": - st = protocol.SecurityType_NONE - case "zero": - st = protocol.SecurityType_ZERO - default: - st = protocol.SecurityType_AUTO - } - return &vmess.Account{ - Id: a.ID, - SecuritySettings: &protocol.SecurityConfig{ - Type: st, - }, - TestsEnabled: a.Experiments, - } -} - -type VMessDetourConfig struct { - ToTag string `json:"to"` -} - -// Build implements Buildable -func (c *VMessDetourConfig) Build() *inbound.DetourConfig { - return &inbound.DetourConfig{ - To: c.ToTag, - } -} - -type VMessDefaultConfig struct { - Level byte `json:"level"` -} - -// Build implements Buildable -func (c *VMessDefaultConfig) Build() *inbound.DefaultConfig { - config := new(inbound.DefaultConfig) - config.Level = uint32(c.Level) - return config -} - -type VMessInboundConfig struct { - Users []json.RawMessage `json:"clients"` - Defaults *VMessDefaultConfig `json:"default"` - DetourConfig *VMessDetourConfig `json:"detour"` -} - -// Build implements Buildable -func (c *VMessInboundConfig) Build() (proto.Message, error) { - config := &inbound.Config{} - - if c.Defaults != nil { - config.Default = c.Defaults.Build() - } - - if c.DetourConfig != nil { - config.Detour = c.DetourConfig.Build() - } - - config.User = make([]*protocol.User, len(c.Users)) - for idx, rawData := range c.Users { - user := new(protocol.User) - if err := json.Unmarshal(rawData, user); err != nil { - return nil, errors.New("invalid VMess user").Base(err) - } - account := new(VMessAccount) - if err := json.Unmarshal(rawData, account); err != nil { - return nil, errors.New("invalid VMess user").Base(err) - } - - u, err := uuid.ParseString(account.ID) - if err != nil { - return nil, err - } - account.ID = u.String() - - user.Account = serial.ToTypedMessage(account.Build()) - config.User[idx] = user - } - - return config, nil -} - -type VMessOutboundTarget struct { - Address *Address `json:"address"` - Port uint16 `json:"port"` - Users []json.RawMessage `json:"users"` -} - -type VMessOutboundConfig struct { - Receivers []*VMessOutboundTarget `json:"vnext"` -} - -// Build implements Buildable -func (c *VMessOutboundConfig) Build() (proto.Message, error) { - config := new(outbound.Config) - - if len(c.Receivers) == 0 { - return nil, errors.New("0 VMess receiver configured") - } - serverSpecs := make([]*protocol.ServerEndpoint, len(c.Receivers)) - for idx, rec := range c.Receivers { - if len(rec.Users) == 0 { - return nil, errors.New("0 user configured for VMess outbound") - } - if rec.Address == nil { - return nil, errors.New("address is not set in VMess outbound config") - } - spec := &protocol.ServerEndpoint{ - Address: rec.Address.Build(), - Port: uint32(rec.Port), - } - for _, rawUser := range rec.Users { - user := new(protocol.User) - if err := json.Unmarshal(rawUser, user); err != nil { - return nil, errors.New("invalid VMess user").Base(err) - } - account := new(VMessAccount) - if err := json.Unmarshal(rawUser, account); err != nil { - return nil, errors.New("invalid VMess user").Base(err) - } - - u, err := uuid.ParseString(account.ID) - if err != nil { - return nil, err - } - account.ID = u.String() - - user.Account = serial.ToTypedMessage(account.Build()) - spec.User = append(spec.User, user) - } - serverSpecs[idx] = spec - } - config.Receiver = serverSpecs - return config, nil -} diff --git a/infra/conf/vmess_test.go b/infra/conf/vmess_test.go deleted file mode 100644 index 8adda170542a..000000000000 --- a/infra/conf/vmess_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package conf_test - -import ( - "testing" - - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/serial" - . "github.com/xtls/xray-core/infra/conf" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/inbound" - "github.com/xtls/xray-core/proxy/vmess/outbound" -) - -func TestVMessOutbound(t *testing.T) { - creator := func() Buildable { - return new(VMessOutboundConfig) - } - - runMultiTestCase(t, []TestCase{ - { - Input: `{ - "vnext": [{ - "address": "127.0.0.1", - "port": 80, - "users": [ - { - "id": "e641f5ad-9397-41e3-bf1a-e8740dfed019", - "email": "love@example.com", - "level": 255 - } - ] - }] - }`, - Parser: loadJSON(creator), - Output: &outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: &net.IPOrDomain{ - Address: &net.IPOrDomain_Ip{ - Ip: []byte{127, 0, 0, 1}, - }, - }, - Port: 80, - User: []*protocol.User{ - { - Email: "love@example.com", - Level: 255, - Account: serial.ToTypedMessage(&vmess.Account{ - Id: "e641f5ad-9397-41e3-bf1a-e8740dfed019", - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AUTO, - }, - }), - }, - }, - }, - }, - }, - }, - }) -} - -func TestVMessInbound(t *testing.T) { - creator := func() Buildable { - return new(VMessInboundConfig) - } - - runMultiTestCase(t, []TestCase{ - { - Input: `{ - "clients": [ - { - "id": "27848739-7e62-4138-9fd3-098a63964b6b", - "level": 0, - "email": "love@example.com", - "security": "aes-128-gcm" - } - ], - "default": { - "level": 0 - }, - "detour": { - "to": "tag_to_detour" - }, - "disableInsecureEncryption": true - }`, - Parser: loadJSON(creator), - Output: &inbound.Config{ - User: []*protocol.User{ - { - Level: 0, - Email: "love@example.com", - Account: serial.ToTypedMessage(&vmess.Account{ - Id: "27848739-7e62-4138-9fd3-098a63964b6b", - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, - }), - }, - }, - Default: &inbound.DefaultConfig{ - Level: 0, - }, - Detour: &inbound.DetourConfig{ - To: "tag_to_detour", - }, - }, - }, - }) -} diff --git a/infra/conf/xray.go b/infra/conf/xray.go index f1d9fb08030a..52b201216609 100644 --- a/infra/conf/xray.go +++ b/infra/conf/xray.go @@ -27,7 +27,6 @@ var ( "mixed": func() interface{} { return new(SocksServerConfig) }, "socks": func() interface{} { return new(SocksServerConfig) }, "vless": func() interface{} { return new(VLessInboundConfig) }, - "vmess": func() interface{} { return new(VMessInboundConfig) }, "trojan": func() interface{} { return new(TrojanServerConfig) }, "wireguard": func() interface{} { return &WireGuardConfig{IsClient: false} }, }, "protocol", "settings") @@ -40,7 +39,6 @@ var ( "shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) }, "socks": func() interface{} { return new(SocksClientConfig) }, "vless": func() interface{} { return new(VLessOutboundConfig) }, - "vmess": func() interface{} { return new(VMessOutboundConfig) }, "trojan": func() interface{} { return new(TrojanClientConfig) }, "dns": func() interface{} { return new(DNSOutboundConfig) }, "wireguard": func() interface{} { return &WireGuardConfig{IsClient: true} }, diff --git a/infra/conf/xray_test.go b/infra/conf/xray_test.go index 1c0fff8d9c6a..a7eb89cc19ac 100644 --- a/infra/conf/xray_test.go +++ b/infra/conf/xray_test.go @@ -2,6 +2,8 @@ package conf_test import ( "encoding/json" + "github.com/xtls/xray-core/proxy/vless" + "github.com/xtls/xray-core/proxy/vless/inbound" "reflect" "testing" @@ -17,8 +19,7 @@ import ( "github.com/xtls/xray-core/common/serial" core "github.com/xtls/xray-core/core" . "github.com/xtls/xray-core/infra/conf" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/inbound" + "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/tls" "github.com/xtls/xray-core/transport/internet/websocket" @@ -56,13 +57,14 @@ func TestXrayConfig(t *testing.T) { }, "security": "tls" }, - "protocol": "vmess", + "protocol": "vless", "port": "443-500", "allocate": { "strategy": "random", "concurrency": 3 }, "settings": { + "decryption": "none", "clients": [ { "security": "aes-128-gcm", @@ -149,14 +151,12 @@ func TestXrayConfig(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Decryption: "none", + Clients: []*protocol.User{ { Level: 0, - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: "0cdf8a45-303d-4fed-9780-29aa7f54175e", - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, }), }, }, @@ -249,45 +249,45 @@ func TestConfig_Override(t *testing.T) { }, { "replace/inbounds", - &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos0"}, {Protocol: "vmess", Tag: "pos1"}}}, + &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos0"}, {Protocol: "vless", Tag: "pos1"}}}, &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}}}, "", &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos0"}, {Tag: "pos1", Protocol: "kcp"}}}, }, { "replace/inbounds-replaceall", - &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos0"}, {Protocol: "vmess", Tag: "pos1"}}}, + &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos0"}, {Protocol: "vless", Tag: "pos1"}}}, &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}}}, "", &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos0"}, {Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}}}, }, { "replace/notag-append", - &Config{InboundConfigs: []InboundDetourConfig{{}, {Protocol: "vmess"}}}, + &Config{InboundConfigs: []InboundDetourConfig{{}, {Protocol: "vless"}}}, &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}}}, "", - &Config{InboundConfigs: []InboundDetourConfig{{}, {Protocol: "vmess"}, {Tag: "pos1", Protocol: "kcp"}}}, + &Config{InboundConfigs: []InboundDetourConfig{{}, {Protocol: "vless"}, {Tag: "pos1", Protocol: "kcp"}}}, }, { "replace/outbounds", - &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Protocol: "vmess", Tag: "pos1"}}}, + &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Protocol: "vless", Tag: "pos1"}}}, &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}}}, "", &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Tag: "pos1", Protocol: "kcp"}}}, }, { "replace/outbounds-prepend", - &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Protocol: "vmess", Tag: "pos1"}, {Tag: "pos3"}}}, + &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Protocol: "vless", Tag: "pos1"}, {Tag: "pos3"}}}, &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}, {Tag: "pos4", Protocol: "kcp"}}}, "config.json", &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos2", Protocol: "kcp"}, {Tag: "pos4", Protocol: "kcp"}, {Tag: "pos0"}, {Tag: "pos1", Protocol: "kcp"}, {Tag: "pos3"}}}, }, { "replace/outbounds-append", - &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Protocol: "vmess", Tag: "pos1"}}}, + &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Protocol: "vless", Tag: "pos1"}}}, &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos2", Protocol: "kcp"}}}, "config_tail.json", - &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Protocol: "vmess", Tag: "pos1"}, {Tag: "pos2", Protocol: "kcp"}}}, + &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Protocol: "vless", Tag: "pos1"}, {Tag: "pos2", Protocol: "kcp"}}}, }, } for _, tt := range tests { diff --git a/main/commands/all/api/inbound_user_add.go b/main/commands/all/api/inbound_user_add.go index 9f9acf7e1bcb..bf467ca7ee95 100644 --- a/main/commands/all/api/inbound_user_add.go +++ b/main/commands/all/api/inbound_user_add.go @@ -16,7 +16,6 @@ import ( "github.com/xtls/xray-core/proxy/shadowsocks_2022" "github.com/xtls/xray-core/proxy/trojan" vlessin "github.com/xtls/xray-core/proxy/vless/inbound" - vmessin "github.com/xtls/xray-core/proxy/vmess/inbound" "github.com/xtls/xray-core/main/commands/base" ) @@ -77,8 +76,6 @@ func extractInboundUsers(inb *core.InboundHandlerConfig) []*protocol.User { return nil } switch ty := inst.(type) { - case *vmessin.Config: - return ty.User case *vlessin.Config: return ty.Clients case *trojan.ServerConfig: diff --git a/main/distro/all/all.go b/main/distro/all/all.go index 198abb3fd2c2..1cc3ed6e5ca3 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -45,8 +45,7 @@ import ( _ "github.com/xtls/xray-core/proxy/trojan" _ "github.com/xtls/xray-core/proxy/vless/inbound" _ "github.com/xtls/xray-core/proxy/vless/outbound" - _ "github.com/xtls/xray-core/proxy/vmess/inbound" - _ "github.com/xtls/xray-core/proxy/vmess/outbound" + _ "github.com/xtls/xray-core/proxy/wireguard" // Transports diff --git a/proxy/vmess/account.go b/proxy/vmess/account.go deleted file mode 100644 index df8ba52bec14..000000000000 --- a/proxy/vmess/account.go +++ /dev/null @@ -1,67 +0,0 @@ -package vmess - -import ( - "google.golang.org/protobuf/proto" - "strings" - - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/uuid" -) - -// MemoryAccount is an in-memory form of VMess account. -type MemoryAccount struct { - // ID is the main ID of the account. - ID *protocol.ID - // Security type of the account. Used for client connections. - Security protocol.SecurityType - - AuthenticatedLengthExperiment bool - NoTerminationSignal bool -} - -// Equals implements protocol.Account. -func (a *MemoryAccount) Equals(account protocol.Account) bool { - vmessAccount, ok := account.(*MemoryAccount) - if !ok { - return false - } - return a.ID.Equals(vmessAccount.ID) -} - -func (a *MemoryAccount) ToProto() proto.Message { - var test = "" - if a.AuthenticatedLengthExperiment { - test = "AuthenticatedLength|" - } - if a.NoTerminationSignal { - test = test + "NoTerminationSignal" - } - return &Account{ - Id: a.ID.String(), - TestsEnabled: test, - SecuritySettings: &protocol.SecurityConfig{Type: a.Security}, - } -} - -// AsAccount implements protocol.Account. -func (a *Account) AsAccount() (protocol.Account, error) { - id, err := uuid.ParseString(a.Id) - if err != nil { - return nil, errors.New("failed to parse ID").Base(err).AtError() - } - protoID := protocol.NewID(id) - var AuthenticatedLength, NoTerminationSignal bool - if strings.Contains(a.TestsEnabled, "AuthenticatedLength") { - AuthenticatedLength = true - } - if strings.Contains(a.TestsEnabled, "NoTerminationSignal") { - NoTerminationSignal = true - } - return &MemoryAccount{ - ID: protoID, - Security: a.SecuritySettings.GetSecurityType(), - AuthenticatedLengthExperiment: AuthenticatedLength, - NoTerminationSignal: NoTerminationSignal, - }, nil -} diff --git a/proxy/vmess/account.pb.go b/proxy/vmess/account.pb.go deleted file mode 100644 index 44a8f73a6aac..000000000000 --- a/proxy/vmess/account.pb.go +++ /dev/null @@ -1,163 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 -// source: proxy/vmess/account.proto - -package vmess - -import ( - protocol "github.com/xtls/xray-core/common/protocol" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Account struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // ID of the account, in the form of a UUID, e.g., - // "66ad4540-b58c-4ad2-9926-ea63445a9b57". - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - // Security settings. Only applies to client side. - SecuritySettings *protocol.SecurityConfig `protobuf:"bytes,3,opt,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"` - // Define tests enabled for this account - TestsEnabled string `protobuf:"bytes,4,opt,name=tests_enabled,json=testsEnabled,proto3" json:"tests_enabled,omitempty"` -} - -func (x *Account) Reset() { - *x = Account{} - mi := &file_proxy_vmess_account_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Account) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Account) ProtoMessage() {} - -func (x *Account) ProtoReflect() protoreflect.Message { - mi := &file_proxy_vmess_account_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Account.ProtoReflect.Descriptor instead. -func (*Account) Descriptor() ([]byte, []int) { - return file_proxy_vmess_account_proto_rawDescGZIP(), []int{0} -} - -func (x *Account) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *Account) GetSecuritySettings() *protocol.SecurityConfig { - if x != nil { - return x.SecuritySettings - } - return nil -} - -func (x *Account) GetTestsEnabled() string { - if x != nil { - return x.TestsEnabled - } - return "" -} - -var File_proxy_vmess_account_proto protoreflect.FileDescriptor - -var file_proxy_vmess_account_proto_rawDesc = []byte{ - 0x0a, 0x19, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, - 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x1a, 0x1d, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x91, 0x01, 0x0a, - 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x51, 0x0a, 0x11, 0x73, 0x65, 0x63, 0x75, - 0x72, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, - 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, - 0x69, 0x74, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x74, - 0x65, 0x73, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x74, 0x65, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, - 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, - 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6d, 0x65, 0x73, - 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, - 0x6d, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_proxy_vmess_account_proto_rawDescOnce sync.Once - file_proxy_vmess_account_proto_rawDescData = file_proxy_vmess_account_proto_rawDesc -) - -func file_proxy_vmess_account_proto_rawDescGZIP() []byte { - file_proxy_vmess_account_proto_rawDescOnce.Do(func() { - file_proxy_vmess_account_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_vmess_account_proto_rawDescData) - }) - return file_proxy_vmess_account_proto_rawDescData -} - -var file_proxy_vmess_account_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_proxy_vmess_account_proto_goTypes = []any{ - (*Account)(nil), // 0: xray.proxy.vmess.Account - (*protocol.SecurityConfig)(nil), // 1: xray.common.protocol.SecurityConfig -} -var file_proxy_vmess_account_proto_depIdxs = []int32{ - 1, // 0: xray.proxy.vmess.Account.security_settings:type_name -> xray.common.protocol.SecurityConfig - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_proxy_vmess_account_proto_init() } -func file_proxy_vmess_account_proto_init() { - if File_proxy_vmess_account_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_proxy_vmess_account_proto_rawDesc, - NumEnums: 0, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_proxy_vmess_account_proto_goTypes, - DependencyIndexes: file_proxy_vmess_account_proto_depIdxs, - MessageInfos: file_proxy_vmess_account_proto_msgTypes, - }.Build() - File_proxy_vmess_account_proto = out.File - file_proxy_vmess_account_proto_rawDesc = nil - file_proxy_vmess_account_proto_goTypes = nil - file_proxy_vmess_account_proto_depIdxs = nil -} diff --git a/proxy/vmess/account.proto b/proxy/vmess/account.proto deleted file mode 100644 index 3fac639909c4..000000000000 --- a/proxy/vmess/account.proto +++ /dev/null @@ -1,19 +0,0 @@ -syntax = "proto3"; - -package xray.proxy.vmess; -option csharp_namespace = "Xray.Proxy.Vmess"; -option go_package = "github.com/xtls/xray-core/proxy/vmess"; -option java_package = "com.xray.proxy.vmess"; -option java_multiple_files = true; - -import "common/protocol/headers.proto"; - -message Account { - // ID of the account, in the form of a UUID, e.g., - // "66ad4540-b58c-4ad2-9926-ea63445a9b57". - string id = 1; - // Security settings. Only applies to client side. - xray.common.protocol.SecurityConfig security_settings = 3; - // Define tests enabled for this account - string tests_enabled = 4; -} diff --git a/proxy/vmess/aead/authid.go b/proxy/vmess/aead/authid.go deleted file mode 100644 index 2ec48f110820..000000000000 --- a/proxy/vmess/aead/authid.go +++ /dev/null @@ -1,119 +0,0 @@ -package aead - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - rand3 "crypto/rand" - "encoding/binary" - "errors" - "hash/crc32" - "io" - "math" - "time" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/antireplay" -) - -var ( - ErrNotFound = errors.New("user do not exist") - ErrReplay = errors.New("replayed request") -) - -func CreateAuthID(cmdKey []byte, time int64) [16]byte { - buf := bytes.NewBuffer(nil) - common.Must(binary.Write(buf, binary.BigEndian, time)) - var zero uint32 - common.Must2(io.CopyN(buf, rand3.Reader, 4)) - zero = crc32.ChecksumIEEE(buf.Bytes()) - common.Must(binary.Write(buf, binary.BigEndian, zero)) - aesBlock := NewCipherFromKey(cmdKey) - if buf.Len() != 16 { - panic("Size unexpected") - } - var result [16]byte - aesBlock.Encrypt(result[:], buf.Bytes()) - return result -} - -func NewCipherFromKey(cmdKey []byte) cipher.Block { - aesBlock, err := aes.NewCipher(KDF16(cmdKey, KDFSaltConstAuthIDEncryptionKey)) - if err != nil { - panic(err) - } - return aesBlock -} - -type AuthIDDecoder struct { - s cipher.Block -} - -func NewAuthIDDecoder(cmdKey []byte) *AuthIDDecoder { - return &AuthIDDecoder{NewCipherFromKey(cmdKey)} -} - -func (aidd *AuthIDDecoder) Decode(data [16]byte) (int64, uint32, int32, []byte) { - aidd.s.Decrypt(data[:], data[:]) - var t int64 - var zero uint32 - var rand int32 - reader := bytes.NewReader(data[:]) - common.Must(binary.Read(reader, binary.BigEndian, &t)) - common.Must(binary.Read(reader, binary.BigEndian, &rand)) - common.Must(binary.Read(reader, binary.BigEndian, &zero)) - return t, zero, rand, data[:] -} - -func NewAuthIDDecoderHolder() *AuthIDDecoderHolder { - return &AuthIDDecoderHolder{make(map[string]*AuthIDDecoderItem), antireplay.NewReplayFilter(120)} -} - -type AuthIDDecoderHolder struct { - decoders map[string]*AuthIDDecoderItem - filter *antireplay.ReplayFilter -} - -type AuthIDDecoderItem struct { - dec *AuthIDDecoder - ticket interface{} -} - -func NewAuthIDDecoderItem(key [16]byte, ticket interface{}) *AuthIDDecoderItem { - return &AuthIDDecoderItem{ - dec: NewAuthIDDecoder(key[:]), - ticket: ticket, - } -} - -func (a *AuthIDDecoderHolder) AddUser(key [16]byte, ticket interface{}) { - a.decoders[string(key[:])] = NewAuthIDDecoderItem(key, ticket) -} - -func (a *AuthIDDecoderHolder) RemoveUser(key [16]byte) { - delete(a.decoders, string(key[:])) -} - -func (a *AuthIDDecoderHolder) Match(authID [16]byte) (interface{}, error) { - for _, v := range a.decoders { - t, z, _, d := v.dec.Decode(authID) - if z != crc32.ChecksumIEEE(d[:12]) { - continue - } - - if t < 0 { - continue - } - - if math.Abs(math.Abs(float64(t))-float64(time.Now().Unix())) > 120 { - continue - } - - if !a.filter.Check(authID[:]) { - return nil, ErrReplay - } - - return v.ticket, nil - } - return nil, ErrNotFound -} diff --git a/proxy/vmess/aead/authid_test.go b/proxy/vmess/aead/authid_test.go deleted file mode 100644 index 837d3372bfdc..000000000000 --- a/proxy/vmess/aead/authid_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package aead - -import ( - "fmt" - "strconv" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestCreateAuthID(t *testing.T) { - key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") - authid := CreateAuthID(key, time.Now().Unix()) - - fmt.Println(key) - fmt.Println(authid) -} - -func TestCreateAuthIDAndDecode(t *testing.T) { - key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") - authid := CreateAuthID(key, time.Now().Unix()) - - fmt.Println(key) - fmt.Println(authid) - - AuthDecoder := NewAuthIDDecoderHolder() - var keyw [16]byte - copy(keyw[:], key) - AuthDecoder.AddUser(keyw, "Demo User") - res, err := AuthDecoder.Match(authid) - fmt.Println(res) - fmt.Println(err) - assert.Equal(t, "Demo User", res) - assert.Nil(t, err) -} - -func TestCreateAuthIDAndDecode2(t *testing.T) { - key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") - authid := CreateAuthID(key, time.Now().Unix()) - - fmt.Println(key) - fmt.Println(authid) - - AuthDecoder := NewAuthIDDecoderHolder() - var keyw [16]byte - copy(keyw[:], key) - AuthDecoder.AddUser(keyw, "Demo User") - res, err := AuthDecoder.Match(authid) - fmt.Println(res) - fmt.Println(err) - assert.Equal(t, "Demo User", res) - assert.Nil(t, err) - - key2 := KDF16([]byte("Demo Key for Auth ID Test2"), "Demo Path for Auth ID Test") - authid2 := CreateAuthID(key2, time.Now().Unix()) - - res2, err2 := AuthDecoder.Match(authid2) - assert.EqualError(t, err2, "user do not exist") - assert.Nil(t, res2) -} - -func TestCreateAuthIDAndDecodeMassive(t *testing.T) { - key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") - authid := CreateAuthID(key, time.Now().Unix()) - - fmt.Println(key) - fmt.Println(authid) - - AuthDecoder := NewAuthIDDecoderHolder() - var keyw [16]byte - copy(keyw[:], key) - AuthDecoder.AddUser(keyw, "Demo User") - res, err := AuthDecoder.Match(authid) - fmt.Println(res) - fmt.Println(err) - assert.Equal(t, "Demo User", res) - assert.Nil(t, err) - - for i := 0; i <= 10000; i++ { - key2 := KDF16([]byte("Demo Key for Auth ID Test2"), "Demo Path for Auth ID Test", strconv.Itoa(i)) - var keyw2 [16]byte - copy(keyw2[:], key2) - AuthDecoder.AddUser(keyw2, "Demo User"+strconv.Itoa(i)) - } - - authid3 := CreateAuthID(key, time.Now().Unix()) - - res2, err2 := AuthDecoder.Match(authid3) - assert.Equal(t, "Demo User", res2) - assert.Nil(t, err2) -} - -func TestCreateAuthIDAndDecodeSuperMassive(t *testing.T) { - key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") - authid := CreateAuthID(key, time.Now().Unix()) - - fmt.Println(key) - fmt.Println(authid) - - AuthDecoder := NewAuthIDDecoderHolder() - var keyw [16]byte - copy(keyw[:], key) - AuthDecoder.AddUser(keyw, "Demo User") - res, err := AuthDecoder.Match(authid) - fmt.Println(res) - fmt.Println(err) - assert.Equal(t, "Demo User", res) - assert.Nil(t, err) - - for i := 0; i <= 1000000; i++ { - key2 := KDF16([]byte("Demo Key for Auth ID Test2"), "Demo Path for Auth ID Test", strconv.Itoa(i)) - var keyw2 [16]byte - copy(keyw2[:], key2) - AuthDecoder.AddUser(keyw2, "Demo User"+strconv.Itoa(i)) - } - - authid3 := CreateAuthID(key, time.Now().Unix()) - - before := time.Now() - res2, err2 := AuthDecoder.Match(authid3) - after := time.Now() - assert.Equal(t, "Demo User", res2) - assert.Nil(t, err2) - - fmt.Println(after.Sub(before).Seconds()) -} diff --git a/proxy/vmess/aead/consts.go b/proxy/vmess/aead/consts.go deleted file mode 100644 index ef13977dbf4a..000000000000 --- a/proxy/vmess/aead/consts.go +++ /dev/null @@ -1,14 +0,0 @@ -package aead - -const ( - KDFSaltConstAuthIDEncryptionKey = "AES Auth ID Encryption" - KDFSaltConstAEADRespHeaderLenKey = "AEAD Resp Header Len Key" - KDFSaltConstAEADRespHeaderLenIV = "AEAD Resp Header Len IV" - KDFSaltConstAEADRespHeaderPayloadKey = "AEAD Resp Header Key" - KDFSaltConstAEADRespHeaderPayloadIV = "AEAD Resp Header IV" - KDFSaltConstVMessAEADKDF = "VMess AEAD KDF" - KDFSaltConstVMessHeaderPayloadAEADKey = "VMess Header AEAD Key" - KDFSaltConstVMessHeaderPayloadAEADIV = "VMess Header AEAD Nonce" - KDFSaltConstVMessHeaderPayloadLengthAEADKey = "VMess Header AEAD Key_Length" - KDFSaltConstVMessHeaderPayloadLengthAEADIV = "VMess Header AEAD Nonce_Length" -) diff --git a/proxy/vmess/aead/encrypt.go b/proxy/vmess/aead/encrypt.go deleted file mode 100644 index 8995f2ea4cf6..000000000000 --- a/proxy/vmess/aead/encrypt.go +++ /dev/null @@ -1,168 +0,0 @@ -package aead - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "encoding/binary" - "io" - "time" - - "github.com/xtls/xray-core/common" -) - -func SealVMessAEADHeader(key [16]byte, data []byte) []byte { - generatedAuthID := CreateAuthID(key[:], time.Now().Unix()) - - connectionNonce := make([]byte, 8) - if _, err := io.ReadFull(rand.Reader, connectionNonce); err != nil { - panic(err.Error()) - } - - aeadPayloadLengthSerializeBuffer := bytes.NewBuffer(nil) - - headerPayloadDataLen := uint16(len(data)) - - common.Must(binary.Write(aeadPayloadLengthSerializeBuffer, binary.BigEndian, headerPayloadDataLen)) - - aeadPayloadLengthSerializedByte := aeadPayloadLengthSerializeBuffer.Bytes() - var payloadHeaderLengthAEADEncrypted []byte - - { - payloadHeaderLengthAEADKey := KDF16(key[:], KDFSaltConstVMessHeaderPayloadLengthAEADKey, string(generatedAuthID[:]), string(connectionNonce)) - - payloadHeaderLengthAEADNonce := KDF(key[:], KDFSaltConstVMessHeaderPayloadLengthAEADIV, string(generatedAuthID[:]), string(connectionNonce))[:12] - - payloadHeaderLengthAEADAESBlock, err := aes.NewCipher(payloadHeaderLengthAEADKey) - if err != nil { - panic(err.Error()) - } - - payloadHeaderAEAD, err := cipher.NewGCM(payloadHeaderLengthAEADAESBlock) - if err != nil { - panic(err.Error()) - } - - payloadHeaderLengthAEADEncrypted = payloadHeaderAEAD.Seal(nil, payloadHeaderLengthAEADNonce, aeadPayloadLengthSerializedByte, generatedAuthID[:]) - } - - var payloadHeaderAEADEncrypted []byte - - { - payloadHeaderAEADKey := KDF16(key[:], KDFSaltConstVMessHeaderPayloadAEADKey, string(generatedAuthID[:]), string(connectionNonce)) - - payloadHeaderAEADNonce := KDF(key[:], KDFSaltConstVMessHeaderPayloadAEADIV, string(generatedAuthID[:]), string(connectionNonce))[:12] - - payloadHeaderAEADAESBlock, err := aes.NewCipher(payloadHeaderAEADKey) - if err != nil { - panic(err.Error()) - } - - payloadHeaderAEAD, err := cipher.NewGCM(payloadHeaderAEADAESBlock) - if err != nil { - panic(err.Error()) - } - - payloadHeaderAEADEncrypted = payloadHeaderAEAD.Seal(nil, payloadHeaderAEADNonce, data, generatedAuthID[:]) - } - - outputBuffer := bytes.NewBuffer(nil) - - common.Must2(outputBuffer.Write(generatedAuthID[:])) // 16 - common.Must2(outputBuffer.Write(payloadHeaderLengthAEADEncrypted)) // 2+16 - common.Must2(outputBuffer.Write(connectionNonce)) // 8 - common.Must2(outputBuffer.Write(payloadHeaderAEADEncrypted)) - - return outputBuffer.Bytes() -} - -func OpenVMessAEADHeader(key [16]byte, authid [16]byte, data io.Reader) ([]byte, bool, int, error) { - var payloadHeaderLengthAEADEncrypted [18]byte - var nonce [8]byte - - var bytesRead int - - authidCheckValueReadBytesCounts, err := io.ReadFull(data, payloadHeaderLengthAEADEncrypted[:]) - bytesRead += authidCheckValueReadBytesCounts - if err != nil { - return nil, false, bytesRead, err - } - - nonceReadBytesCounts, err := io.ReadFull(data, nonce[:]) - bytesRead += nonceReadBytesCounts - if err != nil { - return nil, false, bytesRead, err - } - - // Decrypt Length - - var decryptedAEADHeaderLengthPayloadResult []byte - - { - payloadHeaderLengthAEADKey := KDF16(key[:], KDFSaltConstVMessHeaderPayloadLengthAEADKey, string(authid[:]), string(nonce[:])) - - payloadHeaderLengthAEADNonce := KDF(key[:], KDFSaltConstVMessHeaderPayloadLengthAEADIV, string(authid[:]), string(nonce[:]))[:12] - - payloadHeaderAEADAESBlock, err := aes.NewCipher(payloadHeaderLengthAEADKey) - if err != nil { - panic(err.Error()) - } - - payloadHeaderLengthAEAD, err := cipher.NewGCM(payloadHeaderAEADAESBlock) - if err != nil { - panic(err.Error()) - } - - decryptedAEADHeaderLengthPayload, erropenAEAD := payloadHeaderLengthAEAD.Open(nil, payloadHeaderLengthAEADNonce, payloadHeaderLengthAEADEncrypted[:], authid[:]) - - if erropenAEAD != nil { - return nil, true, bytesRead, erropenAEAD - } - - decryptedAEADHeaderLengthPayloadResult = decryptedAEADHeaderLengthPayload - } - - var length uint16 - - common.Must(binary.Read(bytes.NewReader(decryptedAEADHeaderLengthPayloadResult), binary.BigEndian, &length)) - - var decryptedAEADHeaderPayloadR []byte - - var payloadHeaderAEADEncryptedReadedBytesCounts int - - { - payloadHeaderAEADKey := KDF16(key[:], KDFSaltConstVMessHeaderPayloadAEADKey, string(authid[:]), string(nonce[:])) - - payloadHeaderAEADNonce := KDF(key[:], KDFSaltConstVMessHeaderPayloadAEADIV, string(authid[:]), string(nonce[:]))[:12] - - // 16 == AEAD Tag size - payloadHeaderAEADEncrypted := make([]byte, length+16) - - payloadHeaderAEADEncryptedReadedBytesCounts, err = io.ReadFull(data, payloadHeaderAEADEncrypted) - bytesRead += payloadHeaderAEADEncryptedReadedBytesCounts - if err != nil { - return nil, false, bytesRead, err - } - - payloadHeaderAEADAESBlock, err := aes.NewCipher(payloadHeaderAEADKey) - if err != nil { - panic(err.Error()) - } - - payloadHeaderAEAD, err := cipher.NewGCM(payloadHeaderAEADAESBlock) - if err != nil { - panic(err.Error()) - } - - decryptedAEADHeaderPayload, erropenAEAD := payloadHeaderAEAD.Open(nil, payloadHeaderAEADNonce, payloadHeaderAEADEncrypted, authid[:]) - - if erropenAEAD != nil { - return nil, true, bytesRead, erropenAEAD - } - - decryptedAEADHeaderPayloadR = decryptedAEADHeaderPayload - } - - return decryptedAEADHeaderPayloadR, false, bytesRead, nil -} diff --git a/proxy/vmess/aead/encrypt_test.go b/proxy/vmess/aead/encrypt_test.go deleted file mode 100644 index 5f8e33cfe052..000000000000 --- a/proxy/vmess/aead/encrypt_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package aead - -import ( - "bytes" - "fmt" - "io" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestOpenVMessAEADHeader(t *testing.T) { - TestHeader := []byte("Test Header") - key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") - var keyw [16]byte - copy(keyw[:], key) - sealed := SealVMessAEADHeader(keyw, TestHeader) - - AEADR := bytes.NewReader(sealed) - - var authid [16]byte - - io.ReadFull(AEADR, authid[:]) - - out, _, _, err := OpenVMessAEADHeader(keyw, authid, AEADR) - - fmt.Println(string(out)) - fmt.Println(err) -} - -func TestOpenVMessAEADHeader2(t *testing.T) { - TestHeader := []byte("Test Header") - key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") - var keyw [16]byte - copy(keyw[:], key) - sealed := SealVMessAEADHeader(keyw, TestHeader) - - AEADR := bytes.NewReader(sealed) - - var authid [16]byte - - io.ReadFull(AEADR, authid[:]) - - out, _, readen, err := OpenVMessAEADHeader(keyw, authid, AEADR) - assert.Equal(t, len(sealed)-16-AEADR.Len(), readen) - assert.Equal(t, string(TestHeader), string(out)) - assert.Nil(t, err) -} - -func TestOpenVMessAEADHeader4(t *testing.T) { - for i := 0; i <= 60; i++ { - TestHeader := []byte("Test Header") - key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") - var keyw [16]byte - copy(keyw[:], key) - sealed := SealVMessAEADHeader(keyw, TestHeader) - var sealedm [16]byte - copy(sealedm[:], sealed) - sealed[i] ^= 0xff - AEADR := bytes.NewReader(sealed) - - var authid [16]byte - - io.ReadFull(AEADR, authid[:]) - - out, drain, readen, err := OpenVMessAEADHeader(keyw, authid, AEADR) - assert.Equal(t, len(sealed)-16-AEADR.Len(), readen) - assert.Equal(t, true, drain) - assert.NotNil(t, err) - if err == nil { - fmt.Println(">") - } - assert.Nil(t, out) - } -} - -func TestOpenVMessAEADHeader4Massive(t *testing.T) { - for j := 0; j < 1000; j++ { - for i := 0; i <= 60; i++ { - TestHeader := []byte("Test Header") - key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") - var keyw [16]byte - copy(keyw[:], key) - sealed := SealVMessAEADHeader(keyw, TestHeader) - var sealedm [16]byte - copy(sealedm[:], sealed) - sealed[i] ^= 0xff - AEADR := bytes.NewReader(sealed) - - var authid [16]byte - - io.ReadFull(AEADR, authid[:]) - - out, drain, readen, err := OpenVMessAEADHeader(keyw, authid, AEADR) - assert.Equal(t, len(sealed)-16-AEADR.Len(), readen) - assert.Equal(t, true, drain) - assert.NotNil(t, err) - if err == nil { - fmt.Println(">") - } - assert.Nil(t, out) - } - } -} diff --git a/proxy/vmess/aead/kdf.go b/proxy/vmess/aead/kdf.go deleted file mode 100644 index 8194f5c5a0c5..000000000000 --- a/proxy/vmess/aead/kdf.go +++ /dev/null @@ -1,33 +0,0 @@ -package aead - -import ( - "crypto/hmac" - "crypto/sha256" - "hash" -) - -type hash2 struct { - hash.Hash -} - -func KDF(key []byte, path ...string) []byte { - hmacf := hmac.New(sha256.New, []byte(KDFSaltConstVMessAEADKDF)) - - for _, v := range path { - first := true - hmacf = hmac.New(func() hash.Hash { - if first { - first = false - return hash2{hmacf} - } - return hmacf - }, []byte(v)) - } - hmacf.Write(key) - return hmacf.Sum(nil) -} - -func KDF16(key []byte, path ...string) []byte { - r := KDF(key, path...) - return r[:16] -} diff --git a/proxy/vmess/encoding/auth.go b/proxy/vmess/encoding/auth.go deleted file mode 100644 index 99bdaa49c82f..000000000000 --- a/proxy/vmess/encoding/auth.go +++ /dev/null @@ -1,99 +0,0 @@ -package encoding - -import ( - "crypto/md5" - "encoding/binary" - "hash/fnv" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/crypto" - "golang.org/x/crypto/sha3" -) - -// Authenticate authenticates a byte array using Fnv hash. -func Authenticate(b []byte) uint32 { - fnv1hash := fnv.New32a() - common.Must2(fnv1hash.Write(b)) - return fnv1hash.Sum32() -} - -// [DEPRECATED 2023-06] -type NoOpAuthenticator struct{} - -func (NoOpAuthenticator) NonceSize() int { - return 0 -} - -func (NoOpAuthenticator) Overhead() int { - return 0 -} - -// Seal implements AEAD.Seal(). -func (NoOpAuthenticator) Seal(dst, nonce, plaintext, additionalData []byte) []byte { - return append(dst[:0], plaintext...) -} - -// Open implements AEAD.Open(). -func (NoOpAuthenticator) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { - return append(dst[:0], ciphertext...), nil -} - -// GenerateChacha20Poly1305Key generates a 32-byte key from a given 16-byte array. -func GenerateChacha20Poly1305Key(b []byte) []byte { - key := make([]byte, 32) - t := md5.Sum(b) - copy(key, t[:]) - t = md5.Sum(key[:16]) - copy(key[16:], t[:]) - return key -} - -type ShakeSizeParser struct { - shake sha3.ShakeHash - buffer [2]byte -} - -func NewShakeSizeParser(nonce []byte) *ShakeSizeParser { - shake := sha3.NewShake128() - common.Must2(shake.Write(nonce)) - return &ShakeSizeParser{ - shake: shake, - } -} - -func (*ShakeSizeParser) SizeBytes() int32 { - return 2 -} - -func (s *ShakeSizeParser) next() uint16 { - common.Must2(s.shake.Read(s.buffer[:])) - return binary.BigEndian.Uint16(s.buffer[:]) -} - -func (s *ShakeSizeParser) Decode(b []byte) (uint16, error) { - mask := s.next() - size := binary.BigEndian.Uint16(b) - return mask ^ size, nil -} - -func (s *ShakeSizeParser) Encode(size uint16, b []byte) []byte { - mask := s.next() - binary.BigEndian.PutUint16(b, mask^size) - return b[:2] -} - -func (s *ShakeSizeParser) NextPaddingLen() uint16 { - return s.next() % 64 -} - -func (s *ShakeSizeParser) MaxPaddingLen() uint16 { - return 64 -} - -type AEADSizeParser struct { - crypto.AEADChunkSizeParser -} - -func NewAEADSizeParser(auth *crypto.AEADAuthenticator) *AEADSizeParser { - return &AEADSizeParser{crypto.AEADChunkSizeParser{Auth: auth}} -} diff --git a/proxy/vmess/encoding/client.go b/proxy/vmess/encoding/client.go deleted file mode 100644 index d678646b64c8..000000000000 --- a/proxy/vmess/encoding/client.go +++ /dev/null @@ -1,344 +0,0 @@ -package encoding - -import ( - "bytes" - "context" - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "crypto/sha256" - "encoding/binary" - "hash/fnv" - "io" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/bitmask" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/crypto" - "github.com/xtls/xray-core/common/dice" - "github.com/xtls/xray-core/common/drain" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/proxy/vmess" - vmessaead "github.com/xtls/xray-core/proxy/vmess/aead" - "golang.org/x/crypto/chacha20poly1305" -) - -// ClientSession stores connection session info for VMess client. -type ClientSession struct { - requestBodyKey [16]byte - requestBodyIV [16]byte - responseBodyKey [16]byte - responseBodyIV [16]byte - responseReader io.Reader - responseHeader byte - - readDrainer drain.Drainer -} - -// NewClientSession creates a new ClientSession. -func NewClientSession(ctx context.Context, behaviorSeed int64) *ClientSession { - session := &ClientSession{} - - randomBytes := make([]byte, 33) // 16 + 16 + 1 - common.Must2(rand.Read(randomBytes)) - copy(session.requestBodyKey[:], randomBytes[:16]) - copy(session.requestBodyIV[:], randomBytes[16:32]) - session.responseHeader = randomBytes[32] - - BodyKey := sha256.Sum256(session.requestBodyKey[:]) - copy(session.responseBodyKey[:], BodyKey[:16]) - BodyIV := sha256.Sum256(session.requestBodyIV[:]) - copy(session.responseBodyIV[:], BodyIV[:16]) - { - var err error - session.readDrainer, err = drain.NewBehaviorSeedLimitedDrainer(behaviorSeed, 18, 3266, 64) - if err != nil { - errors.LogInfoInner(ctx, err, "unable to initialize drainer") - session.readDrainer = drain.NewNopDrainer() - } - } - - return session -} - -func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) error { - account := header.User.Account.(*vmess.MemoryAccount) - - buffer := buf.New() - defer buffer.Release() - - common.Must(buffer.WriteByte(Version)) - common.Must2(buffer.Write(c.requestBodyIV[:])) - common.Must2(buffer.Write(c.requestBodyKey[:])) - common.Must(buffer.WriteByte(c.responseHeader)) - common.Must(buffer.WriteByte(byte(header.Option))) - - paddingLen := dice.Roll(16) - security := byte(paddingLen<<4) | byte(header.Security) - common.Must2(buffer.Write([]byte{security, byte(0), byte(header.Command)})) - - if header.Command != protocol.RequestCommandMux { - if err := addrParser.WriteAddressPort(buffer, header.Address, header.Port); err != nil { - return errors.New("failed to writer address and port").Base(err) - } - } - - if paddingLen > 0 { - common.Must2(buffer.ReadFullFrom(rand.Reader, int32(paddingLen))) - } - - { - fnv1a := fnv.New32a() - common.Must2(fnv1a.Write(buffer.Bytes())) - hashBytes := buffer.Extend(int32(fnv1a.Size())) - fnv1a.Sum(hashBytes[:0]) - } - - var fixedLengthCmdKey [16]byte - copy(fixedLengthCmdKey[:], account.ID.CmdKey()) - vmessout := vmessaead.SealVMessAEADHeader(fixedLengthCmdKey, buffer.Bytes()) - common.Must2(io.Copy(writer, bytes.NewReader(vmessout))) - - return nil -} - -func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) { - var sizeParser crypto.ChunkSizeEncoder = crypto.PlainChunkSizeParser{} - if request.Option.Has(protocol.RequestOptionChunkMasking) { - sizeParser = NewShakeSizeParser(c.requestBodyIV[:]) - } - var padding crypto.PaddingLengthGenerator - if request.Option.Has(protocol.RequestOptionGlobalPadding) { - var ok bool - padding, ok = sizeParser.(crypto.PaddingLengthGenerator) - if !ok { - return nil, errors.New("invalid option: RequestOptionGlobalPadding") - } - } - - switch request.Security { - case protocol.SecurityType_NONE: - if request.Option.Has(protocol.RequestOptionChunkStream) { - if request.Command.TransferType() == protocol.TransferTypeStream { - return crypto.NewChunkStreamWriter(sizeParser, writer), nil - } - auth := &crypto.AEADAuthenticator{ - AEAD: new(NoOpAuthenticator), - NonceGenerator: crypto.GenerateEmptyBytes(), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding), nil - } - - return buf.NewWriter(writer), nil - case protocol.SecurityType_AES128_GCM: - aead := crypto.NewAesGcm(c.requestBodyKey[:]) - auth := &crypto.AEADAuthenticator{ - AEAD: aead, - NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { - AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len") - AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey) - - lengthAuth := &crypto.AEADAuthenticator{ - AEAD: AuthenticatedLengthKeyAEAD, - NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - sizeParser = NewAEADSizeParser(lengthAuth) - } - return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil - case protocol.SecurityType_CHACHA20_POLY1305: - aead, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.requestBodyKey[:])) - common.Must(err) - - auth := &crypto.AEADAuthenticator{ - AEAD: aead, - NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { - AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len") - AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey)) - common.Must(err) - - lengthAuth := &crypto.AEADAuthenticator{ - AEAD: AuthenticatedLengthKeyAEAD, - NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - sizeParser = NewAEADSizeParser(lengthAuth) - } - return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil - default: - return nil, errors.New("invalid option: Security") - } -} - -func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.ResponseHeader, error) { - aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey) - aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12] - - aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block) - aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD) - - var aeadEncryptedResponseHeaderLength [18]byte - var decryptedResponseHeaderLength int - var decryptedResponseHeaderLengthBinaryDeserializeBuffer uint16 - - if n, err := io.ReadFull(reader, aeadEncryptedResponseHeaderLength[:]); err != nil { - c.readDrainer.AcknowledgeReceive(n) - return nil, drain.WithError(c.readDrainer, reader, errors.New("Unable to Read Header Len").Base(err)) - } else { // nolint: golint - c.readDrainer.AcknowledgeReceive(n) - } - if decryptedResponseHeaderLengthBinaryBuffer, err := aeadResponseHeaderLengthEncryptionAEAD.Open(nil, aeadResponseHeaderLengthEncryptionIV, aeadEncryptedResponseHeaderLength[:], nil); err != nil { - return nil, drain.WithError(c.readDrainer, reader, errors.New("Failed To Decrypt Length").Base(err)) - } else { // nolint: golint - common.Must(binary.Read(bytes.NewReader(decryptedResponseHeaderLengthBinaryBuffer), binary.BigEndian, &decryptedResponseHeaderLengthBinaryDeserializeBuffer)) - decryptedResponseHeaderLength = int(decryptedResponseHeaderLengthBinaryDeserializeBuffer) - } - - aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey) - aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12] - - aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block) - aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD) - - encryptedResponseHeaderBuffer := make([]byte, decryptedResponseHeaderLength+16) - - if n, err := io.ReadFull(reader, encryptedResponseHeaderBuffer); err != nil { - c.readDrainer.AcknowledgeReceive(n) - return nil, drain.WithError(c.readDrainer, reader, errors.New("Unable to Read Header Data").Base(err)) - } else { // nolint: golint - c.readDrainer.AcknowledgeReceive(n) - } - - if decryptedResponseHeaderBuffer, err := aeadResponseHeaderPayloadEncryptionAEAD.Open(nil, aeadResponseHeaderPayloadEncryptionIV, encryptedResponseHeaderBuffer, nil); err != nil { - return nil, drain.WithError(c.readDrainer, reader, errors.New("Failed To Decrypt Payload").Base(err)) - } else { // nolint: golint - c.responseReader = bytes.NewReader(decryptedResponseHeaderBuffer) - } - - buffer := buf.StackNew() - defer buffer.Release() - - if _, err := buffer.ReadFullFrom(c.responseReader, 4); err != nil { - return nil, errors.New("failed to read response header").Base(err).AtWarning() - } - - if buffer.Byte(0) != c.responseHeader { - return nil, errors.New("unexpected response header. Expecting ", int(c.responseHeader), " but actually ", int(buffer.Byte(0))) - } - - header := &protocol.ResponseHeader{ - Option: bitmask.Byte(buffer.Byte(1)), - } - - if buffer.Byte(2) != 0 { - cmdID := buffer.Byte(2) - dataLen := int32(buffer.Byte(3)) - - buffer.Clear() - if _, err := buffer.ReadFullFrom(c.responseReader, dataLen); err != nil { - return nil, errors.New("failed to read response command").Base(err) - } - command, err := UnmarshalCommand(cmdID, buffer.Bytes()) - if err == nil { - header.Command = command - } - } - aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:]) - c.responseReader = crypto.NewCryptionReader(aesStream, reader) - return header, nil -} - -func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, reader io.Reader) (buf.Reader, error) { - var sizeParser crypto.ChunkSizeDecoder = crypto.PlainChunkSizeParser{} - if request.Option.Has(protocol.RequestOptionChunkMasking) { - sizeParser = NewShakeSizeParser(c.responseBodyIV[:]) - } - var padding crypto.PaddingLengthGenerator - if request.Option.Has(protocol.RequestOptionGlobalPadding) { - var ok bool - padding, ok = sizeParser.(crypto.PaddingLengthGenerator) - if !ok { - return nil, errors.New("invalid option: RequestOptionGlobalPadding") - } - } - - switch request.Security { - case protocol.SecurityType_NONE: - if request.Option.Has(protocol.RequestOptionChunkStream) { - if request.Command.TransferType() == protocol.TransferTypeStream { - return crypto.NewChunkStreamReader(sizeParser, reader), nil - } - - auth := &crypto.AEADAuthenticator{ - AEAD: new(NoOpAuthenticator), - NonceGenerator: crypto.GenerateEmptyBytes(), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - - return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding), nil - } - - return buf.NewReader(reader), nil - case protocol.SecurityType_AES128_GCM: - aead := crypto.NewAesGcm(c.responseBodyKey[:]) - - auth := &crypto.AEADAuthenticator{ - AEAD: aead, - NonceGenerator: GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { - AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len") - AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey) - - lengthAuth := &crypto.AEADAuthenticator{ - AEAD: AuthenticatedLengthKeyAEAD, - NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - sizeParser = NewAEADSizeParser(lengthAuth) - } - return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil - case protocol.SecurityType_CHACHA20_POLY1305: - aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.responseBodyKey[:])) - - auth := &crypto.AEADAuthenticator{ - AEAD: aead, - NonceGenerator: GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { - AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len") - AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey)) - common.Must(err) - - lengthAuth := &crypto.AEADAuthenticator{ - AEAD: AuthenticatedLengthKeyAEAD, - NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - sizeParser = NewAEADSizeParser(lengthAuth) - } - return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil - default: - return nil, errors.New("invalid option: Security") - } -} - -func GenerateChunkNonce(nonce []byte, size uint32) crypto.BytesGenerator { - c := append([]byte(nil), nonce...) - count := uint16(0) - return func() []byte { - binary.BigEndian.PutUint16(c, count) - count++ - return c[:size] - } -} diff --git a/proxy/vmess/encoding/commands.go b/proxy/vmess/encoding/commands.go deleted file mode 100644 index cdab871bd1e4..000000000000 --- a/proxy/vmess/encoding/commands.go +++ /dev/null @@ -1,145 +0,0 @@ -package encoding - -import ( - "encoding/binary" - "io" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/serial" - "github.com/xtls/xray-core/common/uuid" -) - -var ( - ErrCommandTooLarge = errors.New("Command too large.") - ErrCommandTypeMismatch = errors.New("Command type mismatch.") - ErrInvalidAuth = errors.New("Invalid auth.") - ErrInsufficientLength = errors.New("Insufficient length.") - ErrUnknownCommand = errors.New("Unknown command.") -) - -func MarshalCommand(command interface{}, writer io.Writer) error { - if command == nil { - return ErrUnknownCommand - } - - var cmdID byte - var factory CommandFactory - switch command.(type) { - case *protocol.CommandSwitchAccount: - factory = new(CommandSwitchAccountFactory) - cmdID = 1 - default: - return ErrUnknownCommand - } - - buffer := buf.New() - defer buffer.Release() - - err := factory.Marshal(command, buffer) - if err != nil { - return err - } - - auth := Authenticate(buffer.Bytes()) - length := buffer.Len() + 4 - if length > 255 { - return ErrCommandTooLarge - } - - common.Must2(writer.Write([]byte{cmdID, byte(length), byte(auth >> 24), byte(auth >> 16), byte(auth >> 8), byte(auth)})) - common.Must2(writer.Write(buffer.Bytes())) - return nil -} - -func UnmarshalCommand(cmdID byte, data []byte) (protocol.ResponseCommand, error) { - if len(data) <= 4 { - return nil, ErrInsufficientLength - } - expectedAuth := Authenticate(data[4:]) - actualAuth := binary.BigEndian.Uint32(data[:4]) - if expectedAuth != actualAuth { - return nil, ErrInvalidAuth - } - - var factory CommandFactory - switch cmdID { - case 1: - factory = new(CommandSwitchAccountFactory) - default: - return nil, ErrUnknownCommand - } - return factory.Unmarshal(data[4:]) -} - -type CommandFactory interface { - Marshal(command interface{}, writer io.Writer) error - Unmarshal(data []byte) (interface{}, error) -} - -type CommandSwitchAccountFactory struct{} - -func (f *CommandSwitchAccountFactory) Marshal(command interface{}, writer io.Writer) error { - cmd, ok := command.(*protocol.CommandSwitchAccount) - if !ok { - return ErrCommandTypeMismatch - } - - hostStr := "" - if cmd.Host != nil { - hostStr = cmd.Host.String() - } - common.Must2(writer.Write([]byte{byte(len(hostStr))})) - - if len(hostStr) > 0 { - common.Must2(writer.Write([]byte(hostStr))) - } - - common.Must2(serial.WriteUint16(writer, cmd.Port.Value())) - - idBytes := cmd.ID.Bytes() - common.Must2(writer.Write(idBytes)) - common.Must2(serial.WriteUint16(writer, 0)) // compatible with legacy alterId - common.Must2(writer.Write([]byte{byte(cmd.Level)})) - - common.Must2(writer.Write([]byte{cmd.ValidMin})) - return nil -} - -func (f *CommandSwitchAccountFactory) Unmarshal(data []byte) (interface{}, error) { - cmd := new(protocol.CommandSwitchAccount) - if len(data) == 0 { - return nil, ErrInsufficientLength - } - lenHost := int(data[0]) - if len(data) < lenHost+1 { - return nil, ErrInsufficientLength - } - if lenHost > 0 { - cmd.Host = net.ParseAddress(string(data[1 : 1+lenHost])) - } - portStart := 1 + lenHost - if len(data) < portStart+2 { - return nil, ErrInsufficientLength - } - cmd.Port = net.PortFromBytes(data[portStart : portStart+2]) - idStart := portStart + 2 - if len(data) < idStart+16 { - return nil, ErrInsufficientLength - } - cmd.ID, _ = uuid.ParseBytes(data[idStart : idStart+16]) - levelStart := idStart + 16 + 2 - if len(data) < levelStart+1 { - return nil, ErrInsufficientLength - } - cmd.Level = uint32(data[levelStart]) - timeStart := levelStart + 1 - if len(data) < timeStart+1 { - return nil, ErrInsufficientLength - } - cmd.ValidMin = data[timeStart] - return cmd, nil -} diff --git a/proxy/vmess/encoding/commands_test.go b/proxy/vmess/encoding/commands_test.go deleted file mode 100644 index c54159596193..000000000000 --- a/proxy/vmess/encoding/commands_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package encoding_test - -import ( - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/assert" - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/uuid" - . "github.com/xtls/xray-core/proxy/vmess/encoding" -) - -func TestSwitchAccount(t *testing.T) { - sa := &protocol.CommandSwitchAccount{ - Port: 1234, - ID: uuid.New(), - Level: 128, - ValidMin: 16, - } - - buffer := buf.New() - common.Must(MarshalCommand(sa, buffer)) - - cmd, err := UnmarshalCommand(1, buffer.BytesFrom(2)) - common.Must(err) - - sa2, ok := cmd.(*protocol.CommandSwitchAccount) - if !ok { - t.Fatal("failed to convert command to CommandSwitchAccount") - } - if r := cmp.Diff(sa2, sa); r != "" { - t.Error(r) - } -} - -func TestSwitchAccountBugOffByOne(t *testing.T) { - sa := &protocol.CommandSwitchAccount{ - Port: 1234, - ID: uuid.New(), - Level: 128, - ValidMin: 16, - } - - buffer := buf.New() - csaf := CommandSwitchAccountFactory{} - common.Must(csaf.Marshal(sa, buffer)) - - Payload := buffer.Bytes() - - cmd, err := csaf.Unmarshal(Payload[:len(Payload)-1]) - assert.Error(t, err) - assert.Nil(t, cmd) -} diff --git a/proxy/vmess/encoding/encoding.go b/proxy/vmess/encoding/encoding.go deleted file mode 100644 index 45c5d20791af..000000000000 --- a/proxy/vmess/encoding/encoding.go +++ /dev/null @@ -1,17 +0,0 @@ -package encoding - -import ( - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol" -) - -const ( - Version = byte(1) -) - -var addrParser = protocol.NewAddressParser( - protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4), - protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain), - protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6), - protocol.PortThenAddress(), -) diff --git a/proxy/vmess/encoding/encoding_test.go b/proxy/vmess/encoding/encoding_test.go deleted file mode 100644 index ddae29e15e20..000000000000 --- a/proxy/vmess/encoding/encoding_test.go +++ /dev/null @@ -1,153 +0,0 @@ -package encoding_test - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/uuid" - "github.com/xtls/xray-core/proxy/vmess" - . "github.com/xtls/xray-core/proxy/vmess/encoding" -) - -func toAccount(a *vmess.Account) protocol.Account { - account, err := a.AsAccount() - common.Must(err) - return account -} - -func TestRequestSerialization(t *testing.T) { - user := &protocol.MemoryUser{ - Level: 0, - Email: "test@example.com", - } - id := uuid.New() - account := &vmess.Account{ - Id: id.String(), - } - user.Account = toAccount(account) - - expectedRequest := &protocol.RequestHeader{ - Version: 1, - User: user, - Command: protocol.RequestCommandTCP, - Address: net.DomainAddress("www.example.com"), - Port: net.Port(443), - Security: protocol.SecurityType_AES128_GCM, - } - - buffer := buf.New() - client := NewClientSession(context.TODO(), 0) - common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) - - buffer2 := buf.New() - buffer2.Write(buffer.Bytes()) - - sessionHistory := NewSessionHistory() - defer common.Close(sessionHistory) - - userValidator := vmess.NewTimedUserValidator() - userValidator.Add(user) - defer common.Close(userValidator) - - server := NewServerSession(userValidator, sessionHistory) - actualRequest, err := server.DecodeRequestHeader(buffer, false) - common.Must(err) - - if r := cmp.Diff(actualRequest, expectedRequest, cmp.AllowUnexported(protocol.ID{})); r != "" { - t.Error(r) - } - - _, err = server.DecodeRequestHeader(buffer2, false) - // anti replay attack - if err == nil { - t.Error("nil error") - } -} - -func TestInvalidRequest(t *testing.T) { - user := &protocol.MemoryUser{ - Level: 0, - Email: "test@example.com", - } - id := uuid.New() - account := &vmess.Account{ - Id: id.String(), - } - user.Account = toAccount(account) - - expectedRequest := &protocol.RequestHeader{ - Version: 1, - User: user, - Command: protocol.RequestCommand(100), - Address: net.DomainAddress("www.example.com"), - Port: net.Port(443), - Security: protocol.SecurityType_AES128_GCM, - } - - buffer := buf.New() - client := NewClientSession(context.TODO(), 0) - common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) - - buffer2 := buf.New() - buffer2.Write(buffer.Bytes()) - - sessionHistory := NewSessionHistory() - defer common.Close(sessionHistory) - - userValidator := vmess.NewTimedUserValidator() - userValidator.Add(user) - defer common.Close(userValidator) - - server := NewServerSession(userValidator, sessionHistory) - _, err := server.DecodeRequestHeader(buffer, false) - if err == nil { - t.Error("nil error") - } -} - -func TestMuxRequest(t *testing.T) { - user := &protocol.MemoryUser{ - Level: 0, - Email: "test@example.com", - } - id := uuid.New() - account := &vmess.Account{ - Id: id.String(), - } - user.Account = toAccount(account) - - expectedRequest := &protocol.RequestHeader{ - Version: 1, - User: user, - Command: protocol.RequestCommandMux, - Security: protocol.SecurityType_AES128_GCM, - Address: net.DomainAddress("v1.mux.cool"), - } - - buffer := buf.New() - client := NewClientSession(context.TODO(), 0) - common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) - - buffer2 := buf.New() - buffer2.Write(buffer.Bytes()) - - sessionHistory := NewSessionHistory() - defer common.Close(sessionHistory) - - userValidator := vmess.NewTimedUserValidator() - userValidator.Add(user) - defer common.Close(userValidator) - - server := NewServerSession(userValidator, sessionHistory) - actualRequest, err := server.DecodeRequestHeader(buffer, false) - common.Must(err) - - if r := cmp.Diff(actualRequest, expectedRequest, cmp.AllowUnexported(protocol.ID{})); r != "" { - t.Error(r) - } -} diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go deleted file mode 100644 index 99e7abc90837..000000000000 --- a/proxy/vmess/encoding/server.go +++ /dev/null @@ -1,451 +0,0 @@ -package encoding - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/sha256" - "encoding/binary" - "hash/fnv" - "io" - "sync" - "time" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/bitmask" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/crypto" - "github.com/xtls/xray-core/common/drain" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/task" - "github.com/xtls/xray-core/proxy/vmess" - vmessaead "github.com/xtls/xray-core/proxy/vmess/aead" - "golang.org/x/crypto/chacha20poly1305" -) - -type sessionID struct { - user [16]byte - key [16]byte - nonce [16]byte -} - -// SessionHistory keeps track of historical session ids, to prevent replay attacks. -type SessionHistory struct { - sync.RWMutex - cache map[sessionID]time.Time - task *task.Periodic -} - -// NewSessionHistory creates a new SessionHistory object. -func NewSessionHistory() *SessionHistory { - h := &SessionHistory{ - cache: make(map[sessionID]time.Time, 128), - } - h.task = &task.Periodic{ - Interval: time.Second * 30, - Execute: h.removeExpiredEntries, - } - return h -} - -// Close implements common.Closable. -func (h *SessionHistory) Close() error { - return h.task.Close() -} - -func (h *SessionHistory) addIfNotExits(session sessionID) bool { - h.Lock() - - if expire, found := h.cache[session]; found && expire.After(time.Now()) { - h.Unlock() - return false - } - - h.cache[session] = time.Now().Add(time.Minute * 3) - h.Unlock() - common.Must(h.task.Start()) - return true -} - -func (h *SessionHistory) removeExpiredEntries() error { - now := time.Now() - - h.Lock() - defer h.Unlock() - - if len(h.cache) == 0 { - return errors.New("nothing to do") - } - - for session, expire := range h.cache { - if expire.Before(now) { - delete(h.cache, session) - } - } - - if len(h.cache) == 0 { - h.cache = make(map[sessionID]time.Time, 128) - } - - return nil -} - -// ServerSession keeps information for a session in VMess server. -type ServerSession struct { - userValidator *vmess.TimedUserValidator - sessionHistory *SessionHistory - requestBodyKey [16]byte - requestBodyIV [16]byte - responseBodyKey [16]byte - responseBodyIV [16]byte - responseWriter io.Writer - responseHeader byte -} - -// NewServerSession creates a new ServerSession, using the given UserValidator. -// The ServerSession instance doesn't take ownership of the validator. -func NewServerSession(validator *vmess.TimedUserValidator, sessionHistory *SessionHistory) *ServerSession { - return &ServerSession{ - userValidator: validator, - sessionHistory: sessionHistory, - } -} - -func parseSecurityType(b byte) protocol.SecurityType { - if _, f := protocol.SecurityType_name[int32(b)]; f { - st := protocol.SecurityType(b) - // For backward compatibility. - if st == protocol.SecurityType_UNKNOWN { - st = protocol.SecurityType_AUTO - } - return st - } - return protocol.SecurityType_UNKNOWN -} - -// DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream. -func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*protocol.RequestHeader, error) { - buffer := buf.New() - - drainer, err := drain.NewBehaviorSeedLimitedDrainer(int64(s.userValidator.GetBehaviorSeed()), 16+38, 3266, 64) - if err != nil { - return nil, errors.New("failed to initialize drainer").Base(err) - } - - drainConnection := func(e error) error { - // We read a deterministic generated length of data before closing the connection to offset padding read pattern - drainer.AcknowledgeReceive(int(buffer.Len())) - if isDrain { - return drain.WithError(drainer, reader, e) - } - return e - } - - defer func() { - buffer.Release() - }() - - if _, err := buffer.ReadFullFrom(reader, protocol.IDBytesLen); err != nil { - return nil, errors.New("failed to read request header").Base(err) - } - - var decryptor io.Reader - var vmessAccount *vmess.MemoryAccount - - user, foundAEAD, errorAEAD := s.userValidator.GetAEAD(buffer.Bytes()) - - var fixedSizeAuthID [16]byte - copy(fixedSizeAuthID[:], buffer.Bytes()) - - switch { - case foundAEAD: - vmessAccount = user.Account.(*vmess.MemoryAccount) - var fixedSizeCmdKey [16]byte - copy(fixedSizeCmdKey[:], vmessAccount.ID.CmdKey()) - aeadData, shouldDrain, bytesRead, errorReason := vmessaead.OpenVMessAEADHeader(fixedSizeCmdKey, fixedSizeAuthID, reader) - if errorReason != nil { - if shouldDrain { - drainer.AcknowledgeReceive(bytesRead) - return nil, drainConnection(errors.New("AEAD read failed").Base(errorReason)) - } else { - return nil, drainConnection(errors.New("AEAD read failed, drain skipped").Base(errorReason)) - } - } - decryptor = bytes.NewReader(aeadData) - default: - return nil, drainConnection(errors.New("invalid user").Base(errorAEAD)) - } - - drainer.AcknowledgeReceive(int(buffer.Len())) - buffer.Clear() - if _, err := buffer.ReadFullFrom(decryptor, 38); err != nil { - return nil, errors.New("failed to read request header").Base(err) - } - - request := &protocol.RequestHeader{ - User: user, - Version: buffer.Byte(0), - } - - copy(s.requestBodyIV[:], buffer.BytesRange(1, 17)) // 16 bytes - copy(s.requestBodyKey[:], buffer.BytesRange(17, 33)) // 16 bytes - var sid sessionID - copy(sid.user[:], vmessAccount.ID.Bytes()) - sid.key = s.requestBodyKey - sid.nonce = s.requestBodyIV - if !s.sessionHistory.addIfNotExits(sid) { - return nil, errors.New("duplicated session id, possibly under replay attack, but this is a AEAD request") - } - - s.responseHeader = buffer.Byte(33) // 1 byte - request.Option = bitmask.Byte(buffer.Byte(34)) // 1 byte - paddingLen := int(buffer.Byte(35) >> 4) - request.Security = parseSecurityType(buffer.Byte(35) & 0x0F) - // 1 bytes reserved - request.Command = protocol.RequestCommand(buffer.Byte(37)) - - switch request.Command { - case protocol.RequestCommandMux: - request.Address = net.DomainAddress("v1.mux.cool") - request.Port = 0 - - case protocol.RequestCommandTCP, protocol.RequestCommandUDP: - if addr, port, err := addrParser.ReadAddressPort(buffer, decryptor); err == nil { - request.Address = addr - request.Port = port - } - } - - if paddingLen > 0 { - if _, err := buffer.ReadFullFrom(decryptor, int32(paddingLen)); err != nil { - return nil, errors.New("failed to read padding").Base(err) - } - } - - if _, err := buffer.ReadFullFrom(decryptor, 4); err != nil { - return nil, errors.New("failed to read checksum").Base(err) - } - - fnv1a := fnv.New32a() - common.Must2(fnv1a.Write(buffer.BytesTo(-4))) - actualHash := fnv1a.Sum32() - expectedHash := binary.BigEndian.Uint32(buffer.BytesFrom(-4)) - - if actualHash != expectedHash { - return nil, errors.New("invalid auth, but this is a AEAD request") - } - - if request.Address == nil { - return nil, errors.New("invalid remote address") - } - - if request.Security == protocol.SecurityType_UNKNOWN || request.Security == protocol.SecurityType_AUTO { - return nil, errors.New("unknown security type: ", request.Security) - } - - return request, nil -} - -// DecodeRequestBody returns Reader from which caller can fetch decrypted body. -func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reader io.Reader) (buf.Reader, error) { - var sizeParser crypto.ChunkSizeDecoder = crypto.PlainChunkSizeParser{} - if request.Option.Has(protocol.RequestOptionChunkMasking) { - sizeParser = NewShakeSizeParser(s.requestBodyIV[:]) - } - var padding crypto.PaddingLengthGenerator - if request.Option.Has(protocol.RequestOptionGlobalPadding) { - var ok bool - padding, ok = sizeParser.(crypto.PaddingLengthGenerator) - if !ok { - return nil, errors.New("invalid option: RequestOptionGlobalPadding") - } - } - - switch request.Security { - case protocol.SecurityType_NONE: - if request.Option.Has(protocol.RequestOptionChunkStream) { - if request.Command.TransferType() == protocol.TransferTypeStream { - return crypto.NewChunkStreamReader(sizeParser, reader), nil - } - - auth := &crypto.AEADAuthenticator{ - AEAD: new(NoOpAuthenticator), - NonceGenerator: crypto.GenerateEmptyBytes(), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding), nil - } - return buf.NewReader(reader), nil - - case protocol.SecurityType_AES128_GCM: - aead := crypto.NewAesGcm(s.requestBodyKey[:]) - auth := &crypto.AEADAuthenticator{ - AEAD: aead, - NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { - AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len") - AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey) - - lengthAuth := &crypto.AEADAuthenticator{ - AEAD: AuthenticatedLengthKeyAEAD, - NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - sizeParser = NewAEADSizeParser(lengthAuth) - } - return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil - - case protocol.SecurityType_CHACHA20_POLY1305: - aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.requestBodyKey[:])) - - auth := &crypto.AEADAuthenticator{ - AEAD: aead, - NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { - AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len") - AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey)) - common.Must(err) - - lengthAuth := &crypto.AEADAuthenticator{ - AEAD: AuthenticatedLengthKeyAEAD, - NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - sizeParser = NewAEADSizeParser(lengthAuth) - } - return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil - - default: - return nil, errors.New("invalid option: Security") - } -} - -// EncodeResponseHeader writes encoded response header into the given writer. -func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, writer io.Writer) { - var encryptionWriter io.Writer - BodyKey := sha256.Sum256(s.requestBodyKey[:]) - copy(s.responseBodyKey[:], BodyKey[:16]) - BodyIV := sha256.Sum256(s.requestBodyIV[:]) - copy(s.responseBodyIV[:], BodyIV[:16]) - - aesStream := crypto.NewAesEncryptionStream(s.responseBodyKey[:], s.responseBodyIV[:]) - encryptionWriter = crypto.NewCryptionWriter(aesStream, writer) - s.responseWriter = encryptionWriter - - aeadEncryptedHeaderBuffer := bytes.NewBuffer(nil) - encryptionWriter = aeadEncryptedHeaderBuffer - - common.Must2(encryptionWriter.Write([]byte{s.responseHeader, byte(header.Option)})) - err := MarshalCommand(header.Command, encryptionWriter) - if err != nil { - common.Must2(encryptionWriter.Write([]byte{0x00, 0x00})) - } - - aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey) - aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12] - - aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block) - aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD) - - aeadResponseHeaderLengthEncryptionBuffer := bytes.NewBuffer(nil) - - decryptedResponseHeaderLengthBinaryDeserializeBuffer := uint16(aeadEncryptedHeaderBuffer.Len()) - - common.Must(binary.Write(aeadResponseHeaderLengthEncryptionBuffer, binary.BigEndian, decryptedResponseHeaderLengthBinaryDeserializeBuffer)) - - AEADEncryptedLength := aeadResponseHeaderLengthEncryptionAEAD.Seal(nil, aeadResponseHeaderLengthEncryptionIV, aeadResponseHeaderLengthEncryptionBuffer.Bytes(), nil) - common.Must2(io.Copy(writer, bytes.NewReader(AEADEncryptedLength))) - - aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey) - aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12] - - aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block) - aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD) - - aeadEncryptedHeaderPayload := aeadResponseHeaderPayloadEncryptionAEAD.Seal(nil, aeadResponseHeaderPayloadEncryptionIV, aeadEncryptedHeaderBuffer.Bytes(), nil) - common.Must2(io.Copy(writer, bytes.NewReader(aeadEncryptedHeaderPayload))) -} - -// EncodeResponseBody returns a Writer that auto-encrypt content written by caller. -func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) { - var sizeParser crypto.ChunkSizeEncoder = crypto.PlainChunkSizeParser{} - if request.Option.Has(protocol.RequestOptionChunkMasking) { - sizeParser = NewShakeSizeParser(s.responseBodyIV[:]) - } - var padding crypto.PaddingLengthGenerator - if request.Option.Has(protocol.RequestOptionGlobalPadding) { - var ok bool - padding, ok = sizeParser.(crypto.PaddingLengthGenerator) - if !ok { - return nil, errors.New("invalid option: RequestOptionGlobalPadding") - } - } - - switch request.Security { - case protocol.SecurityType_NONE: - if request.Option.Has(protocol.RequestOptionChunkStream) { - if request.Command.TransferType() == protocol.TransferTypeStream { - return crypto.NewChunkStreamWriter(sizeParser, writer), nil - } - - auth := &crypto.AEADAuthenticator{ - AEAD: new(NoOpAuthenticator), - NonceGenerator: crypto.GenerateEmptyBytes(), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding), nil - } - return buf.NewWriter(writer), nil - - case protocol.SecurityType_AES128_GCM: - aead := crypto.NewAesGcm(s.responseBodyKey[:]) - auth := &crypto.AEADAuthenticator{ - AEAD: aead, - NonceGenerator: GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { - AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len") - AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey) - - lengthAuth := &crypto.AEADAuthenticator{ - AEAD: AuthenticatedLengthKeyAEAD, - NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - sizeParser = NewAEADSizeParser(lengthAuth) - } - return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil - - case protocol.SecurityType_CHACHA20_POLY1305: - aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.responseBodyKey[:])) - - auth := &crypto.AEADAuthenticator{ - AEAD: aead, - NonceGenerator: GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { - AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len") - AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey)) - common.Must(err) - - lengthAuth := &crypto.AEADAuthenticator{ - AEAD: AuthenticatedLengthKeyAEAD, - NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), - AdditionalDataGenerator: crypto.GenerateEmptyBytes(), - } - sizeParser = NewAEADSizeParser(lengthAuth) - } - return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil - - default: - return nil, errors.New("invalid option: Security") - } -} diff --git a/proxy/vmess/inbound/config.go b/proxy/vmess/inbound/config.go deleted file mode 100644 index e4c5fbb3c98e..000000000000 --- a/proxy/vmess/inbound/config.go +++ /dev/null @@ -1,9 +0,0 @@ -package inbound - -// GetDefaultValue returns default settings of DefaultConfig. -func (c *Config) GetDefaultValue() *DefaultConfig { - if c.GetDefault() == nil { - return &DefaultConfig{} - } - return c.Default -} diff --git a/proxy/vmess/inbound/config.pb.go b/proxy/vmess/inbound/config.pb.go deleted file mode 100644 index 4aa259e8e2b7..000000000000 --- a/proxy/vmess/inbound/config.pb.go +++ /dev/null @@ -1,263 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 -// source: proxy/vmess/inbound/config.proto - -package inbound - -import ( - protocol "github.com/xtls/xray-core/common/protocol" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type DetourConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - To string `protobuf:"bytes,1,opt,name=to,proto3" json:"to,omitempty"` -} - -func (x *DetourConfig) Reset() { - *x = DetourConfig{} - mi := &file_proxy_vmess_inbound_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DetourConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DetourConfig) ProtoMessage() {} - -func (x *DetourConfig) ProtoReflect() protoreflect.Message { - mi := &file_proxy_vmess_inbound_config_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DetourConfig.ProtoReflect.Descriptor instead. -func (*DetourConfig) Descriptor() ([]byte, []int) { - return file_proxy_vmess_inbound_config_proto_rawDescGZIP(), []int{0} -} - -func (x *DetourConfig) GetTo() string { - if x != nil { - return x.To - } - return "" -} - -type DefaultConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Level uint32 `protobuf:"varint,2,opt,name=level,proto3" json:"level,omitempty"` -} - -func (x *DefaultConfig) Reset() { - *x = DefaultConfig{} - mi := &file_proxy_vmess_inbound_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DefaultConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DefaultConfig) ProtoMessage() {} - -func (x *DefaultConfig) ProtoReflect() protoreflect.Message { - mi := &file_proxy_vmess_inbound_config_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DefaultConfig.ProtoReflect.Descriptor instead. -func (*DefaultConfig) Descriptor() ([]byte, []int) { - return file_proxy_vmess_inbound_config_proto_rawDescGZIP(), []int{1} -} - -func (x *DefaultConfig) GetLevel() uint32 { - if x != nil { - return x.Level - } - return 0 -} - -type Config struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - User []*protocol.User `protobuf:"bytes,1,rep,name=user,proto3" json:"user,omitempty"` - Default *DefaultConfig `protobuf:"bytes,2,opt,name=default,proto3" json:"default,omitempty"` - Detour *DetourConfig `protobuf:"bytes,3,opt,name=detour,proto3" json:"detour,omitempty"` // 4 is for legacy setting -} - -func (x *Config) Reset() { - *x = Config{} - mi := &file_proxy_vmess_inbound_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Config) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Config) ProtoMessage() {} - -func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_proxy_vmess_inbound_config_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Config.ProtoReflect.Descriptor instead. -func (*Config) Descriptor() ([]byte, []int) { - return file_proxy_vmess_inbound_config_proto_rawDescGZIP(), []int{2} -} - -func (x *Config) GetUser() []*protocol.User { - if x != nil { - return x.User - } - return nil -} - -func (x *Config) GetDefault() *DefaultConfig { - if x != nil { - return x.Default - } - return nil -} - -func (x *Config) GetDetour() *DetourConfig { - if x != nil { - return x.Detour - } - return nil -} - -var File_proxy_vmess_inbound_config_proto protoreflect.FileDescriptor - -var file_proxy_vmess_inbound_config_proto_rawDesc = []byte{ - 0x0a, 0x20, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, - 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x12, 0x18, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, - 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x1a, 0x1a, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x75, 0x73, - 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1e, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x6f, - 0x75, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x0d, 0x44, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, - 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, - 0xbb, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x04, 0x75, 0x73, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, - 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x07, 0x64, 0x65, - 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, - 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69, - 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x3e, 0x0a, - 0x06, 0x64, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, - 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, - 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x44, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x64, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x42, 0x6a, 0x0a, - 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, - 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a, - 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, - 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x2f, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, - 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6d, 0x65, 0x73, - 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, -} - -var ( - file_proxy_vmess_inbound_config_proto_rawDescOnce sync.Once - file_proxy_vmess_inbound_config_proto_rawDescData = file_proxy_vmess_inbound_config_proto_rawDesc -) - -func file_proxy_vmess_inbound_config_proto_rawDescGZIP() []byte { - file_proxy_vmess_inbound_config_proto_rawDescOnce.Do(func() { - file_proxy_vmess_inbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_vmess_inbound_config_proto_rawDescData) - }) - return file_proxy_vmess_inbound_config_proto_rawDescData -} - -var file_proxy_vmess_inbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_proxy_vmess_inbound_config_proto_goTypes = []any{ - (*DetourConfig)(nil), // 0: xray.proxy.vmess.inbound.DetourConfig - (*DefaultConfig)(nil), // 1: xray.proxy.vmess.inbound.DefaultConfig - (*Config)(nil), // 2: xray.proxy.vmess.inbound.Config - (*protocol.User)(nil), // 3: xray.common.protocol.User -} -var file_proxy_vmess_inbound_config_proto_depIdxs = []int32{ - 3, // 0: xray.proxy.vmess.inbound.Config.user:type_name -> xray.common.protocol.User - 1, // 1: xray.proxy.vmess.inbound.Config.default:type_name -> xray.proxy.vmess.inbound.DefaultConfig - 0, // 2: xray.proxy.vmess.inbound.Config.detour:type_name -> xray.proxy.vmess.inbound.DetourConfig - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name -} - -func init() { file_proxy_vmess_inbound_config_proto_init() } -func file_proxy_vmess_inbound_config_proto_init() { - if File_proxy_vmess_inbound_config_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_proxy_vmess_inbound_config_proto_rawDesc, - NumEnums: 0, - NumMessages: 3, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_proxy_vmess_inbound_config_proto_goTypes, - DependencyIndexes: file_proxy_vmess_inbound_config_proto_depIdxs, - MessageInfos: file_proxy_vmess_inbound_config_proto_msgTypes, - }.Build() - File_proxy_vmess_inbound_config_proto = out.File - file_proxy_vmess_inbound_config_proto_rawDesc = nil - file_proxy_vmess_inbound_config_proto_goTypes = nil - file_proxy_vmess_inbound_config_proto_depIdxs = nil -} diff --git a/proxy/vmess/inbound/config.proto b/proxy/vmess/inbound/config.proto deleted file mode 100644 index 7da1d5810415..000000000000 --- a/proxy/vmess/inbound/config.proto +++ /dev/null @@ -1,24 +0,0 @@ -syntax = "proto3"; - -package xray.proxy.vmess.inbound; -option csharp_namespace = "Xray.Proxy.Vmess.Inbound"; -option go_package = "github.com/xtls/xray-core/proxy/vmess/inbound"; -option java_package = "com.xray.proxy.vmess.inbound"; -option java_multiple_files = true; - -import "common/protocol/user.proto"; - -message DetourConfig { - string to = 1; -} - -message DefaultConfig { - uint32 level = 2; -} - -message Config { - repeated xray.common.protocol.User user = 1; - DefaultConfig default = 2; - DetourConfig detour = 3; - // 4 is for legacy setting -} diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go deleted file mode 100644 index 28b560d8341e..000000000000 --- a/proxy/vmess/inbound/inbound.go +++ /dev/null @@ -1,365 +0,0 @@ -package inbound - -import ( - "context" - "io" - "strings" - "sync" - "time" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/log" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/session" - "github.com/xtls/xray-core/common/signal" - "github.com/xtls/xray-core/common/task" - "github.com/xtls/xray-core/common/uuid" - "github.com/xtls/xray-core/core" - feature_inbound "github.com/xtls/xray-core/features/inbound" - "github.com/xtls/xray-core/features/policy" - "github.com/xtls/xray-core/features/routing" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/encoding" - "github.com/xtls/xray-core/transport/internet/stat" -) - -type userByEmail struct { - sync.Mutex - cache map[string]*protocol.MemoryUser - defaultLevel uint32 -} - -func newUserByEmail(config *DefaultConfig) *userByEmail { - return &userByEmail{ - cache: make(map[string]*protocol.MemoryUser), - defaultLevel: config.Level, - } -} - -func (v *userByEmail) addNoLock(u *protocol.MemoryUser) bool { - email := strings.ToLower(u.Email) - _, found := v.cache[email] - if found { - return false - } - v.cache[email] = u - return true -} - -func (v *userByEmail) Add(u *protocol.MemoryUser) bool { - v.Lock() - defer v.Unlock() - - return v.addNoLock(u) -} - -func (v *userByEmail) GetOrGenerate(email string) (*protocol.MemoryUser, bool) { - email = strings.ToLower(email) - - v.Lock() - defer v.Unlock() - - user, found := v.cache[email] - if !found { - id := uuid.New() - rawAccount := &vmess.Account{ - Id: id.String(), - } - account, err := rawAccount.AsAccount() - common.Must(err) - user = &protocol.MemoryUser{ - Level: v.defaultLevel, - Email: email, - Account: account, - } - v.cache[email] = user - } - return user, found -} - -func (v *userByEmail) Get(email string) *protocol.MemoryUser { - email = strings.ToLower(email) - v.Lock() - defer v.Unlock() - return v.cache[email] -} - -func (v *userByEmail) Remove(email string) bool { - email = strings.ToLower(email) - - v.Lock() - defer v.Unlock() - - if _, found := v.cache[email]; !found { - return false - } - delete(v.cache, email) - return true -} - -// Handler is an inbound connection handler that handles messages in VMess protocol. -type Handler struct { - policyManager policy.Manager - inboundHandlerManager feature_inbound.Manager - clients *vmess.TimedUserValidator - usersByEmail *userByEmail - detours *DetourConfig - sessionHistory *encoding.SessionHistory -} - -// New creates a new VMess inbound handler. -func New(ctx context.Context, config *Config) (*Handler, error) { - v := core.MustFromContext(ctx) - handler := &Handler{ - policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), - inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager), - clients: vmess.NewTimedUserValidator(), - detours: config.Detour, - usersByEmail: newUserByEmail(config.GetDefaultValue()), - sessionHistory: encoding.NewSessionHistory(), - } - - for _, user := range config.User { - mUser, err := user.ToMemoryUser() - if err != nil { - return nil, errors.New("failed to get VMess user").Base(err) - } - - if err := handler.AddUser(ctx, mUser); err != nil { - return nil, errors.New("failed to initiate user").Base(err) - } - } - - return handler, nil -} - -// Close implements common.Closable. -func (h *Handler) Close() error { - return errors.Combine( - h.sessionHistory.Close(), - common.Close(h.usersByEmail)) -} - -// Network implements proxy.Inbound.Network(). -func (*Handler) Network() []net.Network { - return []net.Network{net.Network_TCP, net.Network_UNIX} -} - -func (h *Handler) GetOrGenerateUser(email string) *protocol.MemoryUser { - user, existing := h.usersByEmail.GetOrGenerate(email) - if !existing { - h.clients.Add(user) - } - return user -} - -func (h *Handler) GetUser(ctx context.Context, email string) *protocol.MemoryUser { - return h.usersByEmail.Get(email) -} - -func (h *Handler) GetUsers(ctx context.Context) []*protocol.MemoryUser { - return h.clients.GetUsers() -} - -func (h *Handler) GetUsersCount(context.Context) int64 { - return h.clients.GetCount() -} - -func (h *Handler) AddUser(ctx context.Context, user *protocol.MemoryUser) error { - if len(user.Email) > 0 && !h.usersByEmail.Add(user) { - return errors.New("User ", user.Email, " already exists.") - } - return h.clients.Add(user) -} - -func (h *Handler) RemoveUser(ctx context.Context, email string) error { - if email == "" { - return errors.New("Email must not be empty.") - } - if !h.usersByEmail.Remove(email) { - return errors.New("User ", email, " not found.") - } - h.clients.Remove(email) - return nil -} - -func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSession, request *protocol.RequestHeader, response *protocol.ResponseHeader, input buf.Reader, output *buf.BufferedWriter) error { - session.EncodeResponseHeader(response, output) - - bodyWriter, err := session.EncodeResponseBody(request, output) - if err != nil { - return errors.New("failed to start decoding response").Base(err) - } - { - // Optimize for small response packet - data, err := input.ReadMultiBuffer() - if err != nil { - return err - } - - if err := bodyWriter.WriteMultiBuffer(data); err != nil { - return err - } - } - - if err := output.SetBuffered(false); err != nil { - return err - } - - if err := buf.Copy(input, bodyWriter, buf.UpdateActivity(timer)); err != nil { - return err - } - - account := request.User.Account.(*vmess.MemoryAccount) - - if request.Option.Has(protocol.RequestOptionChunkStream) && !account.NoTerminationSignal { - if err := bodyWriter.WriteMultiBuffer(buf.MultiBuffer{}); err != nil { - return err - } - } - - return nil -} - -// Process implements proxy.Inbound.Process(). -func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error { - sessionPolicy := h.policyManager.ForLevel(0) - if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { - return errors.New("unable to set read deadline").Base(err).AtWarning() - } - - iConn := connection - if statConn, ok := iConn.(*stat.CounterConnection); ok { - iConn = statConn.Connection - } - _, isDrain := iConn.(*net.TCPConn) - if !isDrain { - _, isDrain = iConn.(*net.UnixConn) - } - - reader := &buf.BufferedReader{Reader: buf.NewReader(connection)} - svrSession := encoding.NewServerSession(h.clients, h.sessionHistory) - request, err := svrSession.DecodeRequestHeader(reader, isDrain) - if err != nil { - if errors.Cause(err) != io.EOF { - log.Record(&log.AccessMessage{ - From: connection.RemoteAddr(), - To: "", - Status: log.AccessRejected, - Reason: err, - }) - err = errors.New("invalid request from ", connection.RemoteAddr()).Base(err).AtInfo() - } - return err - } - - if request.Command != protocol.RequestCommandMux { - ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ - From: connection.RemoteAddr(), - To: request.Destination(), - Status: log.AccessAccepted, - Reason: "", - Email: request.User.Email, - }) - } - - errors.LogInfo(ctx, "received request for ", request.Destination()) - - if err := connection.SetReadDeadline(time.Time{}); err != nil { - errors.LogInfoInner(ctx, err, "unable to set back read deadline") - } - - inbound := session.InboundFromContext(ctx) - inbound.Name = "vmess" - inbound.CanSpliceCopy = 3 - inbound.User = request.User - - sessionPolicy = h.policyManager.ForLevel(request.User.Level) - - ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) - - ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer) - link, err := dispatcher.Dispatch(ctx, request.Destination()) - if err != nil { - return errors.New("failed to dispatch request to ", request.Destination()).Base(err) - } - - requestDone := func() error { - defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) - - bodyReader, err := svrSession.DecodeRequestBody(request, reader) - if err != nil { - return errors.New("failed to start decoding").Base(err) - } - if err := buf.Copy(bodyReader, link.Writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transfer request").Base(err) - } - return nil - } - - responseDone := func() error { - defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) - - writer := buf.NewBufferedWriter(buf.NewWriter(connection)) - defer writer.Flush() - - response := &protocol.ResponseHeader{ - Command: h.generateCommand(ctx, request), - } - return transferResponse(timer, svrSession, request, response, link.Reader, writer) - } - - requestDonePost := task.OnSuccess(requestDone, task.Close(link.Writer)) - if err := task.Run(ctx, requestDonePost, responseDone); err != nil { - common.Interrupt(link.Reader) - common.Interrupt(link.Writer) - return errors.New("connection ends").Base(err) - } - - return nil -} - -func (h *Handler) generateCommand(ctx context.Context, request *protocol.RequestHeader) protocol.ResponseCommand { - if h.detours != nil { - tag := h.detours.To - if h.inboundHandlerManager != nil { - handler, err := h.inboundHandlerManager.GetHandler(ctx, tag) - if err != nil { - errors.LogWarningInner(ctx, err, "failed to get detour handler: ", tag) - return nil - } - proxyHandler, port, availableMin := handler.GetRandomInboundProxy() - inboundHandler, ok := proxyHandler.(*Handler) - if ok && inboundHandler != nil { - if availableMin > 255 { - availableMin = 255 - } - - errors.LogDebug(ctx, "pick detour handler for port ", port, " for ", availableMin, " minutes.") - user := inboundHandler.GetOrGenerateUser(request.User.Email) - if user == nil { - return nil - } - account := user.Account.(*vmess.MemoryAccount) - return &protocol.CommandSwitchAccount{ - Port: port, - ID: account.ID.UUID(), - Level: user.Level, - ValidMin: byte(availableMin), - } - } - } - } - - return nil -} - -func init() { - common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { - return New(ctx, config.(*Config)) - })) -} diff --git a/proxy/vmess/outbound/command.go b/proxy/vmess/outbound/command.go deleted file mode 100644 index 2d4747dc288b..000000000000 --- a/proxy/vmess/outbound/command.go +++ /dev/null @@ -1,41 +0,0 @@ -package outbound - -import ( - "time" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/proxy/vmess" -) - -func (h *Handler) handleSwitchAccount(cmd *protocol.CommandSwitchAccount) { - rawAccount := &vmess.Account{ - Id: cmd.ID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AUTO, - }, - } - - account, err := rawAccount.AsAccount() - common.Must(err) - user := &protocol.MemoryUser{ - Email: "", - Level: cmd.Level, - Account: account, - } - dest := net.TCPDestination(cmd.Host, cmd.Port) - until := time.Now().Add(time.Duration(cmd.ValidMin) * time.Minute) - h.serverList.AddServer(protocol.NewServerSpec(dest, protocol.BeforeTime(until), user)) -} - -func (h *Handler) handleCommand(dest net.Destination, cmd protocol.ResponseCommand) { - switch typedCommand := cmd.(type) { - case *protocol.CommandSwitchAccount: - if typedCommand.Host == nil { - typedCommand.Host = dest.Address - } - h.handleSwitchAccount(typedCommand) - default: - } -} diff --git a/proxy/vmess/outbound/config.go b/proxy/vmess/outbound/config.go deleted file mode 100644 index a1e73e06682c..000000000000 --- a/proxy/vmess/outbound/config.go +++ /dev/null @@ -1 +0,0 @@ -package outbound diff --git a/proxy/vmess/outbound/config.pb.go b/proxy/vmess/outbound/config.pb.go deleted file mode 100644 index 0bc4d9254d57..000000000000 --- a/proxy/vmess/outbound/config.pb.go +++ /dev/null @@ -1,142 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 -// source: proxy/vmess/outbound/config.proto - -package outbound - -import ( - protocol "github.com/xtls/xray-core/common/protocol" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Config struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Receiver []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=Receiver,proto3" json:"Receiver,omitempty"` -} - -func (x *Config) Reset() { - *x = Config{} - mi := &file_proxy_vmess_outbound_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Config) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Config) ProtoMessage() {} - -func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_proxy_vmess_outbound_config_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Config.ProtoReflect.Descriptor instead. -func (*Config) Descriptor() ([]byte, []int) { - return file_proxy_vmess_outbound_config_proto_rawDescGZIP(), []int{0} -} - -func (x *Config) GetReceiver() []*protocol.ServerEndpoint { - if x != nil { - return x.Receiver - } - return nil -} - -var File_proxy_vmess_outbound_config_proto protoreflect.FileDescriptor - -var file_proxy_vmess_outbound_config_proto_rawDesc = []byte{ - 0x0a, 0x21, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2f, 0x6f, 0x75, - 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, - 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x1a, 0x21, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0x4a, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x40, 0x0a, 0x08, 0x52, - 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, - 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x52, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x42, 0x6d, 0x0a, - 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, - 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, - 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, - 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x2f, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2f, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0xaa, 0x02, 0x19, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6d, - 0x65, 0x73, 0x73, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_proxy_vmess_outbound_config_proto_rawDescOnce sync.Once - file_proxy_vmess_outbound_config_proto_rawDescData = file_proxy_vmess_outbound_config_proto_rawDesc -) - -func file_proxy_vmess_outbound_config_proto_rawDescGZIP() []byte { - file_proxy_vmess_outbound_config_proto_rawDescOnce.Do(func() { - file_proxy_vmess_outbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_vmess_outbound_config_proto_rawDescData) - }) - return file_proxy_vmess_outbound_config_proto_rawDescData -} - -var file_proxy_vmess_outbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_proxy_vmess_outbound_config_proto_goTypes = []any{ - (*Config)(nil), // 0: xray.proxy.vmess.outbound.Config - (*protocol.ServerEndpoint)(nil), // 1: xray.common.protocol.ServerEndpoint -} -var file_proxy_vmess_outbound_config_proto_depIdxs = []int32{ - 1, // 0: xray.proxy.vmess.outbound.Config.Receiver:type_name -> xray.common.protocol.ServerEndpoint - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_proxy_vmess_outbound_config_proto_init() } -func file_proxy_vmess_outbound_config_proto_init() { - if File_proxy_vmess_outbound_config_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_proxy_vmess_outbound_config_proto_rawDesc, - NumEnums: 0, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_proxy_vmess_outbound_config_proto_goTypes, - DependencyIndexes: file_proxy_vmess_outbound_config_proto_depIdxs, - MessageInfos: file_proxy_vmess_outbound_config_proto_msgTypes, - }.Build() - File_proxy_vmess_outbound_config_proto = out.File - file_proxy_vmess_outbound_config_proto_rawDesc = nil - file_proxy_vmess_outbound_config_proto_goTypes = nil - file_proxy_vmess_outbound_config_proto_depIdxs = nil -} diff --git a/proxy/vmess/outbound/config.proto b/proxy/vmess/outbound/config.proto deleted file mode 100644 index 2a8271581b55..000000000000 --- a/proxy/vmess/outbound/config.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax = "proto3"; - -package xray.proxy.vmess.outbound; -option csharp_namespace = "Xray.Proxy.Vmess.Outbound"; -option go_package = "github.com/xtls/xray-core/proxy/vmess/outbound"; -option java_package = "com.xray.proxy.vmess.outbound"; -option java_multiple_files = true; - -import "common/protocol/server_spec.proto"; - -message Config { - repeated xray.common.protocol.ServerEndpoint Receiver = 1; -} diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go deleted file mode 100644 index 27079e039189..000000000000 --- a/proxy/vmess/outbound/outbound.go +++ /dev/null @@ -1,249 +0,0 @@ -package outbound - -import ( - "context" - "crypto/hmac" - "crypto/sha256" - "hash/crc64" - "time" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/platform" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/retry" - "github.com/xtls/xray-core/common/session" - "github.com/xtls/xray-core/common/signal" - "github.com/xtls/xray-core/common/task" - "github.com/xtls/xray-core/common/xudp" - core "github.com/xtls/xray-core/core" - "github.com/xtls/xray-core/features/policy" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/encoding" - "github.com/xtls/xray-core/transport" - "github.com/xtls/xray-core/transport/internet" - "github.com/xtls/xray-core/transport/internet/stat" -) - -// Handler is an outbound connection handler for VMess protocol. -type Handler struct { - serverList *protocol.ServerList - serverPicker protocol.ServerPicker - policyManager policy.Manager - cone bool -} - -// New creates a new VMess outbound handler. -func New(ctx context.Context, config *Config) (*Handler, error) { - serverList := protocol.NewServerList() - for _, rec := range config.Receiver { - s, err := protocol.NewServerSpecFromPB(rec) - if err != nil { - return nil, errors.New("failed to parse server spec").Base(err) - } - serverList.AddServer(s) - } - - v := core.MustFromContext(ctx) - handler := &Handler{ - serverList: serverList, - serverPicker: protocol.NewRoundRobinServerPicker(serverList), - policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), - cone: ctx.Value("cone").(bool), - } - - return handler, nil -} - -// Process implements proxy.Outbound.Process(). -func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if !ob.Target.IsValid() { - return errors.New("target not specified").AtError() - } - ob.Name = "vmess" - ob.CanSpliceCopy = 3 - - var rec *protocol.ServerSpec - var conn stat.Connection - err := retry.ExponentialBackoff(5, 200).On(func() error { - rec = h.serverPicker.PickServer() - rawConn, err := dialer.Dial(ctx, rec.Destination()) - if err != nil { - return err - } - conn = rawConn - - return nil - }) - if err != nil { - return errors.New("failed to find an available destination").Base(err).AtWarning() - } - defer conn.Close() - - target := ob.Target - errors.LogInfo(ctx, "tunneling request to ", target, " via ", rec.Destination().NetAddr()) - - command := protocol.RequestCommandTCP - if target.Network == net.Network_UDP { - command = protocol.RequestCommandUDP - } - if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.cool" { - command = protocol.RequestCommandMux - } - - user := rec.PickUser() - request := &protocol.RequestHeader{ - Version: encoding.Version, - User: user, - Command: command, - Address: target.Address, - Port: target.Port, - Option: protocol.RequestOptionChunkStream, - } - - account := request.User.Account.(*vmess.MemoryAccount) - request.Security = account.Security - - if request.Security == protocol.SecurityType_AES128_GCM || request.Security == protocol.SecurityType_NONE || request.Security == protocol.SecurityType_CHACHA20_POLY1305 { - request.Option.Set(protocol.RequestOptionChunkMasking) - } - - if shouldEnablePadding(request.Security) && request.Option.Has(protocol.RequestOptionChunkMasking) { - request.Option.Set(protocol.RequestOptionGlobalPadding) - } - - if request.Security == protocol.SecurityType_ZERO { - request.Security = protocol.SecurityType_NONE - request.Option.Clear(protocol.RequestOptionChunkStream) - request.Option.Clear(protocol.RequestOptionChunkMasking) - } - - if account.AuthenticatedLengthExperiment { - request.Option.Set(protocol.RequestOptionAuthenticatedLength) - } - - input := link.Reader - output := link.Writer - - hashkdf := hmac.New(sha256.New, []byte("VMessBF")) - hashkdf.Write(account.ID.Bytes()) - - behaviorSeed := crc64.Checksum(hashkdf.Sum(nil), crc64.MakeTable(crc64.ISO)) - - var newCtx context.Context - var newCancel context.CancelFunc - if session.TimeoutOnlyFromContext(ctx) { - newCtx, newCancel = context.WithCancel(context.Background()) - } - - session := encoding.NewClientSession(ctx, int64(behaviorSeed)) - sessionPolicy := h.policyManager.ForLevel(request.User.Level) - - ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, func() { - cancel() - if newCancel != nil { - newCancel() - } - }, sessionPolicy.Timeouts.ConnectionIdle) - - if request.Command == protocol.RequestCommandUDP && h.cone && request.Port != 53 && request.Port != 443 { - request.Command = protocol.RequestCommandMux - request.Address = net.DomainAddress("v1.mux.cool") - request.Port = net.Port(666) - } - - requestDone := func() error { - defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) - - writer := buf.NewBufferedWriter(buf.NewWriter(conn)) - if err := session.EncodeRequestHeader(request, writer); err != nil { - return errors.New("failed to encode request").Base(err).AtWarning() - } - - bodyWriter, err := session.EncodeRequestBody(request, writer) - if err != nil { - return errors.New("failed to start encoding").Base(err) - } - bodyWriter2 := bodyWriter - if request.Command == protocol.RequestCommandMux && request.Port == 666 { - bodyWriter = xudp.NewPacketWriter(bodyWriter, target, xudp.GetGlobalID(ctx)) - } - if err := buf.CopyOnceTimeout(input, bodyWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { - return errors.New("failed to write first payload").Base(err) - } - - if err := writer.SetBuffered(false); err != nil { - return err - } - - if err := buf.Copy(input, bodyWriter, buf.UpdateActivity(timer)); err != nil { - return err - } - - if request.Option.Has(protocol.RequestOptionChunkStream) && !account.NoTerminationSignal { - if err := bodyWriter2.WriteMultiBuffer(buf.MultiBuffer{}); err != nil { - return err - } - } - - return nil - } - - responseDone := func() error { - defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) - - reader := &buf.BufferedReader{Reader: buf.NewReader(conn)} - header, err := session.DecodeResponseHeader(reader) - if err != nil { - return errors.New("failed to read header").Base(err) - } - h.handleCommand(rec.Destination(), header.Command) - - bodyReader, err := session.DecodeResponseBody(request, reader) - if err != nil { - return errors.New("failed to start encoding response").Base(err) - } - if request.Command == protocol.RequestCommandMux && request.Port == 666 { - bodyReader = xudp.NewPacketReader(&buf.BufferedReader{Reader: bodyReader}) - } - - return buf.Copy(bodyReader, output, buf.UpdateActivity(timer)) - } - - if newCtx != nil { - ctx = newCtx - } - - responseDonePost := task.OnSuccess(responseDone, task.Close(output)) - if err := task.Run(ctx, requestDone, responseDonePost); err != nil { - return errors.New("connection ends").Base(err) - } - - return nil -} - -var ( - enablePadding = false -) - -func shouldEnablePadding(s protocol.SecurityType) bool { - return enablePadding || s == protocol.SecurityType_AES128_GCM || s == protocol.SecurityType_CHACHA20_POLY1305 || s == protocol.SecurityType_AUTO -} - -func init() { - common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { - return New(ctx, config.(*Config)) - })) - - const defaultFlagValue = "NOT_DEFINED_AT_ALL" - - paddingValue := platform.NewEnvFlag(platform.UseVmessPadding).GetValue(func() string { return defaultFlagValue }) - if paddingValue != defaultFlagValue { - enablePadding = true - } -} diff --git a/proxy/vmess/validator.go b/proxy/vmess/validator.go deleted file mode 100644 index bfffadcf7ada..000000000000 --- a/proxy/vmess/validator.go +++ /dev/null @@ -1,127 +0,0 @@ -package vmess - -import ( - "crypto/hmac" - "crypto/sha256" - "hash/crc64" - "strings" - "sync" - - "github.com/xtls/xray-core/common/dice" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/proxy/vmess/aead" -) - -// TimedUserValidator is a user Validator based on time. -type TimedUserValidator struct { - sync.RWMutex - users []*protocol.MemoryUser - - behaviorSeed uint64 - behaviorFused bool - - aeadDecoderHolder *aead.AuthIDDecoderHolder -} - -// NewTimedUserValidator creates a new TimedUserValidator. -func NewTimedUserValidator() *TimedUserValidator { - tuv := &TimedUserValidator{ - users: make([]*protocol.MemoryUser, 0, 16), - aeadDecoderHolder: aead.NewAuthIDDecoderHolder(), - } - return tuv -} - -func (v *TimedUserValidator) Add(u *protocol.MemoryUser) error { - v.Lock() - defer v.Unlock() - - v.users = append(v.users, u) - - account, ok := u.Account.(*MemoryAccount) - if !ok { - return errors.New("account type is incorrect") - } - if !v.behaviorFused { - hashkdf := hmac.New(sha256.New, []byte("VMESSBSKDF")) - hashkdf.Write(account.ID.Bytes()) - v.behaviorSeed = crc64.Update(v.behaviorSeed, crc64.MakeTable(crc64.ECMA), hashkdf.Sum(nil)) - } - - var cmdkeyfl [16]byte - copy(cmdkeyfl[:], account.ID.CmdKey()) - v.aeadDecoderHolder.AddUser(cmdkeyfl, u) - - return nil -} - -func (v *TimedUserValidator) GetUsers() []*protocol.MemoryUser { - v.Lock() - defer v.Unlock() - dst := make([]*protocol.MemoryUser, len(v.users)) - copy(dst, v.users) - return dst -} - -func (v *TimedUserValidator) GetCount() int64 { - v.Lock() - defer v.Unlock() - return int64(len(v.users)) -} - -func (v *TimedUserValidator) GetAEAD(userHash []byte) (*protocol.MemoryUser, bool, error) { - v.RLock() - defer v.RUnlock() - - var userHashFL [16]byte - copy(userHashFL[:], userHash) - - userd, err := v.aeadDecoderHolder.Match(userHashFL) - if err != nil { - return nil, false, err - } - return userd.(*protocol.MemoryUser), true, nil -} - -func (v *TimedUserValidator) Remove(email string) bool { - v.Lock() - defer v.Unlock() - - email = strings.ToLower(email) - idx := -1 - for i, u := range v.users { - if strings.EqualFold(u.Email, email) { - idx = i - var cmdkeyfl [16]byte - copy(cmdkeyfl[:], u.Account.(*MemoryAccount).ID.CmdKey()) - v.aeadDecoderHolder.RemoveUser(cmdkeyfl) - break - } - } - if idx == -1 { - return false - } - ulen := len(v.users) - - v.users[idx] = v.users[ulen-1] - v.users[ulen-1] = nil - v.users = v.users[:ulen-1] - - return true -} - -func (v *TimedUserValidator) GetBehaviorSeed() uint64 { - v.Lock() - defer v.Unlock() - - v.behaviorFused = true - if v.behaviorSeed == 0 { - v.behaviorSeed = dice.RollUint64() - } - return v.behaviorSeed -} - -var ErrNotFound = errors.New("Not Found") - -var ErrTainted = errors.New("ErrTainted") diff --git a/proxy/vmess/validator_test.go b/proxy/vmess/validator_test.go deleted file mode 100644 index 83313cbc4c4e..000000000000 --- a/proxy/vmess/validator_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package vmess_test - -import ( - "testing" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/uuid" - . "github.com/xtls/xray-core/proxy/vmess" -) - -func toAccount(a *Account) protocol.Account { - account, err := a.AsAccount() - common.Must(err) - return account -} - -func BenchmarkUserValidator(b *testing.B) { - for i := 0; i < b.N; i++ { - v := NewTimedUserValidator() - - for j := 0; j < 1500; j++ { - id := uuid.New() - v.Add(&protocol.MemoryUser{ - Email: "test", - Account: toAccount(&Account{ - Id: id.String(), - }), - }) - } - - common.Close(v) - } -} diff --git a/proxy/vmess/vmess.go b/proxy/vmess/vmess.go deleted file mode 100644 index dd86f516b493..000000000000 --- a/proxy/vmess/vmess.go +++ /dev/null @@ -1,6 +0,0 @@ -// Package vmess contains the implementation of VMess protocol and transportation. -// -// VMess contains both inbound and outbound connections. VMess inbound is usually used on servers -// together with 'freedom' to talk to final destination, while VMess outbound is usually used on -// clients with 'socks' for proxying. -package vmess diff --git a/testing/scenarios/command_test.go b/testing/scenarios/command_test.go index 8ee9e4bf9038..d81d2cbe122d 100644 --- a/testing/scenarios/command_test.go +++ b/testing/scenarios/command_test.go @@ -3,6 +3,9 @@ package scenarios import ( "context" "fmt" + "github.com/xtls/xray-core/proxy/vless" + "github.com/xtls/xray-core/proxy/vless/inbound" + "github.com/xtls/xray-core/proxy/vless/outbound" "io" "strings" "testing" @@ -25,9 +28,6 @@ import ( core "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/proxy/dokodemo" "github.com/xtls/xray-core/proxy/freedom" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/inbound" - "github.com/xtls/xray-core/proxy/vmess/outbound" "github.com/xtls/xray-core/testing/servers/tcp" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -363,9 +363,9 @@ func TestCommanderAddRemoveUser(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: u1.String(), }), }, @@ -423,17 +423,14 @@ func TestCommanderAddRemoveUser(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: u2.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, }), }, }, @@ -465,7 +462,7 @@ func TestCommanderAddRemoveUser(t *testing.T) { &command.AddUserOperation{ User: &protocol.User{ Email: "test@example.com", - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: u2.String(), }), }, @@ -545,17 +542,17 @@ func TestCommanderStats(t *testing.T) { }, Inbound: []*core.InboundHandlerConfig{ { - Tag: "vmess", + Tag: "vless", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { Level: 1, Email: "test", - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -600,17 +597,14 @@ func TestCommanderStats(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, }), }, }, @@ -662,7 +656,7 @@ func TestCommanderStats(t *testing.T) { } sresp, err = sClient.GetStats(context.Background(), &statscmd.GetStatsRequest{ - Name: "inbound>>>vmess>>>traffic>>>uplink", + Name: "inbound>>>vless>>>traffic>>>uplink", Reset_: true, }) common.Must(err) diff --git a/testing/scenarios/dokodemo_test.go b/testing/scenarios/dokodemo_test.go index f74fb4bd3fa3..5c6ee379e9c7 100644 --- a/testing/scenarios/dokodemo_test.go +++ b/testing/scenarios/dokodemo_test.go @@ -1,6 +1,9 @@ package scenarios import ( + "github.com/xtls/xray-core/proxy/vless" + "github.com/xtls/xray-core/proxy/vless/inbound" + "github.com/xtls/xray-core/proxy/vless/outbound" "testing" "time" @@ -15,9 +18,6 @@ import ( "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/proxy/dokodemo" "github.com/xtls/xray-core/proxy/freedom" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/inbound" - "github.com/xtls/xray-core/proxy/vmess/outbound" "github.com/xtls/xray-core/testing/servers/tcp" "github.com/xtls/xray-core/testing/servers/udp" "golang.org/x/sync/errgroup" @@ -47,9 +47,9 @@ func TestDokodemoTCP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -94,13 +94,13 @@ func TestDokodemoTCP(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -149,9 +149,9 @@ func TestDokodemoUDP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -190,13 +190,13 @@ func TestDokodemoUDP(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, diff --git a/testing/scenarios/feature_test.go b/testing/scenarios/feature_test.go index 6fe0d85098da..abcdd1edc804 100644 --- a/testing/scenarios/feature_test.go +++ b/testing/scenarios/feature_test.go @@ -2,6 +2,9 @@ package scenarios import ( "context" + "github.com/xtls/xray-core/proxy/vless" + "github.com/xtls/xray-core/proxy/vless/inbound" + "github.com/xtls/xray-core/proxy/vless/outbound" "io" "net/http" "net/url" @@ -26,9 +29,6 @@ import ( "github.com/xtls/xray-core/proxy/freedom" v2http "github.com/xtls/xray-core/proxy/http" "github.com/xtls/xray-core/proxy/socks" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/inbound" - "github.com/xtls/xray-core/proxy/vmess/outbound" "github.com/xtls/xray-core/testing/servers/tcp" "github.com/xtls/xray-core/testing/servers/udp" "github.com/xtls/xray-core/transport/internet" @@ -108,9 +108,9 @@ func TestProxy(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: serverUserID.String(), }), }, @@ -135,9 +135,9 @@ func TestProxy(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: proxyUserID.String(), }), }, @@ -170,13 +170,13 @@ func TestProxy(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: serverUserID.String(), }), }, @@ -193,13 +193,13 @@ func TestProxy(t *testing.T) { { Tag: "proxy", ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(proxyPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: proxyUserID.String(), }), }, @@ -241,9 +241,9 @@ func TestProxyOverKCP(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: serverUserID.String(), }), }, @@ -268,9 +268,9 @@ func TestProxyOverKCP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: proxyUserID.String(), }), }, @@ -308,13 +308,13 @@ func TestProxyOverKCP(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: serverUserID.String(), }), }, @@ -334,13 +334,13 @@ func TestProxyOverKCP(t *testing.T) { { Tag: "proxy", ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(proxyPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: proxyUserID.String(), }), }, @@ -658,9 +658,9 @@ func TestDialXray(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -685,17 +685,14 @@ func TestDialXray(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, }), }, }, diff --git a/testing/scenarios/policy_test.go b/testing/scenarios/policy_test.go index 1e8aafeaddca..5ad00e9a299e 100644 --- a/testing/scenarios/policy_test.go +++ b/testing/scenarios/policy_test.go @@ -1,7 +1,9 @@ package scenarios import ( - "io" + "github.com/xtls/xray-core/proxy/vless" + "github.com/xtls/xray-core/proxy/vless/inbound" + "github.com/xtls/xray-core/proxy/vless/outbound" "testing" "time" @@ -17,9 +19,7 @@ import ( "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/proxy/dokodemo" "github.com/xtls/xray-core/proxy/freedom" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/inbound" - "github.com/xtls/xray-core/proxy/vmess/outbound" + "github.com/xtls/xray-core/testing/servers/tcp" "golang.org/x/sync/errgroup" ) @@ -43,112 +43,6 @@ func startQuickClosingTCPServer() (net.Listener, error) { return listener, nil } -func TestVMessClosing(t *testing.T) { - tcpServer, err := startQuickClosingTCPServer() - common.Must(err) - defer tcpServer.Close() - - dest := net.DestinationFromAddr(tcpServer.Addr()) - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&policy.Config{ - Level: map[uint32]*policy.Policy{ - 0: { - Timeout: &policy.Policy_Timeout{ - UplinkOnly: &policy.Second{Value: 0}, - DownlinkOnly: &policy.Second{Value: 0}, - }, - }, - }, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&policy.Config{ - Level: map[uint32]*policy.Policy{ - 0: { - Timeout: &policy.Policy_Timeout{ - UplinkOnly: &policy.Second{Value: 0}, - DownlinkOnly: &policy.Second{Value: 0}, - }, - }, - }, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, - }), - }, - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - if err := testTCPConn(clientPort, 1024, time.Second*2)(); err != io.EOF { - t.Error(err) - } -} - func TestZeroBuffer(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, @@ -182,9 +76,9 @@ func TestZeroBuffer(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -223,17 +117,14 @@ func TestZeroBuffer(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, }), }, }, diff --git a/testing/scenarios/reverse_test.go b/testing/scenarios/reverse_test.go index faaa402efc05..469a57dfdceb 100644 --- a/testing/scenarios/reverse_test.go +++ b/testing/scenarios/reverse_test.go @@ -1,6 +1,9 @@ package scenarios import ( + "github.com/xtls/xray-core/proxy/vless" + "github.com/xtls/xray-core/proxy/vless/inbound" + "github.com/xtls/xray-core/proxy/vless/outbound" "testing" "time" @@ -19,9 +22,7 @@ import ( "github.com/xtls/xray-core/proxy/blackhole" "github.com/xtls/xray-core/proxy/dokodemo" "github.com/xtls/xray-core/proxy/freedom" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/inbound" - "github.com/xtls/xray-core/proxy/vmess/outbound" + "github.com/xtls/xray-core/testing/servers/tcp" "golang.org/x/sync/errgroup" ) @@ -87,9 +88,9 @@ func TestReverseProxy(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -155,17 +156,14 @@ func TestReverseProxy(t *testing.T) { { Tag: "reverse", ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(reversePort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, }), }, }, @@ -266,9 +264,9 @@ func TestReverseProxyLongRunning(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -348,17 +346,14 @@ func TestReverseProxyLongRunning(t *testing.T) { { Tag: "reverse", ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(reversePort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, }), }, }, diff --git a/testing/scenarios/tls_test.go b/testing/scenarios/tls_test.go index a8abccd77a24..2d1696c4324d 100644 --- a/testing/scenarios/tls_test.go +++ b/testing/scenarios/tls_test.go @@ -2,6 +2,9 @@ package scenarios import ( "crypto/x509" + "github.com/xtls/xray-core/proxy/vless" + "github.com/xtls/xray-core/proxy/vless/inbound" + "github.com/xtls/xray-core/proxy/vless/outbound" "runtime" "testing" "time" @@ -16,9 +19,6 @@ import ( core "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/proxy/dokodemo" "github.com/xtls/xray-core/proxy/freedom" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/inbound" - "github.com/xtls/xray-core/proxy/vmess/outbound" "github.com/xtls/xray-core/testing/servers/tcp" "github.com/xtls/xray-core/testing/servers/udp" "github.com/xtls/xray-core/transport/internet" @@ -54,9 +54,9 @@ func TestSimpleTLSConnection(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -89,13 +89,13 @@ func TestSimpleTLSConnection(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -169,9 +169,9 @@ func TestAutoIssuingCertificate(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -204,13 +204,13 @@ func TestAutoIssuingCertificate(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -274,9 +274,9 @@ func TestTLSOverKCP(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -309,13 +309,13 @@ func TestTLSOverKCP(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -374,9 +374,9 @@ func TestTLSOverWebSocket(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -409,13 +409,13 @@ func TestTLSOverWebSocket(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -490,9 +490,9 @@ func TestGRPC(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -525,13 +525,13 @@ func TestGRPC(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -606,9 +606,9 @@ func TestGRPCMultiMode(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -641,13 +641,13 @@ func TestGRPCMultiMode(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -717,9 +717,9 @@ func TestSimpleTLSConnectionPinned(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -752,13 +752,13 @@ func TestSimpleTLSConnectionPinned(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -819,9 +819,9 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -854,13 +854,13 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -920,9 +920,9 @@ func TestUTLSConnectionPinned(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -955,13 +955,13 @@ func TestUTLSConnectionPinned(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -1023,9 +1023,9 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -1058,13 +1058,13 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, diff --git a/testing/scenarios/transport_test.go b/testing/scenarios/transport_test.go index bb16deee6388..875fc1cf5b8f 100644 --- a/testing/scenarios/transport_test.go +++ b/testing/scenarios/transport_test.go @@ -1,6 +1,9 @@ package scenarios import ( + "github.com/xtls/xray-core/proxy/vless" + "github.com/xtls/xray-core/proxy/vless/inbound" + "github.com/xtls/xray-core/proxy/vless/outbound" "testing" "time" @@ -13,9 +16,7 @@ import ( "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/proxy/dokodemo" "github.com/xtls/xray-core/proxy/freedom" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/inbound" - "github.com/xtls/xray-core/proxy/vmess/outbound" + "github.com/xtls/xray-core/testing/servers/tcp" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/headers/http" @@ -50,9 +51,9 @@ func TestHTTPConnectionHeader(t *testing.T) { }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ + Clients: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, @@ -85,13 +86,13 @@ func TestHTTPConnectionHeader(t *testing.T) { Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ + Vnext: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { - Account: serial.ToTypedMessage(&vmess.Account{ + Account: serial.ToTypedMessage(&vless.Account{ Id: userID.String(), }), }, diff --git a/testing/scenarios/vmess_test.go b/testing/scenarios/vmess_test.go deleted file mode 100644 index dbffe0d60b22..000000000000 --- a/testing/scenarios/vmess_test.go +++ /dev/null @@ -1,1475 +0,0 @@ -package scenarios - -import ( - "os" - "testing" - "time" - - "github.com/xtls/xray-core/app/log" - "github.com/xtls/xray-core/app/proxyman" - "github.com/xtls/xray-core/common" - clog "github.com/xtls/xray-core/common/log" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/serial" - "github.com/xtls/xray-core/common/uuid" - core "github.com/xtls/xray-core/core" - "github.com/xtls/xray-core/proxy/dokodemo" - "github.com/xtls/xray-core/proxy/freedom" - "github.com/xtls/xray-core/proxy/vmess" - "github.com/xtls/xray-core/proxy/vmess/inbound" - "github.com/xtls/xray-core/proxy/vmess/outbound" - "github.com/xtls/xray-core/testing/servers/tcp" - "github.com/xtls/xray-core/testing/servers/udp" - "github.com/xtls/xray-core/transport/internet" - "github.com/xtls/xray-core/transport/internet/kcp" - "golang.org/x/sync/errgroup" -) - -func TestVMessDynamicPort(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - - retry := 1 - serverPort := tcp.PickPort() - for { - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.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{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort + 100)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{ - Range: []*net.PortRange{{From: uint32(serverPort + 1), To: uint32(serverPort + 99)}}, - }, - - Listen: net.NewIPOrDomain(net.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: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - server, _ := InitializeServerConfig(serverConfig) - if server != nil && WaitConnAvailableWithTest(t, testTCPConn(serverPort+100, 1024, time.Second*2)) { - defer CloseServer(server) - break - } - retry += 1 - if retry > 5 { - t.Fatal("All attempts failed to start server") - } - serverPort = tcp.PickPort() - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }, - }, - }), - }, - }, - } - - server, err := InitializeServerConfig(clientConfig) - common.Must(err) - defer CloseServer(server) - - if !WaitConnAvailableWithTest(t, testTCPConn(clientPort, 1024, time.Second*2)) { - t.Fail() - } -} - -func TestVMessGCM(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, - }), - }, - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - if err != nil { - t.Fatal("Failed to initialize all servers: ", err.Error()) - } - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 10; i++ { - errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) - } - - if err := errg.Wait(); err != nil { - t.Error(err) - } -} - -func TestVMessGCMReadv(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, - }), - }, - }, - }, - }, - }), - }, - }, - } - - const envName = "XRAY_BUF_READV" - common.Must(os.Setenv(envName, "enable")) - defer os.Unsetenv(envName) - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - if err != nil { - t.Fatal("Failed to initialize all servers: ", err.Error()) - } - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 10; i++ { - errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) - } - if err := errg.Wait(); err != nil { - t.Error(err) - } -} - -func TestVMessGCMUDP(t *testing.T) { - udpServer := udp.Server{ - MsgProcessor: xor, - } - dest, err := udpServer.Start() - common.Must(err) - defer udpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := udp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_UDP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, - }), - }, - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 2; i++ { - errg.Go(testUDPConn(clientPort, 1024, time.Second*5)) - } - if err := errg.Wait(); err != nil { - t.Error(err) - } -} - -func TestVMessChacha20(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_CHACHA20_POLY1305, - }, - }), - }, - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 10; i++ { - errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*20)) - } - - if err := errg.Wait(); err != nil { - t.Error(err) - } -} - -func TestVMessNone(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_NONE, - }, - }), - }, - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 10; i++ { - errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30)) - } - if err := errg.Wait(); err != nil { - t.Error(err) - } -} - -func TestVMessKCP(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := udp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", - }, - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, - }), - }, - }, - }, - }, - }), - SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ - StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 2; i++ { - errg.Go(testTCPConn(clientPort, 1024, time.Minute*2)) - } - if err := errg.Wait(); err != nil { - t.Error(err) - } -} - -func TestVMessKCPLarge(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := udp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", - TransportSettings: []*internet.TransportConfig{ - { - ProtocolName: "mkcp", - Settings: serial.ToTypedMessage(&kcp.Config{ - ReadBuffer: &kcp.ReadBuffer{ - Size: 512 * 1024, - }, - WriteBuffer: &kcp.WriteBuffer{ - Size: 512 * 1024, - }, - UplinkCapacity: &kcp.UplinkCapacity{ - Value: 20, - }, - DownlinkCapacity: &kcp.DownlinkCapacity{ - Value: 20, - }, - }), - }, - }, - }, - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, - }), - }, - }, - }, - }, - }), - SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ - StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", - TransportSettings: []*internet.TransportConfig{ - { - ProtocolName: "mkcp", - Settings: serial.ToTypedMessage(&kcp.Config{ - ReadBuffer: &kcp.ReadBuffer{ - Size: 512 * 1024, - }, - WriteBuffer: &kcp.WriteBuffer{ - Size: 512 * 1024, - }, - UplinkCapacity: &kcp.UplinkCapacity{ - Value: 20, - }, - DownlinkCapacity: &kcp.DownlinkCapacity{ - Value: 20, - }, - }), - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - - var errg errgroup.Group - for i := 0; i < 2; i++ { - errg.Go(testTCPConn(clientPort, 513*1024, time.Minute*5)) - } - if err := errg.Wait(); err != nil { - t.Error(err) - } - - defer func() { - <-time.After(5 * time.Second) - CloseAllServers(servers) - }() -} - -func TestVMessGCMMux(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ - MultiplexSettings: &proxyman.MultiplexingConfig{ - Enabled: true, - Concurrency: 4, - }, - }), - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, - }), - }, - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - for range "abcd" { - var errg errgroup.Group - for i := 0; i < 16; i++ { - errg.Go(testTCPConn(clientPort, 10240, time.Second*20)) - } - if err := errg.Wait(); err != nil { - t.Fatal(err) - } - time.Sleep(time.Second) - } -} - -func TestVMessGCMMuxUDP(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - udpServer := udp.Server{ - MsgProcessor: xor, - } - udpDest, err := udpServer.Start() - common.Must(err) - defer udpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientUDPPort := udp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientUDPPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(udpDest.Address), - Port: uint32(udpDest.Port), - Networks: []net.Network{net.Network_UDP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ - MultiplexSettings: &proxyman.MultiplexingConfig{ - Enabled: true, - Concurrency: 4, - }, - }), - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, - }), - }, - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - - for range "ab" { - var errg errgroup.Group - for i := 0; i < 2; i++ { - errg.Go(testTCPConn(clientPort, 1024, time.Second*10)) - errg.Go(testUDPConn(clientUDPPort, 1024, time.Second*10)) - } - if err := errg.Wait(); err != nil { - t.Error(err) - } - time.Sleep(time.Second) - } - - defer func() { - <-time.After(5 * time.Second) - CloseAllServers(servers) - }() -} - -func TestVMessZero(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_ZERO, - }, - }), - }, - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 10; i++ { - errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30)) - } - if err := errg.Wait(); err != nil { - t.Error(err) - } -} - -func TestVMessGCMLengthAuth(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, - TestsEnabled: "AuthenticatedLength", - }), - }, - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - if err != nil { - t.Fatal("Failed to initialize all servers: ", err.Error()) - } - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 10; i++ { - errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) - } - - if err := errg.Wait(); err != nil { - t.Error(err) - } -} - -func TestVMessGCMLengthAuthPlusNoTerminationSignal(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - TestsEnabled: "AuthenticatedLength|NoTerminationSignal", - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Receiver: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vmess.Account{ - Id: userID.String(), - SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AES128_GCM, - }, - TestsEnabled: "AuthenticatedLength|NoTerminationSignal", - }), - }, - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - if err != nil { - t.Fatal("Failed to initialize all servers: ", err.Error()) - } - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 10; i++ { - errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) - } - - if err := errg.Wait(); err != nil { - t.Error(err) - } -}