diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go new file mode 100644 index 000000000..df663d292 --- /dev/null +++ b/proxy/shadowsocks/client.go @@ -0,0 +1,48 @@ +package shadowsocks + +import ( + "v2ray.com/core/common/alloc" + "v2ray.com/core/common/log" + v2net "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" + "v2ray.com/core/common/retry" + "v2ray.com/core/proxy" + "v2ray.com/core/transport/internet" + "v2ray.com/core/transport/ray" +) + +type Client struct { + serverPicker protocol.ServerPicker + meta *proxy.OutboundHandlerMeta +} + +func (this *Client) Dispatch(destination v2net.Destination, payload *alloc.Buffer, ray ray.OutboundRay) error { + defer payload.Release() + defer ray.OutboundInput().Release() + defer ray.OutboundOutput().Close() + + network := destination.Network + + var server *protocol.ServerSpec + var conn internet.Connection + + err := retry.Timed(5, 100).On(func() error { + server = this.serverPicker.PickServer() + dest := server.Destination() + dest.Network = network + rawConn, err := internet.Dial(this.meta.Address, dest, this.meta.StreamSettings) + if err != nil { + return err + } + conn = rawConn + + return nil + }) + if err != nil { + log.Error("Shadowsocks|Client: Failed to find an available destination:", err) + return err + } + log.Info("Shadowsocks|Client: Tunneling request to ", destination, " via ", server.Destination()) + + return nil +} diff --git a/proxy/shadowsocks/config.go b/proxy/shadowsocks/config.go index c64317ed9..d59e2039b 100644 --- a/proxy/shadowsocks/config.go +++ b/proxy/shadowsocks/config.go @@ -1,6 +1,7 @@ package shadowsocks import ( + "bytes" "crypto/cipher" "crypto/md5" "errors" @@ -9,6 +10,18 @@ import ( "v2ray.com/core/common/protocol" ) +type ShadowsocksAccount struct { + Cipher Cipher + Key []byte +} + +func (this *ShadowsocksAccount) Equals(another protocol.Account) bool { + if account, ok := another.(*ShadowsocksAccount); ok { + return bytes.Equal(this.Key, account.Key) + } + return false +} + func (this *Account) GetCipher() (Cipher, error) { switch this.CipherType { case CipherType_AES_128_CFB: @@ -24,15 +37,15 @@ func (this *Account) GetCipher() (Cipher, error) { } } -func (this *Account) Equals(another protocol.Account) bool { - if account, ok := another.(*Account); ok { - return account.Password == this.Password - } - return false -} - func (this *Account) AsAccount() (protocol.Account, error) { - return this, nil + cipher, err := this.GetCipher() + if err != nil { + return nil, err + } + return &ShadowsocksAccount{ + Cipher: cipher, + Key: this.GetCipherKey(), + }, nil } func (this *Account) GetCipherKey() []byte { diff --git a/proxy/shadowsocks/config.pb.go b/proxy/shadowsocks/config.pb.go index 49b091ba9..402d3d23a 100644 --- a/proxy/shadowsocks/config.pb.go +++ b/proxy/shadowsocks/config.pb.go @@ -62,9 +62,34 @@ func (x CipherType) String() string { } func (CipherType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +type Account_OneTimeAuth int32 + +const ( + Account_Auto Account_OneTimeAuth = 0 + Account_Disabled Account_OneTimeAuth = 1 + Account_Enabled Account_OneTimeAuth = 2 +) + +var Account_OneTimeAuth_name = map[int32]string{ + 0: "Auto", + 1: "Disabled", + 2: "Enabled", +} +var Account_OneTimeAuth_value = map[string]int32{ + "Auto": 0, + "Disabled": 1, + "Enabled": 2, +} + +func (x Account_OneTimeAuth) String() string { + return proto.EnumName(Account_OneTimeAuth_name, int32(x)) +} +func (Account_OneTimeAuth) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} } + type Account struct { - Password string `protobuf:"bytes,1,opt,name=password" json:"password,omitempty"` - CipherType CipherType `protobuf:"varint,2,opt,name=cipher_type,json=cipherType,enum=v2ray.core.proxy.shadowsocks.CipherType" json:"cipher_type,omitempty"` + Password string `protobuf:"bytes,1,opt,name=password" json:"password,omitempty"` + CipherType CipherType `protobuf:"varint,2,opt,name=cipher_type,json=cipherType,enum=v2ray.core.proxy.shadowsocks.CipherType" json:"cipher_type,omitempty"` + Ota Account_OneTimeAuth `protobuf:"varint,3,opt,name=ota,enum=v2ray.core.proxy.shadowsocks.Account_OneTimeAuth" json:"ota,omitempty"` } func (m *Account) Reset() { *m = Account{} } @@ -110,34 +135,38 @@ func init() { proto.RegisterType((*ServerConfig)(nil), "v2ray.core.proxy.shadowsocks.ServerConfig") proto.RegisterType((*ClientConfig)(nil), "v2ray.core.proxy.shadowsocks.ClientConfig") proto.RegisterEnum("v2ray.core.proxy.shadowsocks.CipherType", CipherType_name, CipherType_value) + proto.RegisterEnum("v2ray.core.proxy.shadowsocks.Account_OneTimeAuth", Account_OneTimeAuth_name, Account_OneTimeAuth_value) } func init() { proto.RegisterFile("v2ray.com/core/proxy/shadowsocks/config.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 371 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x50, 0xdd, 0xab, 0xd3, 0x30, - 0x14, 0xb7, 0xf7, 0x5e, 0xee, 0x9d, 0x27, 0x53, 0x6b, 0x9e, 0xc6, 0x10, 0x2c, 0x7b, 0xaa, 0x17, - 0x4c, 0x67, 0xfd, 0xc0, 0x07, 0x5f, 0xda, 0xd2, 0xb1, 0x21, 0x4c, 0xe9, 0x36, 0x04, 0x11, 0x4a, - 0x97, 0x46, 0x57, 0x5c, 0x9b, 0x90, 0xb4, 0x9b, 0xfd, 0xef, 0x65, 0xc9, 0x3a, 0x87, 0x0f, 0xbb, - 0x6f, 0x39, 0x27, 0xbf, 0xcf, 0x03, 0xaf, 0x77, 0xbe, 0xcc, 0x5a, 0x42, 0x79, 0xe9, 0x51, 0x2e, - 0x99, 0x27, 0x24, 0xff, 0xd3, 0x7a, 0x6a, 0x93, 0xe5, 0x7c, 0xaf, 0x38, 0xfd, 0xad, 0x3c, 0xca, - 0xab, 0x9f, 0xc5, 0x2f, 0x22, 0x24, 0xaf, 0x39, 0x7e, 0xd1, 0xc1, 0x25, 0x23, 0x1a, 0x4a, 0xce, - 0xa0, 0xc3, 0x57, 0xff, 0x89, 0x51, 0x5e, 0x96, 0xbc, 0xf2, 0x34, 0x95, 0xf2, 0xad, 0xd7, 0x28, - 0x26, 0x8d, 0xd0, 0x70, 0xfc, 0x00, 0x54, 0x31, 0xb9, 0x63, 0x32, 0x55, 0x82, 0x51, 0xc3, 0x18, - 0x09, 0xb8, 0x0b, 0x28, 0xe5, 0x4d, 0x55, 0xe3, 0x21, 0xf4, 0x44, 0xa6, 0xd4, 0x9e, 0xcb, 0x7c, - 0x60, 0x39, 0x96, 0xfb, 0x38, 0x39, 0xcd, 0x78, 0x06, 0x88, 0x16, 0x62, 0xc3, 0x64, 0x5a, 0xb7, - 0x82, 0x0d, 0xae, 0x1c, 0xcb, 0x7d, 0xea, 0xbb, 0xe4, 0x52, 0x6e, 0x12, 0x69, 0xc2, 0xb2, 0x15, - 0x2c, 0x01, 0x7a, 0x7a, 0x8f, 0x18, 0xf4, 0x17, 0x3a, 0x46, 0xa4, 0x4f, 0x80, 0x5f, 0x02, 0x6a, - 0x72, 0x91, 0xb2, 0x2a, 0x5b, 0x6f, 0x99, 0x71, 0xee, 0x25, 0xd0, 0xe4, 0x22, 0x36, 0x1b, 0xfc, - 0x0e, 0x6e, 0x0e, 0x15, 0xb5, 0x29, 0xf2, 0x9d, 0x73, 0x53, 0xd3, 0x8f, 0x74, 0xfd, 0xc8, 0x4a, - 0x31, 0x99, 0x68, 0xf4, 0x28, 0x81, 0x7e, 0xb4, 0x2d, 0x58, 0x55, 0x1f, 0x6d, 0x42, 0xb8, 0x35, - 0xed, 0x07, 0x96, 0x73, 0xed, 0x22, 0xff, 0xfe, 0x92, 0x8e, 0x09, 0x18, 0x57, 0xb9, 0xe0, 0x45, - 0x55, 0x27, 0x47, 0xe6, 0xfd, 0x0f, 0x80, 0x7f, 0xa5, 0x30, 0x82, 0xbb, 0xd5, 0xfc, 0xf3, 0xfc, - 0xcb, 0xb7, 0xb9, 0xfd, 0x08, 0x3f, 0x03, 0x14, 0xc4, 0x8b, 0xf4, 0x8d, 0xff, 0x31, 0x8d, 0x26, - 0xa1, 0x6d, 0x75, 0x0b, 0xff, 0xfd, 0x07, 0xbd, 0xb8, 0xc2, 0x7d, 0xe8, 0x45, 0xd3, 0x20, 0x9a, - 0x06, 0xfe, 0xd8, 0xbe, 0xc6, 0xcf, 0xe1, 0x49, 0x37, 0xa5, 0xb3, 0x78, 0xb2, 0xb4, 0x6f, 0xc2, - 0x4f, 0xe0, 0x50, 0x5e, 0x5e, 0xbc, 0x69, 0x88, 0x4c, 0x9b, 0xaf, 0x87, 0xa0, 0xdf, 0xd1, 0xd9, - 0xcf, 0xfa, 0x56, 0x87, 0x7f, 0xfb, 0x37, 0x00, 0x00, 0xff, 0xff, 0xff, 0xed, 0x11, 0xa8, 0x7b, - 0x02, 0x00, 0x00, + // 431 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x91, 0x4f, 0x6f, 0xd3, 0x40, + 0x10, 0xc5, 0xeb, 0x24, 0x6a, 0xc3, 0x6c, 0x00, 0xb3, 0xa7, 0x28, 0x42, 0xc2, 0xca, 0x29, 0x54, + 0x62, 0xdd, 0x9a, 0x3f, 0xe2, 0xc0, 0xc5, 0x31, 0xa9, 0x5a, 0x21, 0xa5, 0xc8, 0x4d, 0x85, 0x84, + 0x90, 0x2c, 0x77, 0x3d, 0x10, 0x8b, 0xd8, 0xb3, 0xda, 0xb5, 0x5b, 0xf2, 0x91, 0xf9, 0x16, 0xc8, + 0xeb, 0x24, 0x44, 0x1c, 0xc2, 0xcd, 0x33, 0x7e, 0xef, 0xf9, 0xcd, 0xcf, 0xf0, 0xea, 0x3e, 0xd0, + 0xe9, 0x5a, 0x48, 0x2a, 0x7c, 0x49, 0x1a, 0x7d, 0xa5, 0xe9, 0xd7, 0xda, 0x37, 0xcb, 0x34, 0xa3, + 0x07, 0x43, 0xf2, 0xa7, 0xf1, 0x25, 0x95, 0xdf, 0xf3, 0x1f, 0x42, 0x69, 0xaa, 0x88, 0x3f, 0xdf, + 0xca, 0x35, 0x0a, 0x2b, 0x15, 0x7b, 0xd2, 0xd1, 0xcb, 0x7f, 0xc2, 0x24, 0x15, 0x05, 0x95, 0xbe, + 0xb5, 0x4a, 0x5a, 0xf9, 0xb5, 0x41, 0xdd, 0x06, 0x8d, 0xce, 0xfe, 0x23, 0x35, 0xa8, 0xef, 0x51, + 0x27, 0x46, 0xa1, 0x6c, 0x1d, 0xe3, 0xdf, 0x0e, 0x9c, 0x84, 0x52, 0x52, 0x5d, 0x56, 0x7c, 0x04, + 0x7d, 0x95, 0x1a, 0xf3, 0x40, 0x3a, 0x1b, 0x3a, 0x9e, 0x33, 0x79, 0x14, 0xef, 0x66, 0x7e, 0x05, + 0x4c, 0xe6, 0x6a, 0x89, 0x3a, 0xa9, 0xd6, 0x0a, 0x87, 0x1d, 0xcf, 0x99, 0x3c, 0x09, 0x26, 0xe2, + 0x50, 0x71, 0x11, 0x59, 0xc3, 0x62, 0xad, 0x30, 0x06, 0xb9, 0x7b, 0xe6, 0x11, 0x74, 0xa9, 0x4a, + 0x87, 0x5d, 0x1b, 0x71, 0x7e, 0x38, 0x62, 0x53, 0x4d, 0x5c, 0x97, 0xb8, 0xc8, 0x0b, 0x0c, 0xeb, + 0x6a, 0x19, 0x37, 0xee, 0x71, 0x00, 0x6c, 0x6f, 0xc7, 0xfb, 0xd0, 0x0b, 0xeb, 0x8a, 0xdc, 0x23, + 0x3e, 0x80, 0xfe, 0xc7, 0xdc, 0xa4, 0x77, 0x2b, 0xcc, 0x5c, 0x87, 0x33, 0x38, 0x99, 0x95, 0xed, + 0xd0, 0x19, 0x23, 0x0c, 0x6e, 0x2c, 0x80, 0xc8, 0xc2, 0xe7, 0x2f, 0x80, 0xd5, 0x99, 0x4a, 0xb0, + 0x15, 0xd8, 0x93, 0xfb, 0x31, 0xd4, 0x99, 0xda, 0x58, 0xf8, 0x1b, 0xe8, 0x35, 0x70, 0xed, 0xb5, + 0x2c, 0xf0, 0xf6, 0xab, 0xb6, 0x64, 0xc5, 0x96, 0xac, 0xb8, 0x35, 0xa8, 0x63, 0xab, 0x1e, 0xc7, + 0x30, 0x88, 0x56, 0x39, 0x96, 0xd5, 0xe6, 0x33, 0x53, 0x38, 0x6e, 0xb9, 0x0f, 0x1d, 0xaf, 0x3b, + 0x61, 0xc1, 0xe9, 0xa1, 0x9c, 0xb6, 0xe0, 0xac, 0xcc, 0x14, 0xe5, 0x65, 0x15, 0x6f, 0x9c, 0xa7, + 0xdf, 0x00, 0xfe, 0xd2, 0x6c, 0xae, 0xba, 0x9d, 0x7f, 0x9a, 0x5f, 0x7f, 0x99, 0xbb, 0x47, 0xfc, + 0x29, 0xb0, 0x70, 0x76, 0x93, 0x9c, 0x07, 0xef, 0x93, 0xe8, 0x62, 0xea, 0x3a, 0xdb, 0x45, 0xf0, + 0xf6, 0x9d, 0x5d, 0x74, 0x1a, 0x24, 0xd1, 0x65, 0x18, 0x5d, 0x86, 0xc1, 0x99, 0xdb, 0xe5, 0xcf, + 0xe0, 0xf1, 0x76, 0x4a, 0xae, 0x66, 0x17, 0x0b, 0xb7, 0x37, 0xfd, 0x00, 0x9e, 0xa4, 0xe2, 0xe0, + 0x9f, 0x98, 0xb2, 0xf6, 0x9a, 0xcf, 0x4d, 0xd1, 0xaf, 0x6c, 0xef, 0xcd, 0xdd, 0xb1, 0x2d, 0xff, + 0xfa, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x59, 0xf6, 0xcd, 0xed, 0xf5, 0x02, 0x00, 0x00, } diff --git a/proxy/shadowsocks/config.proto b/proxy/shadowsocks/config.proto index e25ecf2af..d2ed34be1 100644 --- a/proxy/shadowsocks/config.proto +++ b/proxy/shadowsocks/config.proto @@ -9,8 +9,14 @@ import "v2ray.com/core/common/protocol/user.proto"; import "v2ray.com/core/common/protocol/server_spec.proto"; message Account { + enum OneTimeAuth { + Auto = 0; + Disabled = 1; + Enabled = 2; + } string password = 1; CipherType cipher_type = 2; + OneTimeAuth ota = 3; } enum CipherType { diff --git a/proxy/shadowsocks/init.go b/proxy/shadowsocks/init.go new file mode 100644 index 000000000..0d5f92a8f --- /dev/null +++ b/proxy/shadowsocks/init.go @@ -0,0 +1,12 @@ +package shadowsocks + +import ( + "v2ray.com/core/common/loader" + "v2ray.com/core/proxy/registry" +) + +func init() { + // Must happen after config is initialized + + registry.MustRegisterInboundHandlerCreator(loader.GetType(new(ServerConfig)), new(ServerFactory)) +} diff --git a/proxy/shadowsocks/ota.go b/proxy/shadowsocks/ota.go index fa597537d..864510150 100644 --- a/proxy/shadowsocks/ota.go +++ b/proxy/shadowsocks/ota.go @@ -118,10 +118,11 @@ func (this *ChunkWriter) Release() { this.auth = nil } -func (this *ChunkWriter) Write(payload *alloc.Buffer) (int, error) { +func (this *ChunkWriter) Write(payload *alloc.Buffer) error { totalLength := payload.Len() - authBytes := this.auth.Authenticate(nil, payload.Bytes()) - payload.Prepend(authBytes) + payload.SliceBack(AuthSize) + this.auth.Authenticate(payload.Value[:0], payload.Value[AuthSize:]) payload.PrependUint16(uint16(totalLength)) - return this.writer.Write(payload.Bytes()) + _, err := this.writer.Write(payload.Bytes()) + return err } diff --git a/proxy/shadowsocks/ota_test.go b/proxy/shadowsocks/ota_test.go index eaf53fb1c..2b8015011 100644 --- a/proxy/shadowsocks/ota_test.go +++ b/proxy/shadowsocks/ota_test.go @@ -26,12 +26,11 @@ func TestNormalChunkReading(t *testing.T) { func TestNormalChunkWriting(t *testing.T) { assert := assert.On(t) - buffer := alloc.NewBuffer().Clear() + buffer := alloc.NewLocalBuffer(512).Clear() writer := NewChunkWriter(buffer, NewAuthenticator(ChunkKeyGenerator( []byte{21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36}))) - nBytes, err := writer.Write(alloc.NewBuffer().Clear().Append([]byte{11, 12, 13, 14, 15, 16, 17, 18})) + err := writer.Write(alloc.NewLocalBuffer(256).Clear().Append([]byte{11, 12, 13, 14, 15, 16, 17, 18})) assert.Error(err).IsNil() - assert.Int(nBytes).Equals(buffer.Len()) assert.Bytes(buffer.Value).Equals([]byte{0, 8, 39, 228, 69, 96, 133, 39, 254, 26, 201, 70, 11, 12, 13, 14, 15, 16, 17, 18}) } diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index b567c2260..d54987a5f 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -2,141 +2,343 @@ package shadowsocks import ( "bytes" + "crypto/rand" + "errors" "io" "v2ray.com/core/common/alloc" - "v2ray.com/core/common/log" + "v2ray.com/core/common/crypto" + v2io "v2ray.com/core/common/io" v2net "v2ray.com/core/common/net" - "v2ray.com/core/proxy" - "v2ray.com/core/transport" + "v2ray.com/core/common/protocol" ) const ( + Version = 1 + RequestOptionOneTimeAuth = protocol.RequestOption(101) + AddrTypeIPv4 = 1 AddrTypeIPv6 = 4 AddrTypeDomain = 3 ) -type Request struct { - Address v2net.Address - Port v2net.Port - OTA bool - UDPPayload *alloc.Buffer -} - -func (this *Request) Release() { - this.Address = nil - if this.UDPPayload != nil { - this.UDPPayload.Release() +func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHeader, v2io.Reader, error) { + rawAccount, err := user.GetTypedAccount() + if err != nil { + return nil, nil, errors.New("Shadowsocks|TCP: Failed to parse account: " + err.Error()) } -} + account := rawAccount.(*ShadowsocksAccount) -func (this *Request) DetachUDPPayload() *alloc.Buffer { - payload := this.UDPPayload - this.UDPPayload = nil - return payload -} - -func ReadRequest(reader io.Reader, auth *Authenticator, udp bool) (*Request, error) { - buffer := alloc.NewSmallBuffer() + buffer := alloc.NewLocalBuffer(256) defer buffer.Release() - _, err := io.ReadFull(reader, buffer.Value[:1]) + ivLen := account.Cipher.IVSize() + _, err = io.ReadFull(reader, buffer.Value[:ivLen]) if err != nil { - if err != io.EOF { - log.Warning("Shadowsocks: Failed to read address type: ", err) - return nil, transport.ErrCorruptedPacket - } - return nil, err + return nil, nil, errors.New("Shadowsocks|TCP: Failed to read IV: " + err.Error()) } - lenBuffer := 1 - request := new(Request) + iv := append([]byte(nil), buffer.Value[:ivLen]...) + + stream, err := account.Cipher.NewDecodingStream(account.Key, iv) + if err != nil { + return nil, nil, errors.New("Shadowsocks|TCP: Failed to initialize decoding stream: " + err.Error()) + } + reader = crypto.NewCryptionReader(stream, reader) + + authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv)) + request := &protocol.RequestHeader{ + Version: Version, + User: user, + Command: protocol.RequestCommandTCP, + } + + lenBuffer := 1 + _, err = io.ReadFull(reader, buffer.Value[:1]) + if err != nil { + return nil, nil, errors.New("Sahdowsocks|TCP: Failed to read address type: " + err.Error()) + } addrType := (buffer.Value[0] & 0x0F) if (buffer.Value[0] & 0x10) == 0x10 { - request.OTA = true + request.Option |= RequestOptionOneTimeAuth } + switch addrType { case AddrTypeIPv4: _, err := io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+4]) if err != nil { - log.Warning("Shadowsocks: Failed to read IPv4 address: ", err) - return nil, transport.ErrCorruptedPacket + return nil, nil, errors.New("Shadowsocks|TCP: Failed to read IPv4 address: " + err.Error()) } request.Address = v2net.IPAddress(buffer.Value[lenBuffer : lenBuffer+4]) lenBuffer += 4 case AddrTypeIPv6: _, err := io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+16]) if err != nil { - log.Warning("Shadowsocks: Failed to read IPv6 address: ", err) - return nil, transport.ErrCorruptedPacket + return nil, nil, errors.New("Shadowsocks|TCP: Failed to read IPv6 address: " + err.Error()) } request.Address = v2net.IPAddress(buffer.Value[lenBuffer : lenBuffer+16]) lenBuffer += 16 case AddrTypeDomain: _, err := io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+1]) if err != nil { - log.Warning("Shadowsocks: Failed to read domain lenth: ", err) - return nil, transport.ErrCorruptedPacket + return nil, nil, errors.New("Shadowsocks|TCP: Failed to read domain lenth: " + err.Error()) } domainLength := int(buffer.Value[lenBuffer]) lenBuffer++ _, err = io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+domainLength]) if err != nil { - log.Warning("Shadowsocks: Failed to read domain: ", err) - return nil, transport.ErrCorruptedPacket + return nil, nil, errors.New("Shadowsocks|TCP: Failed to read domain: " + err.Error()) } request.Address = v2net.DomainAddress(string(buffer.Value[lenBuffer : lenBuffer+domainLength])) lenBuffer += domainLength default: - log.Warning("Shadowsocks: Unknown address type: ", addrType) - return nil, transport.ErrCorruptedPacket + return nil, nil, errors.New("Shadowsocks|TCP: Unknown address type.") } _, err = io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+2]) if err != nil { - log.Warning("Shadowsocks: Failed to read port: ", err) - return nil, transport.ErrCorruptedPacket + return nil, nil, errors.New("Shadowsocks|TCP: Failed to read port: " + err.Error()) } request.Port = v2net.PortFromBytes(buffer.Value[lenBuffer : lenBuffer+2]) lenBuffer += 2 - var authBytes []byte - - if udp { - nBytes, err := reader.Read(buffer.Value[lenBuffer:]) + if request.Option.Has(RequestOptionOneTimeAuth) { + authBytes := buffer.Value[lenBuffer : lenBuffer+AuthSize] + _, err = io.ReadFull(reader, authBytes) if err != nil { - log.Warning("Shadowsocks: Failed to read UDP payload: ", err) - return nil, transport.ErrCorruptedPacket + return nil, nil, errors.New("Shadowsocks|TCP: Failed to read OTA: " + err.Error()) } - buffer.Slice(0, lenBuffer+nBytes) - if request.OTA { - authBytes = buffer.Value[lenBuffer+nBytes-AuthSize:] - request.UDPPayload = alloc.NewSmallBuffer().Clear().Append(buffer.Value[lenBuffer : lenBuffer+nBytes-AuthSize]) - lenBuffer = lenBuffer + nBytes - AuthSize - } else { - request.UDPPayload = alloc.NewSmallBuffer().Clear().Append(buffer.Value[lenBuffer:]) - } - } else { - if request.OTA { - authBytes = buffer.Value[lenBuffer : lenBuffer+AuthSize] - _, err = io.ReadFull(reader, authBytes) - if err != nil { - log.Warning("Shadowsocks: Failed to read OTA: ", err) - return nil, transport.ErrCorruptedPacket - } - } - } - if request.OTA { - actualAuth := auth.Authenticate(nil, buffer.Value[0:lenBuffer]) + actualAuth := authenticator.Authenticate(nil, buffer.Value[0:lenBuffer]) if !bytes.Equal(actualAuth, authBytes) { - log.Warning("Shadowsocks: Invalid OTA.") - return nil, proxy.ErrInvalidAuthentication + return nil, nil, errors.New("Shadowsocks|TCP: Invalid OTA") } } - return request, nil + var chunkReader v2io.Reader + if request.Option.Has(RequestOptionOneTimeAuth) { + chunkReader = NewChunkReader(reader, NewAuthenticator(ChunkKeyGenerator(iv))) + } else { + chunkReader = v2io.NewAdaptiveReader(reader) + } + + return request, chunkReader, nil +} + +func WriteTCPRequest(request *protocol.RequestHeader, writer io.Writer) (v2io.Writer, error) { + user := request.User + rawAccount, err := user.GetTypedAccount() + if err != nil { + return nil, errors.New("Shadowsocks|TCP: Failed to parse account: " + err.Error()) + } + account := rawAccount.(*ShadowsocksAccount) + + iv := make([]byte, account.Cipher.IVSize()) + rand.Read(iv) + _, err = writer.Write(iv) + if err != nil { + return nil, errors.New("Shadowsocks|TCP: Failed to write IV: " + err.Error()) + } + + stream, err := account.Cipher.NewEncodingStream(account.Key, iv) + if err != nil { + return nil, errors.New("Shadowsocks|TCP: Failed to create encoding stream: " + err.Error()) + } + + writer = crypto.NewCryptionWriter(stream, writer) + + header := alloc.NewLocalBuffer(512).Clear() + + switch request.Address.Family() { + case v2net.AddressFamilyIPv4: + header.AppendBytes(AddrTypeIPv4) + header.Append([]byte(request.Address.IP())) + case AddrTypeIPv6: + header.AppendBytes(AddrTypeIPv6) + header.Append([]byte(request.Address.IP())) + case AddrTypeDomain: + header.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain()))) + header.Append([]byte(request.Address.Domain())) + default: + return nil, errors.New("Shadowsocks|TCP: Unsupported address type. ") + } + + header.AppendUint16(uint16(request.Port)) + + if request.Option.Has(RequestOptionOneTimeAuth) { + header.Value[0] |= 0x10 + + authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv)) + header.Value = authenticator.Authenticate(header.Value, header.Value) + } + + _, err = writer.Write(header.Value) + if err != nil { + return nil, errors.New("Shadowsocks|TCP: Failed to write header: " + err.Error()) + } + + var chunkWriter v2io.Writer + if request.Option.Has(RequestOptionOneTimeAuth) { + chunkWriter = NewChunkWriter(writer, NewAuthenticator(ChunkKeyGenerator(iv))) + } else { + chunkWriter = v2io.NewAdaptiveWriter(writer) + } + + return chunkWriter, nil +} + +func ReadTCPResponse(user *protocol.User, reader io.Reader) (v2io.Reader, error) { + rawAccount, err := user.GetTypedAccount() + if err != nil { + return nil, errors.New("Shadowsocks|TCP: Failed to parse account: " + err.Error()) + } + account := rawAccount.(*ShadowsocksAccount) + + iv := make([]byte, account.Cipher.IVSize()) + _, err = io.ReadFull(reader, iv) + if err != nil { + return nil, errors.New("Shadowsocks|TCP: Failed to read IV: " + err.Error()) + } + + stream, err := account.Cipher.NewDecodingStream(account.Key, iv) + if err != nil { + return nil, errors.New("Shadowsocks|TCP: Failed to initialize decoding stream: " + err.Error()) + } + return v2io.NewAdaptiveReader(crypto.NewCryptionReader(stream, reader)), nil +} + +func WriteTCPResponse(request *protocol.RequestHeader, writer io.Writer) (v2io.Writer, error) { + user := request.User + rawAccount, err := user.GetTypedAccount() + if err != nil { + return nil, errors.New("Shadowsocks|TCP: Failed to parse account: " + err.Error()) + } + account := rawAccount.(*ShadowsocksAccount) + + iv := make([]byte, account.Cipher.IVSize()) + rand.Read(iv) + _, err = writer.Write(iv) + if err != nil { + return nil, errors.New("Shadowsocks|TCP: Failed to write IV: " + err.Error()) + } + + stream, err := account.Cipher.NewEncodingStream(account.Key, iv) + if err != nil { + return nil, errors.New("Shadowsocks|TCP: Failed to create encoding stream: " + err.Error()) + } + + return v2io.NewAdaptiveWriter(crypto.NewCryptionWriter(stream, writer)), nil +} + +func EncodeUDPPacket(request *protocol.RequestHeader, payload *alloc.Buffer) (*alloc.Buffer, error) { + user := request.User + rawAccount, err := user.GetTypedAccount() + if err != nil { + return nil, errors.New("Shadowsocks|UDP: Failed to parse account: " + err.Error()) + } + account := rawAccount.(*ShadowsocksAccount) + + buffer := alloc.NewLocalBuffer(2048) + ivLen := account.Cipher.IVSize() + buffer.Slice(0, ivLen) + rand.Read(buffer.Value) + iv := buffer.Value + + switch request.Address.Family() { + case v2net.AddressFamilyIPv4: + buffer.AppendBytes(AddrTypeIPv4) + buffer.Append([]byte(request.Address.IP())) + case AddrTypeIPv6: + buffer.AppendBytes(AddrTypeIPv6) + buffer.Append([]byte(request.Address.IP())) + case AddrTypeDomain: + buffer.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain()))) + buffer.Append([]byte(request.Address.Domain())) + default: + return nil, errors.New("Shadowsocks|UDP: Unsupported address type. ") + } + + buffer.AppendUint16(uint16(request.Port)) + buffer.Append(payload.Value) + + if request.Option.Has(RequestOptionOneTimeAuth) { + authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv)) + buffer.Value[ivLen] |= 0x0F + + buffer.Value = authenticator.Authenticate(buffer.Value, buffer.Value[ivLen:]) + } + + stream, err := account.Cipher.NewEncodingStream(account.Key, iv) + if err != nil { + return nil, errors.New("Shadowsocks|TCP: Failed to create encoding stream: " + err.Error()) + } + + stream.XORKeyStream(buffer.Value[ivLen:], buffer.Value[ivLen:]) + return buffer, nil +} + +func DecodeUDPPacket(user *protocol.User, payload *alloc.Buffer) (*protocol.RequestHeader, *alloc.Buffer, error) { + rawAccount, err := user.GetTypedAccount() + if err != nil { + return nil, nil, errors.New("Shadowsocks|UDP: Failed to parse account: " + err.Error()) + } + account := rawAccount.(*ShadowsocksAccount) + + ivLen := account.Cipher.IVSize() + iv := payload.Value[:ivLen] + payload.SliceFrom(ivLen) + + stream, err := account.Cipher.NewDecodingStream(account.Key, iv) + if err != nil { + return nil, nil, errors.New("Shadowsocks|UDP: Failed to initialize decoding stream: " + err.Error()) + } + stream.XORKeyStream(payload.Value, payload.Value) + + authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv)) + request := &protocol.RequestHeader{ + Version: Version, + User: user, + Command: protocol.RequestCommandUDP, + } + + addrType := (payload.Value[0] & 0x0F) + if (payload.Value[0] & 0x10) == 0x10 { + request.Option |= RequestOptionOneTimeAuth + } + + if request.Option.Has(RequestOptionOneTimeAuth) { + payloadLen := payload.Len() - AuthSize + authBytes := payload.Value[payloadLen:] + + actualAuth := authenticator.Authenticate(nil, payload.Value[0:payloadLen]) + if !bytes.Equal(actualAuth, authBytes) { + return nil, nil, errors.New("Shadowsocks|UDP: Invalid OTA.") + } + + payload.Slice(0, payloadLen) + } + + payload.SliceFrom(1) + + switch addrType { + case AddrTypeIPv4: + request.Address = v2net.IPAddress(payload.Value[:4]) + payload.SliceFrom(4) + case AddrTypeIPv6: + request.Address = v2net.IPAddress(payload.Value[:16]) + payload.SliceFrom(16) + case AddrTypeDomain: + domainLength := int(payload.Value[0]) + request.Address = v2net.DomainAddress(string(payload.Value[1 : 1+domainLength])) + payload.SliceFrom(1 + domainLength) + default: + return nil, nil, errors.New("Shadowsocks|UDP: Unknown address type: " + err.Error()) + } + + request.Port = v2net.PortFromBytes(payload.Value[:2]) + payload.SliceFrom(2) + + return request, payload, nil } diff --git a/proxy/shadowsocks/protocol_test.go b/proxy/shadowsocks/protocol_test.go index 4a6b71a66..3ab3a0bcf 100644 --- a/proxy/shadowsocks/protocol_test.go +++ b/proxy/shadowsocks/protocol_test.go @@ -1,136 +1,77 @@ package shadowsocks_test import ( - "io" "testing" "v2ray.com/core/common/alloc" + "v2ray.com/core/common/loader" v2net "v2ray.com/core/common/net" - "v2ray.com/core/proxy" + "v2ray.com/core/common/protocol" . "v2ray.com/core/proxy/shadowsocks" "v2ray.com/core/testing/assert" - "v2ray.com/core/transport" ) -func TestNormalRequestParsing(t *testing.T) { +func TestUDPEncoding(t *testing.T) { assert := assert.On(t) - buffer := alloc.NewLocalBuffer(2048).Clear() - buffer.AppendBytes(1, 127, 0, 0, 1, 0, 80) + request := &protocol.RequestHeader{ + Version: Version, + Command: protocol.RequestCommandUDP, + Address: v2net.LocalHostIP, + Port: 1234, + User: &protocol.User{ + Email: "love@v2ray.com", + Account: loader.NewTypedSettings(&Account{ + Password: "shadowsocks-password", + CipherType: CipherType_AES_128_CFB, + Ota: Account_Disabled, + }), + }, + } - request, err := ReadRequest(buffer, nil, false) + data := alloc.NewLocalBuffer(256).Clear().AppendString("test string") + encodedData, err := EncodeUDPPacket(request, data) assert.Error(err).IsNil() - assert.Address(request.Address).Equals(v2net.LocalHostIP) - assert.Port(request.Port).Equals(v2net.Port(80)) - assert.Bool(request.OTA).IsFalse() -} -func TestEmptyPayload(t *testing.T) { - assert := assert.On(t) - - buffer := alloc.NewLocalBuffer(2048).Clear() - _, err := ReadRequest(buffer, nil, false) - assert.Error(err).Equals(io.EOF) -} - -func TestSingleBytePayload(t *testing.T) { - assert := assert.On(t) - - buffer := alloc.NewLocalBuffer(2048).Clear().AppendBytes(1) - _, err := ReadRequest(buffer, nil, false) - assert.Error(err).Equals(transport.ErrCorruptedPacket) -} - -func TestWrongAddressType(t *testing.T) { - assert := assert.On(t) - - buffer := alloc.NewLocalBuffer(2048).Clear().AppendBytes(5) - _, err := ReadRequest(buffer, nil, false) - assert.Error(err).Equals(transport.ErrCorruptedPacket) -} - -func TestInsufficientAddressRequest(t *testing.T) { - assert := assert.On(t) - - buffer := alloc.NewLocalBuffer(2048).Clear().AppendBytes(1, 1) - _, err := ReadRequest(buffer, nil, false) - assert.Error(err).Equals(transport.ErrCorruptedPacket) - - buffer = alloc.NewLocalBuffer(2048).Clear().AppendBytes(4, 1) - _, err = ReadRequest(buffer, nil, false) - assert.Error(err).Equals(transport.ErrCorruptedPacket) - - buffer = alloc.NewLocalBuffer(2048).Clear().AppendBytes(3, 255, 1) - _, err = ReadRequest(buffer, nil, false) - assert.Error(err).Equals(transport.ErrCorruptedPacket) -} - -func TestInsufficientPortRequest(t *testing.T) { - assert := assert.On(t) - - buffer := alloc.NewLocalBuffer(2048).Clear().AppendBytes(1, 1, 2, 3, 4, 5) - _, err := ReadRequest(buffer, nil, false) - assert.Error(err).Equals(transport.ErrCorruptedPacket) -} - -func TestOTARequest(t *testing.T) { - assert := assert.On(t) - - buffer := alloc.NewLocalBuffer(2048).Clear() - buffer.AppendBytes(0x13, 13, 119, 119, 119, 46, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 0, 239, 115, 52, 212, 178, 172, 26, 6, 168, 0) - - auth := NewAuthenticator(HeaderKeyGenerator( - []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5}, - []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5})) - request, err := ReadRequest(buffer, auth, false) + decodedRequest, decodedData, err := DecodeUDPPacket(request.User, encodedData) assert.Error(err).IsNil() - assert.Address(request.Address).Equals(v2net.DomainAddress("www.v2ray.com")) - assert.Bool(request.OTA).IsTrue() + assert.Bytes(decodedData.Value).Equals(data.Value) + assert.Address(decodedRequest.Address).Equals(request.Address) + assert.Port(decodedRequest.Port).Equals(request.Port) } -func TestInvalidOTARequest(t *testing.T) { +func TestTCPRequest(t *testing.T) { assert := assert.On(t) - buffer := alloc.NewLocalBuffer(2048).Clear() - buffer.AppendBytes(0x13, 13, 119, 119, 119, 46, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 0, 239, 115, 52, 212, 178, 172, 26, 6, 168, 1) + request := &protocol.RequestHeader{ + Version: Version, + Command: protocol.RequestCommandTCP, + Address: v2net.LocalHostIP, + Option: RequestOptionOneTimeAuth, + Port: 1234, + User: &protocol.User{ + Email: "love@v2ray.com", + Account: loader.NewTypedSettings(&Account{ + Password: "tcp-password", + CipherType: CipherType_CHACHA20, + }), + }, + } - auth := NewAuthenticator(HeaderKeyGenerator( - []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5}, - []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5})) - _, err := ReadRequest(buffer, auth, false) - assert.Error(err).Equals(proxy.ErrInvalidAuthentication) -} + data := alloc.NewLocalBuffer(256).Clear().AppendString("test string") + cache := alloc.NewLargeBuffer().Clear() -func TestUDPRequestParsing(t *testing.T) { - assert := assert.On(t) - - buffer := alloc.NewLocalBuffer(2048).Clear() - buffer.AppendBytes(1, 127, 0, 0, 1, 0, 80, 1, 2, 3, 4, 5, 6) - - request, err := ReadRequest(buffer, nil, true) + writer, err := WriteTCPRequest(request, cache) assert.Error(err).IsNil() - assert.Address(request.Address).Equals(v2net.LocalHostIP) - assert.Port(request.Port).Equals(v2net.Port(80)) - assert.Bool(request.OTA).IsFalse() - assert.Bytes(request.UDPPayload.Value).Equals([]byte{1, 2, 3, 4, 5, 6}) -} -func TestUDPRequestWithOTA(t *testing.T) { - assert := assert.On(t) + writer.Write(data) - buffer := alloc.NewLocalBuffer(2048).Clear() - buffer.AppendBytes( - 0x13, 13, 119, 119, 119, 46, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, - 58, 32, 223, 30, 57, 199, 50, 139, 143, 101) - - auth := NewAuthenticator(HeaderKeyGenerator( - []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5}, - []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5})) - request, err := ReadRequest(buffer, auth, true) + decodedRequest, reader, err := ReadTCPSession(request.User, cache) assert.Error(err).IsNil() - assert.Address(request.Address).Equals(v2net.DomainAddress("www.v2ray.com")) - assert.Bool(request.OTA).IsTrue() - assert.Bytes(request.UDPPayload.Value).Equals([]byte{ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0}) + assert.Address(decodedRequest.Address).Equals(request.Address) + assert.Port(decodedRequest.Port).Equals(request.Port) + + decodedData, err := reader.Read() + assert.Error(err).IsNil() + assert.Bytes(decodedData.Value).Equals([]byte("test string")) } diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 46e637ddf..97196f639 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -2,22 +2,17 @@ package shadowsocks import ( - "crypto/rand" - "io" "sync" "v2ray.com/core/app" "v2ray.com/core/app/dispatcher" "v2ray.com/core/common" "v2ray.com/core/common/alloc" - "v2ray.com/core/common/crypto" v2io "v2ray.com/core/common/io" - "v2ray.com/core/common/loader" "v2ray.com/core/common/log" v2net "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/proxy" - "v2ray.com/core/proxy/registry" "v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet/udp" ) @@ -25,8 +20,7 @@ import ( type Server struct { packetDispatcher dispatcher.PacketDispatcher config *ServerConfig - cipher Cipher - cipherKey []byte + user *protocol.User meta *proxy.InboundHandlerMeta accepting bool tcpHub *internet.TCPHub @@ -38,23 +32,11 @@ func NewServer(config *ServerConfig, space app.Space, meta *proxy.InboundHandler if config.GetUser() == nil { return nil, protocol.ErrUserMissing } - rawAccount, err := config.GetUser().GetTypedAccount() - if err != nil { - return nil, err - } - account, ok := rawAccount.(*Account) - if !ok { - return nil, protocol.ErrUnknownAccountType - } - cipher, err := account.GetCipher() - if err != nil { - return nil, err - } + s := &Server{ - config: config, - meta: meta, - cipher: cipher, - cipherKey: account.GetCipherKey(), + config: config, + meta: meta, + user: config.GetUser(), } space.InitializeApplication(func() error { @@ -84,7 +66,6 @@ func (this *Server) Close() { this.udpHub.Close() this.udpHub = nil } - } func (this *Server) Start() error { @@ -115,73 +96,30 @@ func (this *Server) Start() error { } func (this *Server) handlerUDPPayload(payload *alloc.Buffer, session *proxy.SessionInfo) { - defer payload.Release() - source := session.Source - ivLen := this.cipher.IVSize() - iv := payload.Value[:ivLen] - payload.SliceFrom(ivLen) - - stream, err := this.cipher.NewDecodingStream(this.cipherKey, iv) + request, data, err := DecodeUDPPacket(this.user, payload) if err != nil { - log.Error("Shadowsocks: Failed to create decoding stream: ", err) + log.Info("Shadowsocks|Server: Skipping invalid UDP packet from: ", source, ": ", err) + log.Access(source, "", log.AccessRejected, err) + payload.Release() return } - reader := crypto.NewCryptionReader(stream, payload) - - request, err := ReadRequest(reader, NewAuthenticator(HeaderKeyGenerator(this.cipherKey, iv)), true) - if err != nil { - if err != io.EOF { - log.Access(source, "", log.AccessRejected, err) - log.Warning("Shadowsocks: Invalid request from ", source, ": ", err) - } - return - } - //defer request.Release() - - dest := v2net.UDPDestination(request.Address, request.Port) + dest := request.Destination() log.Access(source, dest, log.AccessAccepted, "") - log.Info("Shadowsocks: Tunnelling request to ", dest) + log.Info("Shadowsocks|Server: Tunnelling request to ", dest) - this.udpServer.Dispatch(&proxy.SessionInfo{Source: source, Destination: dest}, request.DetachUDPPayload(), func(destination v2net.Destination, payload *alloc.Buffer) { + this.udpServer.Dispatch(&proxy.SessionInfo{Source: source, Destination: dest}, data, func(destination v2net.Destination, payload *alloc.Buffer) { defer payload.Release() - response := alloc.NewBuffer().Slice(0, ivLen) - defer response.Release() - - rand.Read(response.Value) - respIv := response.Value - - stream, err := this.cipher.NewEncodingStream(this.cipherKey, respIv) + data, err := EncodeUDPPacket(request, payload) if err != nil { - log.Error("Shadowsocks: Failed to create encoding stream: ", err) + log.Warning("Shadowsocks|Server: Failed to encode UDP packet: ", err) return } + defer data.Release() - writer := crypto.NewCryptionWriter(stream, response) - - switch request.Address.Family() { - case v2net.AddressFamilyIPv4: - writer.Write([]byte{AddrTypeIPv4}) - writer.Write(request.Address.IP()) - case v2net.AddressFamilyIPv6: - writer.Write([]byte{AddrTypeIPv6}) - writer.Write(request.Address.IP()) - case v2net.AddressFamilyDomain: - writer.Write([]byte{AddrTypeDomain, byte(len(request.Address.Domain()))}) - writer.Write([]byte(request.Address.Domain())) - } - - writer.Write(request.Port.Bytes(nil)) - writer.Write(payload.Value) - - if request.OTA { - respAuth := NewAuthenticator(HeaderKeyGenerator(this.cipherKey, respIv)) - respAuth.Authenticate(response.Value, response.Value[ivLen:]) - } - - this.udpHub.WriteTo(response.Value, source) + this.udpHub.WriteTo(data.Value, source) }) } @@ -197,41 +135,22 @@ func (this *Server) handleConnection(conn internet.Connection) { bufferedReader := v2io.NewBufferedReader(timedReader) defer bufferedReader.Release() - ivLen := this.cipher.IVSize() - _, err := io.ReadFull(bufferedReader, buffer.Value[:ivLen]) - if err != nil { - if err != io.EOF { - log.Access(conn.RemoteAddr(), "", log.AccessRejected, err) - log.Warning("Shadowsocks: Failed to read IV: ", err) - } - return - } - - iv := buffer.Value[:ivLen] - - stream, err := this.cipher.NewDecodingStream(this.cipherKey, iv) - if err != nil { - log.Error("Shadowsocks: Failed to create decoding stream: ", err) - return - } - - reader := crypto.NewCryptionReader(stream, bufferedReader) - - request, err := ReadRequest(reader, NewAuthenticator(HeaderKeyGenerator(this.cipherKey, iv)), false) + request, bodyReader, err := ReadTCPSession(this.user, bufferedReader) if err != nil { log.Access(conn.RemoteAddr(), "", log.AccessRejected, err) - log.Warning("Shadowsocks: Invalid request from ", conn.RemoteAddr(), ": ", err) + log.Info("Shadowsocks|Server: Failed to create request from: ", conn.RemoteAddr(), ": ", err) return } - defer request.Release() + defer bodyReader.Release() + bufferedReader.SetCached(false) - userSettings := this.config.GetUser().GetSettings() + userSettings := this.user.GetSettings() timedReader.SetTimeOut(userSettings.PayloadReadTimeout) - dest := v2net.TCPDestination(request.Address, request.Port) + dest := request.Destination() log.Access(conn.RemoteAddr(), dest, log.AccessAccepted, "") - log.Info("Shadowsocks: Tunnelling request to ", dest) + log.Info("Shadowsocks|Server: Tunnelling request to ", dest) ray := this.packetDispatcher.DispatchToOutbound(this.meta, &proxy.SessionInfo{ Source: v2net.DestinationFromAddr(conn.RemoteAddr()), @@ -242,41 +161,28 @@ func (this *Server) handleConnection(conn internet.Connection) { var writeFinish sync.Mutex writeFinish.Lock() go func() { - if payload, err := ray.InboundOutput().Read(); err == nil { - payload.SliceBack(ivLen) - rand.Read(payload.Value[:ivLen]) + defer writeFinish.Unlock() - stream, err := this.cipher.NewEncodingStream(this.cipherKey, payload.Value[:ivLen]) - if err != nil { - log.Error("Shadowsocks: Failed to create encoding stream: ", err) - return - } - stream.XORKeyStream(payload.Value[ivLen:], payload.Value[ivLen:]) + bufferedWriter := v2io.NewBufferedWriter(conn) + defer bufferedWriter.Release() - conn.Write(payload.Value) - payload.Release() - - writer := crypto.NewCryptionWriter(stream, conn) - v2writer := v2io.NewAdaptiveWriter(writer) - - v2io.Pipe(ray.InboundOutput(), v2writer) - writer.Release() - v2writer.Release() + responseWriter, err := WriteTCPResponse(request, bufferedWriter) + if err != nil { + log.Warning("Shadowsocks|Server: Failed to write response: ", err) + return + } + defer responseWriter.Release() + + if payload, err := ray.InboundOutput().Read(); err == nil { + responseWriter.Write(payload) + bufferedWriter.SetCached(false) + + v2io.Pipe(ray.InboundOutput(), responseWriter) } - writeFinish.Unlock() }() - var payloadReader v2io.Reader - if request.OTA { - payloadAuth := NewAuthenticator(ChunkKeyGenerator(iv)) - payloadReader = NewChunkReader(reader, payloadAuth) - } else { - payloadReader = v2io.NewAdaptiveReader(reader) - } - - v2io.Pipe(payloadReader, ray.InboundInput()) + v2io.Pipe(bodyReader, ray.InboundInput()) ray.InboundInput().Close() - payloadReader.Release() writeFinish.Lock() } @@ -295,7 +201,3 @@ func (this *ServerFactory) Create(space app.Space, rawConfig interface{}, meta * } return NewServer(rawConfig.(*ServerConfig), space, meta) } - -func init() { - registry.MustRegisterInboundHandlerCreator(loader.GetType(new(ServerConfig)), new(ServerFactory)) -}