From 33755d6e908227f270881e7b859eabedb393c3dc Mon Sep 17 00:00:00 2001 From: RPRX <63339210+rprx@users.noreply.github.com> Date: Mon, 18 Jan 2021 22:52:35 +0000 Subject: [PATCH] Refactor: Shadowsocks AEAD Single-port Multi-user (Needs Optimizations) https://t.me/projectXray/170851 --- infra/conf/shadowsocks.go | 70 ++++++++----- infra/conf/shadowsocks_test.go | 8 +- proxy/shadowsocks/config.pb.go | 87 +++++++--------- proxy/shadowsocks/config.proto | 7 +- proxy/shadowsocks/protocol.go | 158 ++++++++++++++++++++++++----- proxy/shadowsocks/protocol_test.go | 4 +- proxy/shadowsocks/server.go | 54 ++++++---- 7 files changed, 259 insertions(+), 129 deletions(-) diff --git a/infra/conf/shadowsocks.go b/infra/conf/shadowsocks.go index 9ccd37b4..fbb2a79d 100644 --- a/infra/conf/shadowsocks.go +++ b/infra/conf/shadowsocks.go @@ -33,35 +33,60 @@ func cipherFromString(c string) shadowsocks.CipherType { } } +type ShadowsocksUserConfig struct { + Cipher string `json:"method"` + Password string `json:"password"` + Level byte `json:"level"` + Email string `json:"email"` +} + type ShadowsocksServerConfig struct { - Cipher string `json:"method"` - Password string `json:"password"` - UDP bool `json:"udp"` - Level byte `json:"level"` - Email string `json:"email"` - NetworkList *NetworkList `json:"network"` + Cipher string `json:"method"` + Password string `json:"password"` + Level byte `json:"level"` + Email string `json:"email"` + Users []*ShadowsocksUserConfig `json:"clients"` + NetworkList *NetworkList `json:"network"` } func (v *ShadowsocksServerConfig) Build() (proto.Message, error) { config := new(shadowsocks.ServerConfig) - config.UdpEnabled = v.UDP config.Network = v.NetworkList.Build() - if v.Password == "" { - return nil, newError("Shadowsocks password is not specified.") - } - account := &shadowsocks.Account{ - Password: v.Password, - } - account.CipherType = cipherFromString(v.Cipher) - if account.CipherType == shadowsocks.CipherType_UNKNOWN { - return nil, newError("unknown cipher method: ", v.Cipher) - } - - config.User = &protocol.User{ - Email: v.Email, - Level: uint32(v.Level), - Account: serial.ToTypedMessage(account), + if v.Users != nil { + for _, user := range v.Users { + account := &shadowsocks.Account{ + Password: user.Password, + CipherType: cipherFromString(user.Cipher), + } + if account.Password == "" { + return nil, newError("Shadowsocks password is not specified.") + } + if account.CipherType < 5 || account.CipherType > 7 { + return nil, newError("unsupported cipher method: ", user.Cipher) + } + config.Users = append(config.Users, &protocol.User{ + Email: user.Email, + Level: uint32(user.Level), + Account: serial.ToTypedMessage(account), + }) + } + } else { + account := &shadowsocks.Account{ + Password: v.Password, + CipherType: cipherFromString(v.Cipher), + } + if account.Password == "" { + return nil, newError("Shadowsocks password is not specified.") + } + if account.CipherType == shadowsocks.CipherType_UNKNOWN { + return nil, newError("unknown cipher method: ", v.Cipher) + } + config.Users = append(config.Users, &protocol.User{ + Email: v.Email, + Level: uint32(v.Level), + Account: serial.ToTypedMessage(account), + }) } return config, nil @@ -73,7 +98,6 @@ type ShadowsocksServerTarget struct { Cipher string `json:"method"` Password string `json:"password"` Email string `json:"email"` - Ota bool `json:"ota"` Level byte `json:"level"` } diff --git a/infra/conf/shadowsocks_test.go b/infra/conf/shadowsocks_test.go index 2455551a..c3377a9a 100644 --- a/infra/conf/shadowsocks_test.go +++ b/infra/conf/shadowsocks_test.go @@ -18,17 +18,17 @@ func TestShadowsocksServerConfigParsing(t *testing.T) { runMultiTestCase(t, []TestCase{ { Input: `{ - "method": "aes-128-cfb", + "method": "aes-128-gcm", "password": "xray-password" }`, Parser: loadJSON(creator), Output: &shadowsocks.ServerConfig{ - User: &protocol.User{ + Users: []*protocol.User{{ Account: serial.ToTypedMessage(&shadowsocks.Account{ - CipherType: shadowsocks.CipherType_AES_128_CFB, + CipherType: shadowsocks.CipherType_AES_128_GCM, Password: "xray-password", }), - }, + }}, Network: []net.Network{net.Network_TCP}, }, }, diff --git a/proxy/shadowsocks/config.pb.go b/proxy/shadowsocks/config.pb.go index fcf321d9..6bac0d95 100644 --- a/proxy/shadowsocks/config.pb.go +++ b/proxy/shadowsocks/config.pb.go @@ -154,13 +154,8 @@ type ServerConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // UdpEnabled specified whether or not to enable UDP for Shadowsocks. - // Deprecated. Use 'network' field. - // - // Deprecated: Do not use. - UdpEnabled bool `protobuf:"varint,1,opt,name=udp_enabled,json=udpEnabled,proto3" json:"udp_enabled,omitempty"` - User *protocol.User `protobuf:"bytes,2,opt,name=user,proto3" json:"user,omitempty"` - Network []net.Network `protobuf:"varint,3,rep,packed,name=network,proto3,enum=xray.common.net.Network" json:"network,omitempty"` + Users []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"` + Network []net.Network `protobuf:"varint,2,rep,packed,name=network,proto3,enum=xray.common.net.Network" json:"network,omitempty"` } func (x *ServerConfig) Reset() { @@ -195,17 +190,9 @@ func (*ServerConfig) Descriptor() ([]byte, []int) { return file_proxy_shadowsocks_config_proto_rawDescGZIP(), []int{1} } -// Deprecated: Do not use. -func (x *ServerConfig) GetUdpEnabled() bool { +func (x *ServerConfig) GetUsers() []*protocol.User { if x != nil { - return x.UdpEnabled - } - return false -} - -func (x *ServerConfig) GetUser() *protocol.User { - if x != nil { - return x.User + return x.Users } return nil } @@ -282,39 +269,37 @@ var file_proxy_shadowsocks_config_proto_rawDesc = []byte{ 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x22, 0x97, 0x01, - 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, - 0x0a, 0x0b, 0x75, 0x64, 0x70, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0a, 0x75, 0x64, 0x70, 0x45, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 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, 0x32, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x22, 0x4c, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, 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, 0x06, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2a, 0x9f, 0x01, 0x0a, 0x0a, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, - 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x32, 0x38, 0x5f, 0x43, 0x46, 0x42, - 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x32, 0x35, 0x36, 0x5f, 0x43, 0x46, - 0x42, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x10, - 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x49, 0x45, - 0x54, 0x46, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x32, 0x38, 0x5f, - 0x47, 0x43, 0x4d, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x32, 0x35, 0x36, - 0x5f, 0x47, 0x43, 0x4d, 0x10, 0x06, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, - 0x32, 0x30, 0x5f, 0x50, 0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x10, 0x07, 0x12, 0x08, 0x0a, - 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x08, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, - 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x50, 0x01, 0x5a, 0x2b, 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, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, - 0x6f, 0x63, 0x6b, 0x73, 0xaa, 0x02, 0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x2e, 0x53, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x22, 0x74, 0x0a, + 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x30, 0x0a, + 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 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, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, + 0x32, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, + 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, + 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x22, 0x4c, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, 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, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2a, 0x9f, 0x01, 0x0a, 0x0a, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a, + 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x32, 0x38, 0x5f, 0x43, 0x46, 0x42, 0x10, 0x01, 0x12, 0x0f, + 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x32, 0x35, 0x36, 0x5f, 0x43, 0x46, 0x42, 0x10, 0x02, 0x12, + 0x0c, 0x0a, 0x08, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x10, 0x03, 0x12, 0x11, 0x0a, + 0x0d, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x49, 0x45, 0x54, 0x46, 0x10, 0x04, + 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x32, 0x38, 0x5f, 0x47, 0x43, 0x4d, 0x10, + 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x32, 0x35, 0x36, 0x5f, 0x47, 0x43, 0x4d, + 0x10, 0x06, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x50, + 0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x10, 0x07, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, + 0x45, 0x10, 0x08, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, + 0x73, 0x50, 0x01, 0x5a, 0x2b, 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, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, + 0xaa, 0x02, 0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, 0x68, + 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -342,7 +327,7 @@ var file_proxy_shadowsocks_config_proto_goTypes = []interface{}{ } var file_proxy_shadowsocks_config_proto_depIdxs = []int32{ 0, // 0: xray.proxy.shadowsocks.Account.cipher_type:type_name -> xray.proxy.shadowsocks.CipherType - 4, // 1: xray.proxy.shadowsocks.ServerConfig.user:type_name -> xray.common.protocol.User + 4, // 1: xray.proxy.shadowsocks.ServerConfig.users:type_name -> xray.common.protocol.User 5, // 2: xray.proxy.shadowsocks.ServerConfig.network:type_name -> xray.common.net.Network 6, // 3: xray.proxy.shadowsocks.ClientConfig.server:type_name -> xray.common.protocol.ServerEndpoint 4, // [4:4] is the sub-list for method output_type diff --git a/proxy/shadowsocks/config.proto b/proxy/shadowsocks/config.proto index 8d5bb502..39ae11a3 100644 --- a/proxy/shadowsocks/config.proto +++ b/proxy/shadowsocks/config.proto @@ -28,11 +28,8 @@ enum CipherType { } message ServerConfig { - // UdpEnabled specified whether or not to enable UDP for Shadowsocks. - // Deprecated. Use 'network' field. - bool udp_enabled = 1 [deprecated = true]; - xray.common.protocol.User user = 2; - repeated xray.common.net.Network network = 3; + repeated xray.common.protocol.User users = 1; + repeated xray.common.net.Network network = 2; } message ClientConfig { diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index 2849d5e9..328639bb 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -1,6 +1,7 @@ package shadowsocks import ( + "crypto/cipher" "crypto/hmac" "crypto/rand" "crypto/sha256" @@ -10,6 +11,7 @@ import ( "github.com/xtls/xray-core/common" "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/net" "github.com/xtls/xray-core/common/protocol" @@ -28,8 +30,32 @@ var addrParser = protocol.NewAddressParser( }), ) +type FullReader struct { + reader io.Reader + buffer []byte +} + +func (r *FullReader) Read(p []byte) (n int, err error) { + if r.buffer != nil { + n := copy(p, r.buffer) + if n == len(r.buffer) { + r.buffer = nil + } else { + r.buffer = r.buffer[n:] + } + if n == len(p) { + return n, nil + } else { + m, err := r.reader.Read(p[n:]) + return n + m, err + } + } + return r.reader.Read(p) +} + // ReadTCPSession reads a Shadowsocks TCP session from the given reader, returns its header and remaining parts. -func ReadTCPSession(user *protocol.MemoryUser, reader io.Reader) (*protocol.RequestHeader, buf.Reader, error) { +func ReadTCPSession(users []*protocol.MemoryUser, reader io.Reader) (*protocol.RequestHeader, buf.Reader, error) { + user := users[0] account := user.Account.(*MemoryAccount) hashkdf := hmac.New(sha256.New, []byte("SSBSKDF")) @@ -44,28 +70,78 @@ func ReadTCPSession(user *protocol.MemoryUser, reader io.Reader) (*protocol.Requ DrainSize := BaseDrainSize + 16 + 38 + RandDrainRolled readSizeRemain := DrainSize + var r2 buf.Reader + + if len(users) > 1 { + buffer := buf.New() + defer buffer.Release() + + if _, err := buffer.ReadFullFrom(reader, 50); err != nil { + readSizeRemain -= int(buffer.Len()) + DrainConnN(reader, readSizeRemain) + return nil, nil, newError("failed to read 50 bytes").Base(err) + } + + bs := buffer.Bytes() + + var aeadCipher *AEADCipher + var ivLen int32 + subkey := make([]byte, 32) + length := make([]byte, 16) + var aead cipher.AEAD + var err error + for _, user = range users { + account = user.Account.(*MemoryAccount) + aeadCipher = account.Cipher.(*AEADCipher) + ivLen = aeadCipher.IVSize() + subkey = subkey[:aeadCipher.KeyBytes] + hkdfSHA1(account.Key, bs[:ivLen], subkey) + aead = aeadCipher.AEADAuthCreator(subkey) + _, err = aead.Open(length[:0], length[4:16], bs[ivLen:ivLen+18], nil) + if err == nil { + reader = &FullReader{reader, bs[ivLen:]} + auth := &crypto.AEADAuthenticator{ + AEAD: aead, + NonceGenerator: crypto.GenerateInitialAEADNonce(), + } + r2 = crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{ + Auth: auth, + }, reader, protocol.TransferTypeStream, nil) + break + } + } + if err != nil { + readSizeRemain -= int(buffer.Len()) + DrainConnN(reader, readSizeRemain) + return nil, nil, newError("failed to match an user").Base(err) + } + } + buffer := buf.New() defer buffer.Release() - ivLen := account.Cipher.IVSize() - var iv []byte - if ivLen > 0 { - if _, err := buffer.ReadFullFrom(reader, ivLen); err != nil { - readSizeRemain -= int(buffer.Len()) - DrainConnN(reader, readSizeRemain) - return nil, nil, newError("failed to read IV").Base(err) + if r2 == nil { + ivLen := account.Cipher.IVSize() + var iv []byte + if ivLen > 0 { + if _, err := buffer.ReadFullFrom(reader, ivLen); err != nil { + readSizeRemain -= int(buffer.Len()) + DrainConnN(reader, readSizeRemain) + return nil, nil, newError("failed to read IV").Base(err) + } + iv = append([]byte(nil), buffer.BytesTo(ivLen)...) } - iv = append([]byte(nil), buffer.BytesTo(ivLen)...) + r, err := account.Cipher.NewDecryptionReader(account.Key, iv, reader) + if err != nil { + readSizeRemain -= int(buffer.Len()) + DrainConnN(reader, readSizeRemain) + return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError() + } + r2 = r } - r, err := account.Cipher.NewDecryptionReader(account.Key, iv, reader) - if err != nil { - readSizeRemain -= int(buffer.Len()) - DrainConnN(reader, readSizeRemain) - return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError() - } - br := &buf.BufferedReader{Reader: r} + br := &buf.BufferedReader{Reader: r2} request := &protocol.RequestHeader{ Version: Version, @@ -185,17 +261,49 @@ func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buff return buffer, nil } -func DecodeUDPPacket(user *protocol.MemoryUser, payload *buf.Buffer) (*protocol.RequestHeader, *buf.Buffer, error) { - account := user.Account.(*MemoryAccount) +func DecodeUDPPacket(users []*protocol.MemoryUser, payload *buf.Buffer) (*protocol.RequestHeader, *buf.Buffer, error) { + var user *protocol.MemoryUser + var account *MemoryAccount + var err error - var iv []byte - if !account.Cipher.IsAEAD() && account.Cipher.IVSize() > 0 { - // Keep track of IV as it gets removed from payload in DecodePacket. - iv = make([]byte, account.Cipher.IVSize()) - copy(iv, payload.BytesTo(account.Cipher.IVSize())) + if len(users) > 1 { + bs := payload.Bytes() + + var aeadCipher *AEADCipher + var ivLen int32 + subkey := make([]byte, 32) + data := make([]byte, 8192) + var aead cipher.AEAD + var d []byte + for _, user = range users { + account = user.Account.(*MemoryAccount) + aeadCipher = account.Cipher.(*AEADCipher) + ivLen = aeadCipher.IVSize() + subkey = subkey[:aeadCipher.KeyBytes] + hkdfSHA1(account.Key, bs[:ivLen], subkey) + aead = aeadCipher.AEADAuthCreator(subkey) + d, err = aead.Open(data[:0], data[8180:8192], bs[ivLen:], nil) + if err == nil { + payload.Clear() + payload.Write(d) + break + } + } + } else { + user = users[0] + account = user.Account.(*MemoryAccount) + + var iv []byte + if !account.Cipher.IsAEAD() && account.Cipher.IVSize() > 0 { + // Keep track of IV as it gets removed from payload in DecodePacket. + iv = make([]byte, account.Cipher.IVSize()) + copy(iv, payload.BytesTo(account.Cipher.IVSize())) + } + + err = account.Cipher.DecodePacket(account.Key, payload) } - if err := account.Cipher.DecodePacket(account.Key, payload); err != nil { + if err != nil { return nil, nil, newError("failed to decrypt UDP payload").Base(err) } @@ -230,7 +338,7 @@ func (v *UDPReader) ReadMultiBuffer() (buf.MultiBuffer, error) { buffer.Release() return nil, err } - u, payload, err := DecodeUDPPacket(v.User, buffer) + u, payload, err := DecodeUDPPacket([]*protocol.MemoryUser{v.User}, buffer) if err != nil { buffer.Release() return nil, err diff --git a/proxy/shadowsocks/protocol_test.go b/proxy/shadowsocks/protocol_test.go index 9654e9df..9b90f7c2 100644 --- a/proxy/shadowsocks/protocol_test.go +++ b/proxy/shadowsocks/protocol_test.go @@ -38,7 +38,7 @@ func TestUDPEncoding(t *testing.T) { encodedData, err := EncodeUDPPacket(request, data.Bytes()) common.Must(err) - decodedRequest, decodedData, err := DecodeUDPPacket(request.User, encodedData) + decodedRequest, decodedData, err := DecodeUDPPacket([]*protocol.MemoryUser{request.User}, encodedData) common.Must(err) if r := cmp.Diff(decodedData.Bytes(), data.Bytes()); r != "" { @@ -117,7 +117,7 @@ func TestTCPRequest(t *testing.T) { common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{data})) - decodedRequest, reader, err := ReadTCPSession(request.User, cache) + decodedRequest, reader, err := ReadTCPSession([]*protocol.MemoryUser{request.User}, cache) common.Must(err) if r := cmp.Diff(decodedRequest, request); r != "" { t.Error("request: ", r) diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 273f4cc9..dadbfeeb 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -22,30 +22,32 @@ import ( type Server struct { config *ServerConfig - user *protocol.MemoryUser + users []*protocol.MemoryUser policyManager policy.Manager cone bool } // NewServer create a new Shadowsocks server. func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { - if config.GetUser() == nil { - return nil, newError("user is not specified") - } - - mUser, err := config.User.ToMemoryUser() - if err != nil { - return nil, newError("failed to parse user account").Base(err) + if config.Users == nil { + return nil, newError("empty users") } v := core.MustFromContext(ctx) s := &Server{ config: config, - user: mUser, policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), cone: ctx.Value("cone").(bool), } + for _, user := range config.Users { + u, err := user.ToMemoryUser() + if err != nil { + return nil, newError("failed to parse user account").Base(err) + } + s.users = append(s.users, u) + } + return s, nil } @@ -54,9 +56,6 @@ func (s *Server) Network() []net.Network { if len(list) == 0 { list = append(list, net.Network_TCP) } - if s.config.UdpEnabled { - list = append(list, net.Network_UDP) - } return list } @@ -103,7 +102,9 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection if inbound == nil { panic("no inbound metadata") } - inbound.User = s.user + if len(s.users) == 1 { + inbound.User = s.users[0] + } var dest *net.Destination @@ -115,9 +116,21 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection } for _, payload := range mpayload { - request, data, err := DecodeUDPPacket(s.user, payload) + var request *protocol.RequestHeader + var data *buf.Buffer + var err error + + if inbound.User != nil { + request, data, err = DecodeUDPPacket([]*protocol.MemoryUser{inbound.User}, payload) + } else { + request, data, err = DecodeUDPPacket(s.users, payload) + if err == nil { + inbound.User = request.User + } + } + if err != nil { - if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.IsValid() { + if inbound.Source.IsValid() { newError("dropping invalid UDP packet from: ", inbound.Source).Base(err).WriteToLog(session.ExportIDToError(ctx)) log.Record(&log.AccessMessage{ From: inbound.Source, @@ -159,11 +172,13 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection } func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error { - sessionPolicy := s.policyManager.ForLevel(s.user.Level) - conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)) + sessionPolicy := s.policyManager.ForLevel(0) + if err := conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { + return newError("unable to set read deadline").Base(err).AtWarning() + } bufferedReader := buf.BufferedReader{Reader: buf.NewReader(conn)} - request, bodyReader, err := ReadTCPSession(s.user, &bufferedReader) + request, bodyReader, err := ReadTCPSession(s.users, &bufferedReader) if err != nil { log.Record(&log.AccessMessage{ From: conn.RemoteAddr(), @@ -179,7 +194,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, if inbound == nil { panic("no inbound metadata") } - inbound.User = s.user + inbound.User = request.User dest := request.Destination() ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ @@ -191,6 +206,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, }) newError("tunnelling request to ", dest).WriteToLog(session.ExportIDToError(ctx)) + sessionPolicy = s.policyManager.ForLevel(request.User.Level) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)