Ver Fonte

refine shadowsocks server

Darien Raymond há 9 anos atrás
pai
commit
be4f3d0772

+ 48 - 0
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
+}

+ 21 - 8
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 {

+ 56 - 27
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,
 }

+ 6 - 0
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 {

+ 12 - 0
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))
+}

+ 5 - 4
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
 }

+ 2 - 3
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})
 }

+ 272 - 70
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())
 	}
-}
-
-func (this *Request) DetachUDPPayload() *alloc.Buffer {
-	payload := this.UDPPayload
-	this.UDPPayload = nil
-	return payload
-}
+	account := rawAccount.(*ShadowsocksAccount)
 
-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:])
+
+		actualAuth := authenticator.Authenticate(nil, buffer.Value[0:lenBuffer])
+		if !bytes.Equal(actualAuth, authBytes) {
+			return nil, nil, errors.New("Shadowsocks|TCP: Invalid OTA")
 		}
+	}
+
+	var chunkReader v2io.Reader
+	if request.Option.Has(RequestOptionOneTimeAuth) {
+		chunkReader = NewChunkReader(reader, NewAuthenticator(ChunkKeyGenerator(iv)))
 	} 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
-			}
-		}
+		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)
 
-	if request.OTA {
-		actualAuth := auth.Authenticate(nil, buffer.Value[0:lenBuffer])
+	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) {
-			log.Warning("Shadowsocks: Invalid OTA.")
-			return nil, proxy.ErrInvalidAuthentication
+			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())
 	}
 
-	return request, nil
+	request.Port = v2net.PortFromBytes(payload.Value[:2])
+	payload.SliceFrom(2)
+
+	return request, payload, nil
 }

+ 51 - 110
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, err := ReadRequest(buffer, nil, false)
+	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,
+			}),
+		},
+	}
+
+	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)
-
-	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)
-}
-
-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)
+	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,
+			}),
+		},
+	}
+
+	data := alloc.NewLocalBuffer(256).Clear().AppendString("test string")
+	cache := alloc.NewLargeBuffer().Clear()
+
+	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)
+	decodedRequest, reader, err := ReadTCPSession(request.User, cache)
+	assert.Error(err).IsNil()
+	assert.Address(decodedRequest.Address).Equals(request.Address)
+	assert.Port(decodedRequest.Port).Equals(request.Port)
 
-	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)
+	decodedData, err := reader.Read()
 	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.Bytes(decodedData.Value).Equals([]byte("test string"))
 }

+ 37 - 135
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)
-	if err != nil {
-		log.Error("Shadowsocks: Failed to create decoding stream: ", err)
-		return
-	}
-
-	reader := crypto.NewCryptionReader(stream, payload)
-
-	request, err := ReadRequest(reader, NewAuthenticator(HeaderKeyGenerator(this.cipherKey, iv)), true)
+	request, data, err := DecodeUDPPacket(this.user, payload)
 	if err != nil {
-		if err != io.EOF {
-			log.Access(source, "", log.AccessRejected, err)
-			log.Warning("Shadowsocks: Invalid request from ", source, ": ", err)
-		}
+		log.Info("Shadowsocks|Server: Skipping invalid UDP packet from: ", source, ": ", err)
+		log.Access(source, "", log.AccessRejected, err)
+		payload.Release()
 		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()
+		responseWriter, err := WriteTCPResponse(request, bufferedWriter)
+		if err != nil {
+			log.Warning("Shadowsocks|Server: Failed to write response: ", err)
+			return
+		}
+		defer responseWriter.Release()
 
-			writer := crypto.NewCryptionWriter(stream, conn)
-			v2writer := v2io.NewAdaptiveWriter(writer)
+		if payload, err := ray.InboundOutput().Read(); err == nil {
+			responseWriter.Write(payload)
+			bufferedWriter.SetCached(false)
 
-			v2io.Pipe(ray.InboundOutput(), v2writer)
-			writer.Release()
-			v2writer.Release()
+			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))
-}