From f5d06aaad676780205f28287ccfef185da81baff Mon Sep 17 00:00:00 2001 From: Page Fault Date: Wed, 27 May 2020 19:43:42 +0000 Subject: [PATCH] stick all smux headers, disable smux keep alive --- go.mod | 2 +- go.sum | 4 +-- proxy/client/mux.go | 82 ++++++++++++++++++++++++++++++++++-------- proxy/proxy.go | 8 +++++ proxy/server/server.go | 6 ++-- test/proxy_test.go | 5 ++- 6 files changed, 86 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 83e0c07..1f835e0 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/proullon/ramsql v0.0.0-20181213202341-817cee58a244 github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57 github.com/smartystreets/goconvey v1.6.4 - github.com/xtaci/smux v1.5.14 + github.com/xtaci/smux v1.5.15-0.20200523091831-637399ad4398 github.com/ziutek/mymysql v1.5.4 // indirect go.starlark.net v0.0.0-20200519165436-0aa95694c768 // indirect golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 diff --git a/go.sum b/go.sum index cb5c720..9fe8315 100644 --- a/go.sum +++ b/go.sum @@ -292,8 +292,8 @@ github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP080 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xtaci/smux v1.5.14 h1:1j+zJYDZRv9FHaWqCJfH5RPizIm0fSzJIFbfVn8zsfg= -github.com/xtaci/smux v1.5.14/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY= +github.com/xtaci/smux v1.5.15-0.20200523091831-637399ad4398 h1:1nJafFt4SJPzJ5RbWBP2OUJ7Xcx7pdjyjldEdFrLfKs= +github.com/xtaci/smux v1.5.15-0.20200523091831-637399ad4398/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= diff --git a/proxy/client/mux.go b/proxy/client/mux.go index e63fdcd..139a008 100644 --- a/proxy/client/mux.go +++ b/proxy/client/mux.go @@ -19,23 +19,75 @@ import ( // HACK stick the smux 8 bytes header to the payload type smuxStickyReadWriteCloser struct { io.ReadWriteCloser - smuxHeader []byte + synQueue chan []byte + finQueue chan []byte +} + +func (rwc *smuxStickyReadWriteCloser) stickToPayload(p []byte) []byte { + buf := make([]byte, 0, len(p)+16) + for { + select { + case header := <-rwc.synQueue: + buf = append(buf, header...) + default: + goto stick1 + } + } +stick1: + buf = append(buf, p...) + for { + select { + case header := <-rwc.finQueue: + buf = append(buf, header...) + default: + goto stick2 + } + } +stick2: + return buf +} + +func (rwc *smuxStickyReadWriteCloser) Close() error { + const maxPaddingLength = 512 + padding := [maxPaddingLength + 8]byte{0, 0, 'A', 'B', 'C', 'D', 'E', 'F'} + buf := rwc.stickToPayload(nil) + rwc.Write(append(buf, padding[:rand.Intn(maxPaddingLength)]...)) + return rwc.ReadWriteCloser.Close() } func (rwc *smuxStickyReadWriteCloser) Write(p []byte) (int, error) { - if len(p) == 8 && rwc.smuxHeader == nil { - // check version and command, cmdPSH = 2 - if (p[0] == 1 || p[0] == 2) && (p[1] == 2) { - rwc.smuxHeader = p - return 8, nil + if len(p) == 8 { + if p[0] == 1 || p[0] == 2 { //smux 8 bytes header + switch p[1] { + // THE CONTENT OF THE BUFFER MIGHT CHANGE + // NEVER STORE THE POINTER TO HEADER, COPY THE HEADER INSTEAD + case 0: + //cmdSYN + header := make([]byte, 8) + copy(header, p) + rwc.synQueue <- header + return 8, nil + case 1: + //cmdFIN + header := make([]byte, 8) + copy(header, p) + rwc.finQueue <- header + return 8, nil + } + } else { + log.Debug("Unknown 8 bytes") } } - if rwc.smuxHeader != nil { - _, err := rwc.ReadWriteCloser.Write(append(rwc.smuxHeader, p...)) - rwc.smuxHeader = nil - return len(p), err + _, err := rwc.ReadWriteCloser.Write(rwc.stickToPayload(p)) + return len(p), err +} + +func newSmuxStickyReadWriteCloser(rwc io.ReadWriteCloser) *smuxStickyReadWriteCloser { + return &smuxStickyReadWriteCloser{ + ReadWriteCloser: rwc, + synQueue: make(chan []byte, 128), + finQueue: make(chan []byte, 128), } - return rwc.ReadWriteCloser.Write(p) } type muxID uint32 @@ -84,11 +136,11 @@ func (m *MuxManager) newMuxClient() (*muxClientInfo, error) { return nil, err } - smuxRWC := &smuxStickyReadWriteCloser{ - ReadWriteCloser: trojanConn, - } + smuxRWC := newSmuxStickyReadWriteCloser(trojanConn) - client, err := smux.Client(smuxRWC, nil) + smuxConfig := smux.DefaultConfig() + smuxConfig.KeepAliveDisabled = true + client, err := smux.Client(smuxRWC, smuxConfig) common.Must(err) log.Info("Mux TLS tunnel established, client id:", id) return &muxClientInfo{ diff --git a/proxy/proxy.go b/proxy/proxy.go index 05fea9e..3999dee 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -17,6 +17,10 @@ type Buildable interface { } func RelayConn(ctx context.Context, a, b io.ReadWriter, bufferSize int) { + if a == nil || b == nil { + log.Debug("Empty RW") + return + } errChan := make(chan error, 2) copyConn := func(dst io.Writer, src io.Reader) { buf := make([]byte, bufferSize) @@ -36,6 +40,10 @@ func RelayConn(ctx context.Context, a, b io.ReadWriter, bufferSize int) { } func RelayPacket(ctx context.Context, a, b protocol.PacketReadWriter) { + if a == nil || b == nil { + log.Debug("Empty RW") + return + } errChan := make(chan error, 2) copyPacket := func(dst protocol.PacketWriter, src protocol.PacketReader) { for { diff --git a/proxy/server/server.go b/proxy/server/server.go index dd9ea4d..d1e0ab1 100644 --- a/proxy/server/server.go +++ b/proxy/server/server.go @@ -79,13 +79,15 @@ func (s *Server) handleConn(conn net.Conn) { defer conn.Close() if req.Command == protocol.Mux { - muxServer, err := smux.Server(inboundConn, nil) + smuxConfig := smux.DefaultConfig() + smuxConfig.KeepAliveDisabled = true + muxServer, err := smux.Server(inboundConn, smuxConfig) common.Must(err) defer muxServer.Close() for { stream, err := muxServer.AcceptStream() if err != nil { - log.Debug("Mux conn from", conn.RemoteAddr(), "closed:", err) + log.Error(common.NewError("Failed to accpet mux conn from " + conn.RemoteAddr().String()).Base(err)) return } go s.handleMuxConn(stream) diff --git a/test/proxy_test.go b/test/proxy_test.go index 31955da..65383cd 100644 --- a/test/proxy_test.go +++ b/test/proxy_test.go @@ -148,6 +148,9 @@ func getBasicClientConfig() *conf.GlobalConfig { Passwords: getPasswords("trojanpassword"), BufferSize: 512 * 1024, } + file, err := os.OpenFile("keylog.txt", os.O_CREATE|os.O_WRONLY, 0600) + common.Must(err) + config.TLS.KeyLogger = file return config } @@ -381,7 +384,7 @@ func TestRealProxy(t *testing.T) { if os.Getenv("real_test") == "" { t.Skip("skipping real proxy test") } - clientConfig := getBasicClientConfig() + clientConfig := addMuxConfig(getBasicClientConfig()) serverConfig := getBasicServerConfig() go RunClient(context.Background(), clientConfig) go RunHelloHTTPServer(context.Background())