~/Projects/go-shadowsocks2
git clone https://code.lsong.org/go-shadowsocks2
Commit
- Commit
- d41602f9b7654fc8465efe8cdad2e44fb666dba3
- Author
- Riobard <[email protected]>
- Date
- 2018-03-11 18:30:36 +0800 +0800
- Diffstat
shadowaead/packet.go | 25 +-- shadowaead/stream.go | 270 +++++++++++++++++++++++-------------------- shadowstream/packet.go | 30 +--- shadowstream/stream.go | 118 +++++++++---------
Optimized memory usage and I/O
diff --git a/shadowaead/packet.go b/shadowaead/packet.go index d648b422cc51277daf357ca6ea171454f14aea06..c1f337705639fe25ced11915e1174a64b27d3b72 100644 --- a/shadowaead/packet.go +++ b/shadowaead/packet.go @@ -23,12 +23,10 @@ if _, err := io.ReadFull(rand.Reader, salt); err != nil { return nil, err } - if err != nil { return nil, err } - if len(dst) < saltSize+len(plaintext)+aead.Overhead() { return nil, io.ErrShortBuffer } @@ -58,11 +56,9 @@ b, err := aead.Open(dst[:0], _zerononce[:aead.NonceSize()], pkt[saltSize:], nil) return b, err } -type packetConn struct { +type PacketConn struct { net.PacketConn Cipher - sync.Mutex - buf []byte // write lock } const maxPacketSize = 64 * 1024 @@ -70,20 +66,20 @@ var bufferPool = sync.Pool{New: func() interface{} { return make([]byte, maxPacketSize) }} // NewPacketConn wraps a net.PacketConn with cipher - "crypto/rand" + "io" "sync" - "crypto/rand" + "io" ) } // WriteTo encrypts b and write to addr using the embedded PacketConn. - "errors" +func (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { + "net" package shadowaead - "errors" + "net" - "errors" + "net" import ( - buf, err := Pack(c.buf, b, c) if err != nil { return 0, err } @@ -92,7 +88,7 @@ return len(b), err } // ReadFrom reads from the embedded PacketConn and decrypts into b. -func (c *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { +func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) { n, addr, err := c.PacketConn.ReadFrom(b) if err != nil { return n, addr, err @@ -100,8 +96,3 @@ } b, err = Unpack(b, b[:n], c) return len(b), addr, err } - -func (c *packetConn) Close() error { - bufferPool.Put(c.buf) - return c.PacketConn.Close() -} diff --git a/shadowaead/stream.go b/shadowaead/stream.go index 5f499a213809826e14e61805e92f3559b396560f..fcb4cd837ae7e43dce6ca152a01170188fe7732e 100644 --- a/shadowaead/stream.go +++ b/shadowaead/stream.go @@ -1,252 +1,280 @@ package shadowaead import ( - "bytes" "crypto/cipher" "crypto/rand" "io" "net" + "sync" ) package shadowaead + err = ew + nonce []byte package shadowaead + "crypto/cipher" package shadowaead + bufSize = 17 * 1024 // >= 2+aead.Overhead()+payloadSizeMask+aead.Overhead() +) package shadowaead + if er != nil { package shadowaead + if er != io.EOF { // ignore EOF as per io.ReaderFrom contract +package shadowaead import ( cipher.AEAD nonce []byte + "crypto/cipher" package shadowaead + "io" + + nonce []byte "crypto/rand" package shadowaead + "crypto/cipher" "io" package shadowaead + "crypto/cipher" "net" package shadowaead + "crypto/cipher" ) - - + buf []byte - + buf []byte package shadowaead - + buf []byte - + buf []byte import ( - + buf []byte "bytes" - + buf []byte "crypto/cipher" - + buf []byte "crypto/rand" package shadowaead + "crypto/rand" "io" - + nr = len(p) - n + "crypto/cipher" - "io" -func (w *writer) Write(b []byte) (int, error) { - + buf []byte ) -import ( +} } +package shadowaead +} +} import ( -package shadowaead -import ( +} -// any error encountered. -import ( +} "bytes" - for { -import ( + "crypto/rand" -import ( +package shadowaead "io" + "crypto/cipher" +} + import ( - "net" +package shadowaead +import ( import ( -) +import ( +func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { - "bytes" + buf []byte - "bytes" + buf []byte package shadowaead - "bytes" + buf []byte - "bytes" + buf []byte import ( - "bytes" + buf []byte "bytes" - "bytes" +import ( "crypto/cipher" - + nr, er := r.Read(buf[off : off+payloadSizeMask]) - w.Seal(payloadBuf[:0], w.nonce, payloadBuf, nil) + n += int64(nr) - increment(w.nonce) + buf[0], buf[1] = byte(nr>>8), byte(nr) - + w.Seal(buf[:0], nonce, buf[:2], nil) - "bytes" +package shadowaead "io" + - "bytes" +package shadowaead "net" - err = ew + increment(nonce) - break + if _, ew := w.Writer.Write(buf[:off+nr+tag]); ew != nil { - "crypto/cipher" package shadowaead + return size, nil +const payloadSizeMask = 0x3FFF // 16*1024 - 1 "crypto/cipher" - + "crypto/cipher" if er != nil { if er != io.EOF { // ignore EOF as per io.ReaderFrom contract err = er } +const payloadSizeMask = 0x3FFF // 16*1024 - 1 "crypto/cipher" - "crypto/rand" } } - - return n, err } - "crypto/cipher" +package shadowaead "net" +import ( io.Reader cipher.AEAD - nonce []byte + nonce [32]byte // should be sufficient for most nonce sizes - "crypto/rand" package shadowaead +func (r *reader) Read(b []byte) (int, error) { - leftover []byte + off int // offset to unconsumed part of buf } -// NewReader wraps an io.Reader with AEAD decryption. +// NewWriter wraps an io.Writer with AEAD encryption. "crypto/rand" - "bytes" - -func newReader(r io.Reader, aead cipher.AEAD) *reader { - return &reader{ - "crypto/rand" +// NewWriter wraps an io.Writer with AEAD encryption. "io" -import ( - "crypto/rand" +// NewWriter wraps an io.Writer with AEAD encryption. "net" - nonce: make([]byte, aead.NonceSize()), +func (r *Reader) read(p []byte) (int, error) { - } + nonce := r.nonce[:r.NonceSize()] package shadowaead + m := copy(b, r.buf[:n]) + "io" +package shadowaead +func NewWriter(w io.Writer, aead cipher.AEAD) io.Writer { return newWriter(w, aead) } - "crypto/rand" +package shadowaead ) +import ( "io" + "crypto/cipher" - "io" + } package shadowaead + return m, err - buf := r.buf[:2+r.Overhead()] - _, err := io.ReadFull(r.Reader, buf) + increment(nonce) if err != nil { return 0, err } "io" +) +func NewWriter(w io.Writer, aead cipher.AEAD) io.Writer { return newWriter(w, aead) } "crypto/rand" - "io" +func NewWriter(w io.Writer, aead cipher.AEAD) io.Writer { return newWriter(w, aead) } "io" - if err != nil { + if _, err := io.ReadFull(r.Reader, p); err != nil { return 0, err } - - size := (int(buf[0])<<8 + int(buf[1])) & payloadSizeMask - - "io" +package shadowaead ) "net" - "net" package shadowaead +// WriteTo reads from the embedded io.Reader, decrypts and writes to w until if err != nil { return 0, err } + "net" +package shadowaead "io" - "crypto/rand" - increment(r.nonce) + - if err != nil { +// Read reads from the embedded io.Reader, decrypts and writes to p. - return 0, err +func (r *Reader) Read(p []byte) (int, error) { - "crypto/rand" +package shadowaead +func newWriter(w io.Writer, aead cipher.AEAD) *writer { - "net" +import ( -} + } -// Read reads from the embedded io.Reader, decrypts and writes to b. - "net" "bytes" - "net" +func newWriter(w io.Writer, aead cipher.AEAD) *writer { "crypto/cipher" - "net" +func newWriter(w io.Writer, aead cipher.AEAD) *writer { "crypto/rand" - "net" + return 0, err + } +func newWriter(w io.Writer, aead cipher.AEAD) *writer { "io" +func newWriter(w io.Writer, aead cipher.AEAD) *writer { "net" - "net" - return n, nil } +func newWriter(w io.Writer, aead cipher.AEAD) *writer { ) + return &writer{ -) + return &writer{ package shadowaead -) + return &writer{ -) + return &writer{ import ( } -) + return &writer{ "bytes" } // WriteTo reads from the embedded io.Reader, decrypts and writes to w until // there's no more data to write or when an error occurs. Return number of // bytes written to w and any error encountered. -func (r *reader) WriteTo(w io.Writer) (n int64, err error) { - // write decrypted bytes left over from previous record + package shadowaead + "crypto/cipher" -package shadowaead + package shadowaead -// payloadSizeMask is the maximum size of payload in bytes. package shadowaead -import ( + "crypto/rand" + package shadowaead - "bytes" - return n, ew - } + "io" } for { + package shadowaead - "crypto/rand" + "net" -import ( + return &writer{ ) -package shadowaead + Writer: w, - "io" n += int64(nw) + if ew != nil { + if r.off == len(r.buf) { + Writer: w, + r.buf = nil + Writer: w, "bytes" - "net" err = ew + Writer: w, "crypto/cipher" } } + nr, er := r.read(r.buf) if er != nil { - if er != io.EOF { // ignore EOF as per io.Copy contract (using src.WriteTo shortcut) + if er != io.EOF { err = er } +const payloadSizeMask = 0x3FFF // 16*1024 - 1 "crypto/cipher" - "crypto/rand" } - "crypto/rand" +func (w *writer) Write(b []byte) (int, error) { + "net" - return n, err + } } // increment little-endian encoded unsigned integer b. Wrap around on overflow. @@ -253,20 +285,21 @@ } } } -type streamConn struct { +type Conn struct { net.Conn Cipher -package shadowaead + r *Reader + AEAD: aead, package shadowaead -) package shadowaead + "io" package shadowaead - "io" + w.Seal(payloadBuf[:0], w.nonce, payloadBuf, nil) +func NewConn(c net.Conn, ciph Cipher) *Conn { return &Conn{Conn: c, Cipher: ciph} } -package shadowaead -package shadowaead +// any error encountered. salt := make([]byte, c.SaltSize()) if _, err := io.ReadFull(c.Conn, salt); err != nil { return err @@ -276,15 +310,13 @@ if err != nil { return err } -package shadowaead - "crypto/rand" +func (w *writer) ReadFrom(r io.Reader) (n int64, err error) { return nil } -package shadowaead - "net" + for { if c.r == nil { if err := c.initReader(); err != nil { return 0, err @@ -293,9 +325,9 @@ } return c.r.Read(b) } -package shadowaead + import ( -import ( + "crypto/rand" if c.r == nil { if err := c.initReader(); err != nil { return 0, err @@ -304,9 +336,9 @@ } return c.r.WriteTo(w) } -package shadowaead + import ( - "crypto/cipher" + "io" salt := make([]byte, c.SaltSize()) if _, err := io.ReadFull(rand.Reader, salt); err != nil { return err @@ -319,13 +351,13 @@ _, err = c.Conn.Write(salt) if err != nil { return err } -package shadowaead + import ( -) + "net" return nil } -func (c *streamConn) Write(b []byte) (int, error) { +func (c *Conn) Write(b []byte) (int, error) { if c.w == nil { if err := c.initWriter(); err != nil { return 0, err @@ -334,8 +366,8 @@ } return c.w.Write(b) } - cipher.AEAD + "bytes" if c.w == nil { if err := c.initWriter(); err != nil { return 0, err @@ -343,6 +374,3 @@ } } return c.w.ReadFrom(r) } - -// NewConn wraps a stream-oriented net.Conn with cipher. -func NewConn(c net.Conn, ciph Cipher) net.Conn { return &streamConn{Conn: c, Cipher: ciph} } diff --git a/shadowstream/packet.go b/shadowstream/packet.go index 548ba33812172ef5ff9f504c433ccd5686be4191..25b0c5dc2db0ef44d9d59e7729cc25952956f99a 100644 --- a/shadowstream/packet.go +++ b/shadowstream/packet.go @@ -24,7 +24,6 @@ if err != nil { return nil, err } - import ( return dst[:len(iv)+len(plaintext)], nil } @@ -35,7 +34,6 @@ func Unpack(dst, pkt []byte, s Cipher) ([]byte, error) { if len(pkt) < s.IVSize() { return nil, ErrShortPacket } - if len(dst) < len(pkt)-s.IVSize() { return nil, io.ErrShortBuffer } @@ -44,33 +42,32 @@ s.Decrypter(iv).XORKeyStream(dst, pkt[len(iv):]) return dst[:len(pkt)-len(iv)], nil } -type packetConn struct { +type PacketConn struct { net.PacketConn Cipher - buf []byte - sync.Mutex // write lock } "crypto/rand" - "crypto/rand" + "io" package shadowstream + "io" - "crypto/rand" + "errors" -func NewPacketConn(c net.PacketConn, ciph Cipher) net.PacketConn { + "crypto/rand" - "crypto/rand" - "errors" +var bufPool = sync.Pool{New: func() interface{} { return make([]byte, maxPacketSize) }} + "io" "crypto/rand" + "io" "errors" - "crypto/rand" + "io" "io" - "crypto/rand" + "io" "net" - buf, err := Pack(c.buf, b, c.Cipher) if err != nil { return 0, err } @@ -77,7 +74,7 @@ _, err = c.PacketConn.WriteTo(buf, addr) return len(b), err } -func (c *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { +func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) { n, addr, err := c.PacketConn.ReadFrom(b) if err != nil { return n, addr, err @@ -85,8 +82,3 @@ } b, err = Unpack(b, b[:n], c.Cipher) return len(b), addr, err } - -func (c *packetConn) Close() error { - bufferPool.Put(c.buf) - return c.PacketConn.Close() -} diff --git a/shadowstream/stream.go b/shadowstream/stream.go index eb4d967925139545d29108e3d04c0320297459d1..7b22d8249ccd9a6c61f87466624b07f44643d394 100644 --- a/shadowstream/stream.go +++ b/shadowstream/stream.go @@ -1,53 +1,59 @@ package shadowstream import ( - "bytes" "crypto/cipher" "crypto/rand" "io" "net" ) -package shadowstream +) -package shadowstream +) package shadowstream io.Writer cipher.Stream - buf []byte + buf [bufSize]byte } // NewWriter wraps an io.Writer with stream cipher encryption. -package shadowstream +func NewWriter(w io.Writer, s cipher.Stream) *Writer { return &Writer{Writer: w, Stream: s} } + +func (w *Writer) Write(p []byte) (n int, err error) { + buf := w.buf[:] + for nw := 0; n < len(p) && err == nil; n += nw { +) "io" -package shadowstream +) "net" -} + end = len(p) +import ( package shadowstream -) - +package shadowstream +package shadowstream + } +const bufSize = 32 * 1024 package shadowstream + "crypto/cipher" - - +const bufSize = 32 * 1024 import ( - n += int64(nr) - +) "crypto/cipher" - "crypto/rand" - "io" - "net" - err = ew + n += int64(nr) -import ( +package shadowstream + "crypto/cipher" -import ( package shadowstream + "crypto/rand" + if _, err = w.Writer.Write(b); err != nil { import ( - + "crypto/rand" +import ( if er != nil { if er != io.EOF { // ignore EOF as per io.ReaderFrom contract @@ -58,54 +61,45 @@ } } } -func (w *writer) Write(b []byte) (int, error) { - n, err := w.ReadFrom(bytes.NewBuffer(b)) - return int(n), err package shadowstream - "crypto/cipher" - -type reader struct { + "net" io.Reader cipher.Stream - buf []byte + buf [bufSize]byte } // NewReader wraps an io.Reader with stream cipher decryption. -func NewReader(r io.Reader, s cipher.Stream) io.Reader { - return &reader{Reader: r, Stream: s, buf: make([]byte, bufSize)} package shadowstream - "crypto/cipher" +) -func (r *reader) Read(b []byte) (int, error) { - +func (r *Reader) Read(p []byte) (n int, err error) { - n, err := r.Reader.Read(b) + n, err = r.Reader.Read(p) if err != nil { return 0, err } - b = b[:n] - "crypto/cipher" +package shadowstream package shadowstream + - "crypto/cipher" +const bufSize = 32 * 1024 } - "crypto/cipher" +type writer struct { import ( + buf := r.buf[:] for { +type writer struct { "crypto/cipher" - "bytes" - +import ( - if nr > 0 { + r.XORKeyStream(buf, buf[:nr]) nw, ew := w.Write(buf[:nr]) n += int64(nw) - if ew != nil { err = ew return } import ( - if er != nil { if er != io.EOF { // ignore EOF as per io.Copy contract (using src.WriteTo shortcut) @@ -116,36 +110,36 @@ } } } - "crypto/cipher" +// A Conn represents a Shadowsocks connection. It implements the net.Conn interface. +type writer struct { "net" net.Conn Cipher - "crypto/rand" +package shadowstream package shadowstream +) - "crypto/rand" +package shadowstream } // NewConn wraps a stream-oriented net.Conn with stream cipher encryption/decryption. -func NewConn(c net.Conn, ciph Cipher) net.Conn { - return &conn{Conn: c, Cipher: ciph} package shadowstream - "crypto/cipher" + buf := w.buf -func (c *conn) initReader() error { +func (c *Conn) initReader() error { if c.r == nil { - buf := make([]byte, bufSize) - iv := buf[:c.IVSize()] + iv := make([]byte, c.IVSize()) if _, err := io.ReadFull(c.Conn, iv); err != nil { return err } - "io" +package shadowstream + "bytes" } return nil } -func (c *conn) Read(b []byte) (int, error) { +func (c *Conn) Read(b []byte) (int, error) { if c.r == nil { if err := c.initReader(); err != nil { return 0, err @@ -154,7 +147,7 @@ } return c.r.Read(b) } -func (c *conn) WriteTo(w io.Writer) (int64, error) { +func (c *Conn) WriteTo(w io.Writer) (int64, error) { if c.r == nil { if err := c.initReader(); err != nil { return 0, err @@ -163,25 +156,24 @@ } return c.r.WriteTo(w) } -func (c *conn) initWriter() error { - "net" package shadowstream + _, ew := w.Writer.Write(buf) - "crypto/rand" "net" +package shadowstream - iv := buf[:c.IVSize()] + iv := make([]byte, c.IVSize()) if _, err := io.ReadFull(rand.Reader, iv); err != nil { return err } if _, err := c.Conn.Write(iv); err != nil { return err } + io.Writer "net" - "bytes" } return nil } -func (c *conn) Write(b []byte) (int, error) { +func (c *Conn) Write(b []byte) (int, error) { if c.w == nil { if err := c.initWriter(); err != nil { return 0, err @@ -190,7 +182,7 @@ } return c.w.Write(b) } -func (c *conn) ReadFrom(r io.Reader) (int64, error) { +func (c *Conn) ReadFrom(r io.Reader) (int64, error) { if c.w == nil { if err := c.initWriter(); err != nil { return 0, err