From 5da880b010f3fe58b3c37d2fa9f62af046df2b2c Mon Sep 17 00:00:00 2001 From: Anonymous-Someneese Date: Fri, 3 Jan 2020 03:13:07 +0800 Subject: [PATCH 1/3] write salt/iv, addr together with content Sending salt, address and content seperately would generate three packets at the start of a connection. Since the size of salt is almost always 8, 16 or 32, it leaves a strong feature. Additionally, with TCP fastopen turned on, only the first write would be carried by SYN packet. Writing multiple times makes it pointless. --- shadowaead/stream.go | 31 +++++++++++++++++++++++-------- shadowstream/stream.go | 29 ++++++++++++++++++++++------- tcp.go | 26 ++++++++++++++++++++------ 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/shadowaead/stream.go b/shadowaead/stream.go index 5f499a21..054f9419 100644 --- a/shadowaead/stream.go +++ b/shadowaead/stream.go @@ -16,17 +16,19 @@ type writer struct { cipher.AEAD nonce []byte buf []byte + salt []byte } // NewWriter wraps an io.Writer with AEAD encryption. -func NewWriter(w io.Writer, aead cipher.AEAD) io.Writer { return newWriter(w, aead) } +func NewWriter(w io.Writer, aead cipher.AEAD, salt []byte) io.Writer { return newWriter(w, aead, salt) } -func newWriter(w io.Writer, aead cipher.AEAD) *writer { +func newWriter(w io.Writer, aead cipher.AEAD, salt []byte) *writer { return &writer{ Writer: w, AEAD: aead, buf: make([]byte, 2+aead.Overhead()+payloadSizeMask+aead.Overhead()), nonce: make([]byte, aead.NonceSize()), + salt: salt, } } @@ -36,6 +38,23 @@ func (w *writer) Write(b []byte) (int, error) { return int(n), err } +// Write salt before encrypted buffer to io.Writer. +func (w *writer) write(b []byte) (int, error) { + if len(w.salt) == 0 { + return w.Writer.Write(b) + } + buf := make([]byte, len(w.salt) + len(b)) + copy(buf[:len(w.salt)], w.salt) + copy(buf[len(w.salt):], b) + nw, err := w.Writer.Write(buf) + if nw < len(w.salt) { + w.salt = w.salt[nw:] + return 0, err + } + w.salt = nil + return nw - len(w.salt), err +} + // ReadFrom reads from the given io.Reader until EOF or error, encrypts and // writes to the embedded io.Writer. Returns number of bytes read from r and // any error encountered. @@ -56,7 +75,7 @@ func (w *writer) ReadFrom(r io.Reader) (n int64, err error) { w.Seal(payloadBuf[:0], w.nonce, payloadBuf, nil) increment(w.nonce) - _, ew := w.Writer.Write(buf) + _, ew := w.write(buf) if ew != nil { err = ew break @@ -240,11 +259,7 @@ func (c *streamConn) initWriter() error { if err != nil { return err } - _, err = c.Conn.Write(salt) - if err != nil { - return err - } - c.w = newWriter(c.Conn, aead) + c.w = newWriter(c.Conn, aead, salt) return nil } diff --git a/shadowstream/stream.go b/shadowstream/stream.go index eb4d9679..5d1d09bb 100644 --- a/shadowstream/stream.go +++ b/shadowstream/stream.go @@ -14,11 +14,12 @@ type writer struct { io.Writer cipher.Stream buf []byte + iv []byte } // NewWriter wraps an io.Writer with stream cipher encryption. -func NewWriter(w io.Writer, s cipher.Stream) io.Writer { - return &writer{Writer: w, Stream: s, buf: make([]byte, bufSize)} +func NewWriter(w io.Writer, s cipher.Stream, iv []byte) io.Writer { + return &writer{Writer: w, Stream: s, buf: make([]byte, bufSize), iv: iv} } func (w *writer) ReadFrom(r io.Reader) (n int64, err error) { @@ -29,7 +30,7 @@ func (w *writer) ReadFrom(r io.Reader) (n int64, err error) { n += int64(nr) buf = buf[:nr] w.XORKeyStream(buf, buf) - _, ew := w.Writer.Write(buf) + _, ew := w.write(buf) if ew != nil { err = ew return @@ -50,6 +51,23 @@ func (w *writer) Write(b []byte) (int, error) { return int(n), err } +// Write IV before encrypted buffer to io.Writer. +func (w *writer) write(b []byte) (int, error) { + if len(w.iv) == 0 { + return w.Writer.Write(b) + } + buf := make([]byte, len(w.iv) + len(b)) + copy(buf[:len(w.iv)], w.iv) + copy(buf[len(w.iv):], b) + nw, err := w.Writer.Write(buf) + if nw < len(w.iv) { + w.iv = w.iv[nw:] + return 0, err + } + w.iv = nil + return nw - len(w.iv), err +} + type reader struct { io.Reader cipher.Stream @@ -144,10 +162,7 @@ func (c *conn) initWriter() error { if _, err := io.ReadFull(rand.Reader, iv); err != nil { return err } - if _, err := c.Conn.Write(iv); err != nil { - return err - } - c.w = &writer{Writer: c.Conn, Stream: c.Encrypter(iv), buf: buf} + c.w = &writer{Writer: c.Conn, Stream: c.Encrypter(iv), buf: buf, iv: iv} } return nil } diff --git a/tcp.go b/tcp.go index 243b2704..e8892011 100644 --- a/tcp.go +++ b/tcp.go @@ -73,13 +73,9 @@ func tcpLocal(addr, server string, shadow func(net.Conn) net.Conn, getAddr func( rc.(*net.TCPConn).SetKeepAlive(true) rc = shadow(rc) - if _, err = rc.Write(tgt); err != nil { - logf("failed to send target address: %v", err) - return - } - logf("proxy %s <-> %s <-> %s", c.RemoteAddr(), server, tgt) - _, _, err = relay(rc, c) + ca := &connWithAddr{Conn: c, addr: tgt} + _, _, err = relay(rc, ca) if err != nil { if err, ok := err.(net.Error); ok && err.Timeout() { return // ignore i/o timeout @@ -163,3 +159,21 @@ func relay(left, right net.Conn) (int64, int64, error) { } return n, rs.N, err } + +type connWithAddr struct { + net.Conn + addr socks.Addr +} + +// Read reads the addr and data from the connection. +// The format of output is aligned with shadowsocks protocol. +func (c *connWithAddr) Read(b []byte) (n int, err error) { + nc := copy(b, c.addr) + if nc < len(c.addr) { + c.addr = c.addr[:nc] + return nc, nil + } + c.addr = nil + nr, err := c.Conn.Read(b[nc:]) + return nc + nr, err +} From eb0109aa92c2bb952ba92553e1382255ae506662 Mon Sep 17 00:00:00 2001 From: Anonymous-Someneese Date: Fri, 3 Jan 2020 21:45:49 +0800 Subject: [PATCH 2/3] wrap the cipher conn instead of local side conn This decouples two sides and allow easier third party integration. --- core/stream.go | 52 +++++++++++++++++++++++++++++++++++++++++++++++++- tcp.go | 24 ++++------------------- 2 files changed, 55 insertions(+), 21 deletions(-) diff --git a/core/stream.go b/core/stream.go index 5c773cd2..03a19793 100644 --- a/core/stream.go +++ b/core/stream.go @@ -1,6 +1,12 @@ package core -import "net" +import ( + "bytes" + "io" + "net" + + "github.com/shadowsocks/go-shadowsocks2/socks" +) type listener struct { net.Listener @@ -21,3 +27,47 @@ func Dial(network, address string, ciph StreamConnCipher) (net.Conn, error) { c, err := net.Dial(network, address) return ciph.StreamConn(c), err } + +// Connect sends the shadowsocks standard header to underlying ciphered connection +// in the next Write/ReadFrom call. +func Connect(c net.Conn, addr socks.Addr) net.Conn { + return &ssconn{Conn: c, addr: addr} +} + +type ssconn struct { + net.Conn + addr socks.Addr +} + +func (c *ssconn) Write(b []byte) (int, error) { + n, err := c.ReadFrom(bytes.NewBuffer(b)) + return int(n), err +} + +func (c *ssconn) ReadFrom(r io.Reader) (int64, error) { + if len(c.addr) > 0 { + r = &readerWithAddr{Reader: r, b: c.addr} + c.addr = nil + } + return io.Copy(c.Conn, r) +} + +func (c *ssconn) WriteTo(w io.Writer) (int64, error) { + return io.Copy(w, c.Conn) +} + +type readerWithAddr struct { + io.Reader + b []byte +} + +func (r *readerWithAddr) Read(b []byte) (n int, err error) { + nc := copy(b, r.b) + if nc < len(r.b) { + r.b = r.b[:nc] + return nc, nil + } + r.b = nil + nr, err := r.Reader.Read(b[nc:]) + return nc + nr, err +} diff --git a/tcp.go b/tcp.go index e8892011..b410c83f 100644 --- a/tcp.go +++ b/tcp.go @@ -5,6 +5,7 @@ import ( "net" "time" + "github.com/shadowsocks/go-shadowsocks2/core" "github.com/shadowsocks/go-shadowsocks2/socks" ) @@ -72,10 +73,11 @@ func tcpLocal(addr, server string, shadow func(net.Conn) net.Conn, getAddr func( defer rc.Close() rc.(*net.TCPConn).SetKeepAlive(true) rc = shadow(rc) + // Connect to target + rc = core.Connect(rc, tgt) logf("proxy %s <-> %s <-> %s", c.RemoteAddr(), server, tgt) - ca := &connWithAddr{Conn: c, addr: tgt} - _, _, err = relay(rc, ca) + _, _, err = relay(rc, c) if err != nil { if err, ok := err.(net.Error); ok && err.Timeout() { return // ignore i/o timeout @@ -159,21 +161,3 @@ func relay(left, right net.Conn) (int64, int64, error) { } return n, rs.N, err } - -type connWithAddr struct { - net.Conn - addr socks.Addr -} - -// Read reads the addr and data from the connection. -// The format of output is aligned with shadowsocks protocol. -func (c *connWithAddr) Read(b []byte) (n int, err error) { - nc := copy(b, c.addr) - if nc < len(c.addr) { - c.addr = c.addr[:nc] - return nc, nil - } - c.addr = nil - nr, err := c.Conn.Read(b[nc:]) - return nc + nr, err -} From 310a1b2a05cc818428a83b468842c134391a5647 Mon Sep 17 00:00:00 2001 From: Anonymous-Someneese Date: Tue, 7 Jan 2020 12:15:29 +0800 Subject: [PATCH 3/3] send header after 5ms if no content is received --- core/stream.go | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/core/stream.go b/core/stream.go index 03a19793..9cd4c31a 100644 --- a/core/stream.go +++ b/core/stream.go @@ -4,6 +4,9 @@ import ( "bytes" "io" "net" + "sync" + "sync/atomic" + "time" "github.com/shadowsocks/go-shadowsocks2/socks" ) @@ -31,12 +34,25 @@ func Dial(network, address string, ciph StreamConnCipher) (net.Conn, error) { // Connect sends the shadowsocks standard header to underlying ciphered connection // in the next Write/ReadFrom call. func Connect(c net.Conn, addr socks.Addr) net.Conn { - return &ssconn{Conn: c, addr: addr} + return newSSConn(c, addr, 5 * time.Millisecond) } type ssconn struct { net.Conn addr socks.Addr + done uint32 + m sync.Mutex + t *time.Timer +} + +func newSSConn(c net.Conn, addr socks.Addr, delay time.Duration) *ssconn { + sc := &ssconn{Conn: c, addr: addr} + if delay > 0 { + sc.t = time.AfterFunc(delay, func() { + sc.Write([]byte{}) + }) + } + return sc } func (c *ssconn) Write(b []byte) (int, error) { @@ -45,9 +61,22 @@ func (c *ssconn) Write(b []byte) (int, error) { } func (c *ssconn) ReadFrom(r io.Reader) (int64, error) { - if len(c.addr) > 0 { - r = &readerWithAddr{Reader: r, b: c.addr} - c.addr = nil + if atomic.LoadUint32(&c.done) == 0 { + c.m.Lock() + defer c.m.Unlock() + if c.done == 0 { + defer atomic.StoreUint32(&c.done, 1) + if c.t != nil { + c.t.Stop() + c.t = nil + } + ra := readerWithAddr{Reader: r, b: c.addr} + n, err := io.Copy(c.Conn, &ra) + n -= int64(len(c.addr)) + if n < 0 { n = 0 } + c.addr = nil + return n, err + } } return io.Copy(c.Conn, r) } @@ -63,11 +92,10 @@ type readerWithAddr struct { func (r *readerWithAddr) Read(b []byte) (n int, err error) { nc := copy(b, r.b) - if nc < len(r.b) { - r.b = r.b[:nc] + r.b = r.b[nc:] + if len(b) == nc { return nc, nil } - r.b = nil nr, err := r.Reader.Read(b[nc:]) return nc + nr, err }