From 7c92aca3313d513e45c19cd8955f93ebb591b7d2 Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Tue, 27 Oct 2020 22:32:04 +0800 Subject: [PATCH] ssr: a little modification to use buffer pool --- go.mod | 7 +- go.sum | 8 +- proxy/ssr/internal/cipher.go | 342 +++++++++++++++++++++++++++++++++++ proxy/ssr/internal/client.go | 242 +++++++++++++++++++++++++ proxy/ssr/ssr.go | 7 +- 5 files changed, 596 insertions(+), 10 deletions(-) create mode 100644 proxy/ssr/internal/cipher.go create mode 100644 proxy/ssr/internal/client.go diff --git a/go.mod b/go.mod index c5b218f..71ef03e 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,9 @@ go 1.15 require ( github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da + github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d + github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb + github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152 github.com/insomniacslk/dhcp v0.0.0-20200922210017-67c425063dca github.com/mzz2017/shadowsocksR v1.0.0 github.com/nadoo/conflag v0.2.3 @@ -11,8 +14,8 @@ require ( github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/xtaci/kcp-go/v5 v5.6.1 golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 - golang.org/x/net v0.0.0-20201026091529-146b70c837a4 // indirect - golang.org/x/sys v0.0.0-20201026173827-119d4633e4d1 // indirect + golang.org/x/net v0.0.0-20201027133719-8eef5233e2a1 // indirect + golang.org/x/sys v0.0.0-20201027140754-0fcbb8f4928c // indirect golang.org/x/tools v0.0.0-20201026223136-e84cfc6dd5ca // indirect gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect ) diff --git a/go.sum b/go.sum index 469c3d3..4dad7fd 100644 --- a/go.sum +++ b/go.sum @@ -129,8 +129,8 @@ golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201026091529-146b70c837a4 h1:awiuzyrRjJDb+OXi9ceHO3SDxVoN3JER57mhtqkdQBs= -golang.org/x/net v0.0.0-20201026091529-146b70c837a4/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201027133719-8eef5233e2a1 h1:IEhJ99VWSYpHIxjlbu3DQyHegGPnQYAv0IaCX9KHyG0= +golang.org/x/net v0.0.0-20201027133719-8eef5233e2a1/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -154,8 +154,8 @@ golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201026173827-119d4633e4d1 h1:/DtoiOYKoQCcIFXQjz07RnWNPRCbqmSXSpgEzhC9ZHM= -golang.org/x/sys v0.0.0-20201026173827-119d4633e4d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201027140754-0fcbb8f4928c h1:2+jF2APAgFgXJnYOQGDGGiRvvEo6OhqZGQf46n9xgEw= +golang.org/x/sys v0.0.0-20201027140754-0fcbb8f4928c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/proxy/ssr/internal/cipher.go b/proxy/ssr/internal/cipher.go new file mode 100644 index 0000000..961214c --- /dev/null +++ b/proxy/ssr/internal/cipher.go @@ -0,0 +1,342 @@ +package internal + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/md5" + "crypto/rc4" + "encoding/binary" + "errors" + "math/rand" + + "github.com/aead/chacha20" + "github.com/dgryski/go-camellia" + "github.com/dgryski/go-idea" + "github.com/dgryski/go-rc2" + "github.com/mzz2017/shadowsocksR/tools" + "golang.org/x/crypto/blowfish" + "golang.org/x/crypto/cast5" + "golang.org/x/crypto/salsa20/salsa" + + "github.com/nadoo/glider/pool" +) + +var errEmptyPassword = errors.New("empty key") + +type DecOrEnc int + +const ( + Decrypt DecOrEnc = iota + Encrypt +) + +func newCTRStream(block cipher.Block, err error, key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + if err != nil { + return nil, err + } + return cipher.NewCTR(block, iv), nil +} + +func newAESCTRStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + block, err := aes.NewCipher(key) + return newCTRStream(block, err, key, iv, doe) +} + +func newOFBStream(block cipher.Block, err error, key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + if err != nil { + return nil, err + } + return cipher.NewCTR(block, iv), nil +} + +func newAESOFBStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + block, err := aes.NewCipher(key) + return newOFBStream(block, err, key, iv, doe) +} + +func newCFBStream(block cipher.Block, err error, key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + if err != nil { + return nil, err + } + if doe == Encrypt { + return cipher.NewCFBEncrypter(block, iv), nil + } else { + return cipher.NewCFBDecrypter(block, iv), nil + } +} + +func newAESCFBStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + block, err := aes.NewCipher(key) + return newCFBStream(block, err, key, iv, doe) +} + +func newDESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + block, err := des.NewCipher(key) + return newCFBStream(block, err, key, iv, doe) +} + +func newBlowFishStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + block, err := blowfish.NewCipher(key) + return newCFBStream(block, err, key, iv, doe) +} + +func newCast5Stream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + block, err := cast5.NewCipher(key) + return newCFBStream(block, err, key, iv, doe) +} + +func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { + h := md5.New() + h.Write(key) + h.Write(iv) + rc4key := h.Sum(nil) + + return rc4.NewCipher(rc4key) +} + +func newChaCha20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { + return chacha20.NewCipher(iv, key) +} + +func newChacha20IETFStream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { + return chacha20.NewCipher(iv, key) +} + +type salsaStreamCipher struct { + nonce [8]byte + key [32]byte + counter int +} + +func (c *salsaStreamCipher) XORKeyStream(dst, src []byte) { + var buf []byte + padLen := c.counter % 64 + dataSize := len(src) + padLen + if cap(dst) >= dataSize { + buf = dst[:dataSize] + // nadoo: comment out codes here to use pool buffer + // modify start --> + // } else if leakybuf.GlobalLeakyBufSize >= dataSize { + // buf = leakybuf.GlobalLeakyBuf.Get() + // defer leakybuf.GlobalLeakyBuf.Put(buf) + // buf = buf[:dataSize] + // } else { + // buf = make([]byte, dataSize) + // } + } else { + buf = pool.GetBuffer(dataSize) + defer pool.PutBuffer(buf) + } + // --> modify end + + var subNonce [16]byte + copy(subNonce[:], c.nonce[:]) + binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter/64)) + + // It's difficult to avoid data copy here. src or dst maybe slice from + // Conn.Read/Write, which can't have padding. + copy(buf[padLen:], src[:]) + salsa.XORKeyStream(buf, buf, &subNonce, &c.key) + copy(dst, buf[padLen:]) + + c.counter += len(src) +} + +func newSalsa20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { + var c salsaStreamCipher + copy(c.nonce[:], iv[:8]) + copy(c.key[:], key[:32]) + return &c, nil +} + +func newCamelliaStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + block, err := camellia.New(key) + return newCFBStream(block, err, key, iv, doe) +} + +func newIdeaStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + block, err := idea.NewCipher(key) + return newCFBStream(block, err, key, iv, doe) +} + +func newRC2Stream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + block, err := rc2.New(key, 16) + return newCFBStream(block, err, key, iv, doe) +} + +func newRC4Stream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + return rc4.NewCipher(key) +} + +func newSeedStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + // TODO: SEED block cipher implementation is required + block, err := rc2.New(key, 16) + return newCFBStream(block, err, key, iv, doe) +} + +type NoneStream struct { + cipher.Stream +} + +func (*NoneStream) XORKeyStream(dst, src []byte) { + copy(dst, src) +} + +func newNoneStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + return new(NoneStream), nil +} + +type cipherInfo struct { + keyLen int + ivLen int + newStream func(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) +} + +var streamCipherMethod = map[string]*cipherInfo{ + "aes-128-cfb": {16, 16, newAESCFBStream}, + "aes-192-cfb": {24, 16, newAESCFBStream}, + "aes-256-cfb": {32, 16, newAESCFBStream}, + "aes-128-ctr": {16, 16, newAESCTRStream}, + "aes-192-ctr": {24, 16, newAESCTRStream}, + "aes-256-ctr": {32, 16, newAESCTRStream}, + "aes-128-ofb": {16, 16, newAESOFBStream}, + "aes-192-ofb": {24, 16, newAESOFBStream}, + "aes-256-ofb": {32, 16, newAESOFBStream}, + "des-cfb": {8, 8, newDESStream}, + "bf-cfb": {16, 8, newBlowFishStream}, + "cast5-cfb": {16, 8, newCast5Stream}, + "rc4-md5": {16, 16, newRC4MD5Stream}, + "rc4-md5-6": {16, 6, newRC4MD5Stream}, + "chacha20": {32, 8, newChaCha20Stream}, + "chacha20-ietf": {32, 12, newChacha20IETFStream}, + "salsa20": {32, 8, newSalsa20Stream}, + "camellia-128-cfb": {16, 16, newCamelliaStream}, + "camellia-192-cfb": {24, 16, newCamelliaStream}, + "camellia-256-cfb": {32, 16, newCamelliaStream}, + "idea-cfb": {16, 8, newIdeaStream}, + "rc2-cfb": {16, 8, newRC2Stream}, + "seed-cfb": {16, 8, newSeedStream}, + "rc4": {16, 0, newRC4Stream}, + "none": {16, 0, newNoneStream}, +} + +func CheckCipherMethod(method string) error { + if method == "" { + method = "rc4-md5" + } + _, ok := streamCipherMethod[method] + if !ok { + return errors.New("Unsupported encryption method: " + method) + } + return nil +} + +type StreamCipher struct { + enc cipher.Stream + dec cipher.Stream + key []byte + info *cipherInfo + iv []byte +} + +// NewStreamCipher creates a cipher that can be used in Dial() etc. +// Use cipher.Copy() to create a new cipher with the same method and password +// to avoid the cost of repeated cipher initialization. +func NewStreamCipher(method, password string) (c *StreamCipher, err error) { + if password == "" { + return nil, errEmptyPassword + } + if method == "" { + method = "rc4-md5" + } + mi, ok := streamCipherMethod[method] + if !ok { + return nil, errors.New("Unsupported encryption method: " + method) + } + + key := tools.EVPBytesToKey(password, mi.keyLen) + + c = &StreamCipher{key: key, info: mi} + + return c, nil +} + +func (c *StreamCipher) EncryptInited() bool { + return c.enc != nil +} + +func (c *StreamCipher) DecryptInited() bool { + return c.dec != nil +} + +// Initializes the block cipher with CFB mode, returns IV. +func (c *StreamCipher) InitEncrypt() (iv []byte, err error) { + if c.iv == nil { + iv = make([]byte, c.info.ivLen) + rand.Read(iv) + c.iv = iv + } else { + iv = c.iv + } + c.enc, err = c.info.newStream(c.key, iv, Encrypt) + return +} + +func (c *StreamCipher) InitDecrypt(iv []byte) (err error) { + c.dec, err = c.info.newStream(c.key, iv, Decrypt) + return +} + +func (c *StreamCipher) Encrypt(dst, src []byte) { + c.enc.XORKeyStream(dst, src) +} + +func (c *StreamCipher) Decrypt(dst, src []byte) { + c.dec.XORKeyStream(dst, src) +} + +// Copy creates a new cipher at it's initial state. +func (c *StreamCipher) Copy() *StreamCipher { + // This optimization maybe not necessary. But without this function, we + // need to maintain a table cache for newTableCipher and use lock to + // protect concurrent access to that cache. + + // AES and DES ciphers does not return specific types, so it's difficult + // to create copy. But their initialization time is less than 4000ns on my + // 2.26 GHz Intel Core 2 Duo processor. So no need to worry. + + // Currently, blow-fish and cast5 initialization cost is an order of + // magnitude slower than other ciphers. (I'm not sure whether this is + // because the current implementation is not highly optimized, or this is + // the nature of the algorithm.) + + nc := *c + nc.enc = nil + nc.dec = nil + return &nc +} + +func (c *StreamCipher) Key() []byte { + return c.key +} + +func (c *StreamCipher) IV() []byte { + return c.iv +} + +func (c *StreamCipher) SetIV(iv []byte) { + c.iv = iv +} + +func (c *StreamCipher) SetKey(key []byte) { + c.key = key +} + +func (c *StreamCipher) InfoIVLen() int { + return c.info.ivLen +} + +func (c *StreamCipher) InfoKeyLen() int { + return c.info.keyLen +} diff --git a/proxy/ssr/internal/client.go b/proxy/ssr/internal/client.go new file mode 100644 index 0000000..670a648 --- /dev/null +++ b/proxy/ssr/internal/client.go @@ -0,0 +1,242 @@ +// source from https://github.com/v2rayA/shadowsocksR +// just copy here to use the builtin buffer pool. +// as this protocol hasn't been maintained since 2017, it doesn't deserve our research to rewrite it. + +package internal + +import ( + "bytes" + "errors" + "fmt" + "math/rand" + "net" + "time" + + "github.com/mzz2017/shadowsocksR/obfs" + "github.com/mzz2017/shadowsocksR/protocol" + + "github.com/nadoo/glider/pool" +) + +const bufSize = 16 << 10 + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +// SSTCPConn the struct that override the net.Conn methods +type SSTCPConn struct { + net.Conn + *StreamCipher + IObfs obfs.IObfs + IProtocol protocol.IProtocol + readBuf []byte + underPostdecryptBuf *bytes.Buffer + readIndex uint64 + decryptedBuf *bytes.Buffer + writeBuf []byte + lastReadError error +} + +func NewSSTCPConn(c net.Conn, cipher *StreamCipher) *SSTCPConn { + return &SSTCPConn{ + Conn: c, + StreamCipher: cipher, + readBuf: pool.GetBuffer(bufSize), + decryptedBuf: pool.GetWriteBuffer(), + underPostdecryptBuf: pool.GetWriteBuffer(), + writeBuf: pool.GetBuffer(bufSize), + } +} + +func (c *SSTCPConn) Close() error { + pool.PutBuffer(c.readBuf) + pool.PutWriteBuffer(c.decryptedBuf) + pool.PutWriteBuffer(c.underPostdecryptBuf) + pool.PutBuffer(c.writeBuf) + return c.Conn.Close() +} + +func (c *SSTCPConn) GetIv() (iv []byte) { + iv = make([]byte, len(c.IV())) + copy(iv, c.IV()) + return +} + +func (c *SSTCPConn) GetKey() (key []byte) { + key = make([]byte, len(c.Key())) + copy(key, c.Key()) + return +} + +func (c *SSTCPConn) initEncryptor(b []byte) (iv []byte, err error) { + if !c.EncryptInited() { + iv, err = c.InitEncrypt() + if err != nil { + return nil, err + } + + overhead := c.IObfs.GetOverhead() + c.IProtocol.GetOverhead() + // should initialize obfs/protocol now, because iv is ready now + obfsServerInfo := c.IObfs.GetServerInfo() + obfsServerInfo.SetHeadLen(b, 30) + obfsServerInfo.IV, obfsServerInfo.IVLen = c.IV(), c.InfoIVLen() + obfsServerInfo.Key, obfsServerInfo.KeyLen = c.Key(), c.InfoKeyLen() + obfsServerInfo.Overhead = overhead + c.IObfs.SetServerInfo(obfsServerInfo) + + protocolServerInfo := c.IProtocol.GetServerInfo() + protocolServerInfo.SetHeadLen(b, 30) + protocolServerInfo.IV, protocolServerInfo.IVLen = c.IV(), c.InfoIVLen() + protocolServerInfo.Key, protocolServerInfo.KeyLen = c.Key(), c.InfoKeyLen() + protocolServerInfo.Overhead = overhead + c.IProtocol.SetServerInfo(protocolServerInfo) + } + return +} + +func (c *SSTCPConn) Read(b []byte) (n int, err error) { + for { + n, err = c.doRead(b) + if b == nil || n != 0 || err != nil { + return n, err + } + } +} + +func (c *SSTCPConn) doRead(b []byte) (n int, err error) { + if c.decryptedBuf.Len() > 0 { + return c.decryptedBuf.Read(b) + } + n, err = c.Conn.Read(c.readBuf) + if n == 0 || err != nil { + return n, err + } + decodedData, needSendBack, err := c.IObfs.Decode(c.readBuf[:n]) + if err != nil { + //log.Println(c.Conn.LocalAddr().String(), c.IObfs.(*obfs.tls12TicketAuth).handshakeStatus, err) + return 0, err + } + + //do send back + if needSendBack { + c.Write(nil) + //log.Println("sendBack") + return 0, nil + } + //log.Println(len(decodedData), needSendBack, err, n) + if len(decodedData) == 0 { + //log.Println(string(c.readBuf[:200])) + } + decodedDataLen := len(decodedData) + if decodedDataLen == 0 { + return 0, nil + } + + if !c.DecryptInited() { + + if len(decodedData) < c.InfoIVLen() { + return 0, errors.New(fmt.Sprintf("invalid ivLen:%v, actual length:%v", c.InfoIVLen(), len(decodedData))) + } + iv := decodedData[0:c.InfoIVLen()] + if err = c.InitDecrypt(iv); err != nil { + return 0, err + } + + if len(c.IV()) == 0 { + c.SetIV(iv) + } + decodedDataLen -= c.InfoIVLen() + if decodedDataLen <= 0 { + return 0, nil + } + decodedData = decodedData[c.InfoIVLen():] + } + + // nadoo: comment out codes here to use pool buffer + // modify start --> + // buf := make([]byte, decodedDataLen) + // // decrypt decodedData and save it to buf + // c.Decrypt(buf, decodedData) + // // append buf to c.underPostdecryptBuf + // c.underPostdecryptBuf.Write(buf) + // // and read it to buf immediately + // buf = c.underPostdecryptBuf.Bytes() + + buf1 := pool.GetBuffer(decodedDataLen) + defer pool.PutBuffer(buf1) + + c.Decrypt(buf1, decodedData) + c.underPostdecryptBuf.Write(buf1) + buf := c.underPostdecryptBuf.Bytes() + // --> modify end + + postDecryptedData, length, err := c.IProtocol.PostDecrypt(buf) + if err != nil { + c.underPostdecryptBuf.Reset() + //log.Println(string(decodebytes)) + //log.Println("err", err) + return 0, err + } + if length == 0 { + // not enough to postDecrypt + return 0, nil + } else { + c.underPostdecryptBuf.Next(length) + } + + postDecryptedLength := len(postDecryptedData) + blength := len(b) + if blength >= postDecryptedLength { + copy(b, postDecryptedData) + return postDecryptedLength, nil + } + copy(b, postDecryptedData[:blength]) + c.decryptedBuf.Write(postDecryptedData[blength:]) + return blength, nil +} + +func (c *SSTCPConn) preWrite(b []byte) (outData []byte, err error) { + if b == nil { + b = make([]byte, 0) + } + var iv []byte + if iv, err = c.initEncryptor(b); err != nil { + return + } + + var preEncryptedData []byte + preEncryptedData, err = c.IProtocol.PreEncrypt(b) + if err != nil { + return + } + preEncryptedDataLen := len(preEncryptedData) + //! \attention here the expected output buffer length MUST be accurate, it is preEncryptedDataLen now! + + cipherData := c.writeBuf + dataSize := preEncryptedDataLen + len(iv) + if dataSize > len(cipherData) { + cipherData = make([]byte, dataSize) + } else { + cipherData = cipherData[:dataSize] + } + + if iv != nil { + // Put initialization vector in buffer before be encoded + copy(cipherData, iv) + } + c.Encrypt(cipherData[len(iv):], preEncryptedData) + return c.IObfs.Encode(cipherData) +} + +func (c *SSTCPConn) Write(b []byte) (n int, err error) { + outData, err := c.preWrite(b) + if err != nil { + return 0, err + } + n, err = c.Conn.Write(outData) + if err != nil { + return 0, err + } + return len(b), nil +} diff --git a/proxy/ssr/ssr.go b/proxy/ssr/ssr.go index 4797fde..348c12d 100644 --- a/proxy/ssr/ssr.go +++ b/proxy/ssr/ssr.go @@ -7,15 +7,14 @@ import ( "strconv" "strings" - shadowsocksr "github.com/mzz2017/shadowsocksR" "github.com/mzz2017/shadowsocksR/obfs" "github.com/mzz2017/shadowsocksR/protocol" ssrinfo "github.com/mzz2017/shadowsocksR/ssr" - "github.com/mzz2017/shadowsocksR/streamCipher" "github.com/nadoo/glider/log" "github.com/nadoo/glider/proxy" "github.com/nadoo/glider/proxy/socks" + "github.com/nadoo/glider/proxy/ssr/internal" ) func init() { @@ -87,7 +86,7 @@ func (s *SSR) Dial(network, addr string) (net.Conn, error) { return nil, errors.New("[ssr] unable to parse address: " + addr) } - cipher, err := streamCipher.NewStreamCipher(s.EncryptMethod, s.EncryptPassword) + cipher, err := internal.NewStreamCipher(s.EncryptMethod, s.EncryptPassword) if err != nil { return nil, err } @@ -98,7 +97,7 @@ func (s *SSR) Dial(network, addr string) (net.Conn, error) { return nil, err } - ssrconn := shadowsocksr.NewSSTCPConn(c, cipher) + ssrconn := internal.NewSSTCPConn(c, cipher) if ssrconn.Conn == nil || ssrconn.RemoteAddr() == nil { return nil, errors.New("[ssr] nil connection") }