From e45b8dc4041f24cc5e25858856bf2ee966e5eb84 Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Mon, 27 Feb 2023 00:26:49 +0800 Subject: [PATCH] chore: add early conn interface to decrease unneeded write --- adapter/outbound/shadowsocks.go | 16 +++++++++++--- adapter/outbound/vmess.go | 31 +++++++++++++++++++++++----- adapter/outboundgroup/fallback.go | 21 +++++++++++-------- adapter/outboundgroup/loadbalance.go | 22 ++++++++++++-------- adapter/outboundgroup/urltest.go | 22 ++++++++++++-------- common/convert/util.go | 3 ++- common/net/sing.go | 8 +++++++ component/dialer/tfo.go | 4 ++++ go.mod | 6 +++--- go.sum | 12 +++++------ listener/sing_shadowsocks/server.go | 3 ++- transport/vless/conn.go | 9 ++++++++ transport/vmess/websocket.go | 24 +++++++++++++++------ tunnel/tunnel.go | 25 +++++++++++++--------- 14 files changed, 144 insertions(+), 62 deletions(-) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 6e6a8d0a..aab5cf42 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -6,7 +6,9 @@ import ( "fmt" "net" "strconv" + "time" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" @@ -103,9 +105,17 @@ func (ss *ShadowSocks) streamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e } } if metadata.NetWork == C.UDP && ss.option.UDPOverTCP { - return ss.method.DialEarlyConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443")), nil + if N.NeedHandshake(c) { + return ss.method.DialEarlyConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443")), nil + } else { + return ss.method.DialConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443")) + } + } + if N.NeedHandshake(c) { + return ss.method.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil + } else { + return ss.method.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) } - return ss.method.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil } // DialContext implements C.ProxyAdapter @@ -184,7 +194,7 @@ func (ss *ShadowSocks) SupportUOT() bool { func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) - method, err := shadowimpl.FetchMethod(option.Cipher, option.Password) + method, err := shadowimpl.FetchMethod(option.Cipher, option.Password, time.Now) if err != nil { return nil, fmt.Errorf("ss %s initialize error: %w", addr, err) } diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 2ae72069..25ffebf3 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -11,6 +11,7 @@ import ( "strings" "sync" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" tlsC "github.com/Dreamacro/clash/component/tls" @@ -213,12 +214,24 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { } if metadata.NetWork == C.UDP { if v.option.XUDP { - return v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil + if N.NeedHandshake(c) { + return v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil + } else { + return v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + } } else { - return v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil + if N.NeedHandshake(c) { + return v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil + } else { + return v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + } } } else { - return v.client.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil + if N.NeedHandshake(c) { + return v.client.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil + } else { + return v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + } } } @@ -289,9 +302,17 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o }(c) if v.option.XUDP { - c = v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + if N.NeedHandshake(c) { + c = v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + } else { + c, err = v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + } } else { - c = v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + if N.NeedHandshake(c) { + c = v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + } else { + c, err = v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + } } if err != nil { diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index 066e8a37..d1d5e6b3 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -8,6 +8,7 @@ import ( "github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/common/callback" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/constant/provider" @@ -35,15 +36,17 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts . f.onDialFailed(proxy.Type(), err) } - c = &callback.FirstWriteCallBackConn{ - Conn: c, - Callback: func(err error) { - if err == nil { - f.onDialSuccess() - } else { - f.onDialFailed(proxy.Type(), err) - } - }, + if N.NeedHandshake(c) { + c = &callback.FirstWriteCallBackConn{ + Conn: c, + Callback: func(err error) { + if err == nil { + f.onDialSuccess() + } else { + f.onDialFailed(proxy.Type(), err) + } + }, + } } return c, err diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 9a010cf9..b89dba93 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -12,6 +12,7 @@ import ( "github.com/Dreamacro/clash/common/cache" "github.com/Dreamacro/clash/common/callback" "github.com/Dreamacro/clash/common/murmur3" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/constant/provider" @@ -92,16 +93,19 @@ func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, op lb.onDialFailed(proxy.Type(), err) } - c = &callback.FirstWriteCallBackConn{ - Conn: c, - Callback: func(err error) { - if err == nil { - lb.onDialSuccess() - } else { - lb.onDialFailed(proxy.Type(), err) - } - }, + if N.NeedHandshake(c) { + c = &callback.FirstWriteCallBackConn{ + Conn: c, + Callback: func(err error) { + if err == nil { + lb.onDialSuccess() + } else { + lb.onDialFailed(proxy.Type(), err) + } + }, + } } + return } diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index 31eaf4a4..d340539c 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -7,6 +7,7 @@ import ( "github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/common/callback" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/singledo" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" @@ -43,16 +44,19 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts .. u.onDialFailed(proxy.Type(), err) } - c = &callback.FirstWriteCallBackConn{ - Conn: c, - Callback: func(err error) { - if err == nil { - u.onDialSuccess() - } else { - u.onDialFailed(proxy.Type(), err) - } - }, + if N.NeedHandshake(c) { + c = &callback.FirstWriteCallBackConn{ + Conn: c, + Callback: func(err error) { + if err == nil { + u.onDialSuccess() + } else { + u.onDialFailed(proxy.Type(), err) + } + }, + } } + return c, err } diff --git a/common/convert/util.go b/common/convert/util.go index 03a48ecd..282d52d5 100644 --- a/common/convert/util.go +++ b/common/convert/util.go @@ -6,6 +6,7 @@ import ( "math/rand" "net/http" "strings" + "time" "github.com/gofrs/uuid" ) @@ -317,6 +318,6 @@ func SetUserAgent(header http.Header) { } func VerifyMethod(cipher, password string) (err error) { - _, err = shadowimpl.FetchMethod(cipher, password) + _, err = shadowimpl.FetchMethod(cipher, password, time.Now) return } diff --git a/common/net/sing.go b/common/net/sing.go index 342f2e95..5c980738 100644 --- a/common/net/sing.go +++ b/common/net/sing.go @@ -4,6 +4,7 @@ import ( "context" "net" + "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/network" ) @@ -16,6 +17,13 @@ type ExtendedConn = network.ExtendedConn type ExtendedWriter = network.ExtendedWriter type ExtendedReader = network.ExtendedReader +func NeedHandshake(conn any) bool { + if earlyConn, isEarlyConn := common.Cast[network.EarlyConn](conn); isEarlyConn && earlyConn.NeedHandshake() { + return true + } + return false +} + // Relay copies between left and right bidirectionally. func Relay(leftConn, rightConn net.Conn) { _ = bufio.CopyConn(context.TODO(), leftConn, rightConn) diff --git a/component/dialer/tfo.go b/component/dialer/tfo.go index 2db2e91e..0041a976 100644 --- a/component/dialer/tfo.go +++ b/component/dialer/tfo.go @@ -105,6 +105,10 @@ func (c *tfoConn) Upstream() any { return c.Conn } +func (c *tfoConn) NeedHandshake() bool { + return c.Conn == nil +} + func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) { ctx, cancel := context.WithCancel(ctx) dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false} diff --git a/go.mod b/go.mod index 401affac..f9beea8f 100644 --- a/go.mod +++ b/go.mod @@ -19,16 +19,16 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 github.com/metacubex/quic-go v0.32.0 - github.com/metacubex/sing-shadowsocks v0.1.1-0.20230202072246-e2bef5f088c7 + github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 github.com/metacubex/sing-tun v0.1.1-0.20230222113101-fbfa2dab826d github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4 github.com/miekg/dns v1.1.50 github.com/mroth/weightedrand/v2 v2.0.0 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.1.8-0.20230226133421-e83948367009 + github.com/sagernet/sing v0.1.8-0.20230226150041-83d9121b04c6 github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e - github.com/sagernet/sing-vmess v0.1.2 + github.com/sagernet/sing-vmess v0.1.3-0.20230226144228-40c1abdb85be github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d github.com/sagernet/utls v0.0.0-20230220130002-c08891932056 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c diff --git a/go.sum b/go.sum index 79cbc722..1875b574 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65 h1:WUINdCB/UvSX9I github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65/go.mod h1:e3lCxh3TozKMWAsYTC7nBVnepAxPL/sNyScUFvmEoec= github.com/metacubex/quic-go v0.32.0 h1:dSD8LB4MSeBuD4otd8y1DUZcRdDcEB0Ax5esPOqn2Hw= github.com/metacubex/quic-go v0.32.0/go.mod h1:yParIzDYUd/t/pzFlDtZKhnvSqbUu0bPChlKEGmJStA= -github.com/metacubex/sing-shadowsocks v0.1.1-0.20230202072246-e2bef5f088c7 h1:MNCGIpXhxXn9ck5bxfm/cW9Nr2FGQ5cakcGK0yKZcak= -github.com/metacubex/sing-shadowsocks v0.1.1-0.20230202072246-e2bef5f088c7/go.mod h1:8pBSYDKVxTtqUtGZyEh4ZpFJXwP6wBVVKrs6oQiOwmQ= +github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 h1:NnjC2+aIiyzzvFlo+C2WzBOJdsp+HAtu18FZomqYhUE= +github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947/go.mod h1:U2gwhxzqgbhKCgn2B4z3t0Cj0LpMWFl/02BGCoG421w= github.com/metacubex/sing-tun v0.1.1-0.20230222113101-fbfa2dab826d h1:oMzkrEoBdwn2/Vyu0n6/LAmvjxqsyFs+f2kqeg7kI8U= github.com/metacubex/sing-tun v0.1.1-0.20230222113101-fbfa2dab826d/go.mod h1:WmbtxVPpJulKoQGwfnBMk4KSWzZ68sE/myTrQeN/5GE= github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4 h1:d96mCF/LYyC9kULd2xwcXfP0Jd8klrOngmRxuUIZg/8= @@ -127,12 +127,12 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.1.8-0.20230226133421-e83948367009 h1:KjrXGv09UlBl3Rj57XInk6u2TAxqpPfOJ2kUgV5B2lw= -github.com/sagernet/sing v0.1.8-0.20230226133421-e83948367009/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= +github.com/sagernet/sing v0.1.8-0.20230226150041-83d9121b04c6 h1:enq0WDXPUI2QyUUypRd2uiScoWKcs8jo+yB+K5hrdc8= +github.com/sagernet/sing v0.1.8-0.20230226150041-83d9121b04c6/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e h1:S1fd0kB9aEU68dd269AQy783sUlFu/2fSh/4YYVJ/Oc= github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= -github.com/sagernet/sing-vmess v0.1.2 h1:RbOZNAId2LrCai8epMoQXlf0XTrou0bfcw08hNBg6lM= -github.com/sagernet/sing-vmess v0.1.2/go.mod h1:9NSj8mZTx1JIY/HF9LaYRppUsVkysIN5tEFpNZujXxY= +github.com/sagernet/sing-vmess v0.1.3-0.20230226144228-40c1abdb85be h1:FoaWiy89hyRkP/KilUoGn6W/seyVBDZcdsYcJQEx5Hk= +github.com/sagernet/sing-vmess v0.1.3-0.20230226144228-40c1abdb85be/go.mod h1:9NSj8mZTx1JIY/HF9LaYRppUsVkysIN5tEFpNZujXxY= github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d h1:trP/l6ZPWvQ/5Gv99Z7/t/v8iYy06akDMejxW1sznUk= github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d/go.mod h1:jk6Ii8Y3En+j2KQDLgdgQGwb3M6y7EL567jFnGYhN9g= github.com/sagernet/utls v0.0.0-20230220130002-c08891932056 h1:gDXi/0uYe8dA48UyUI1LM2la5QYN0IvsDvR2H2+kFnA= diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index 305a9496..5602a3ed 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "strings" + "time" "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/sockopt" @@ -63,7 +64,7 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C case common.Contains(shadowaead.List, config.Cipher): sl.service, err = shadowaead.NewService(config.Cipher, nil, config.Password, udpTimeout, h) case common.Contains(shadowaead_2022.List, config.Cipher): - sl.service, err = shadowaead_2022.NewServiceWithPassword(config.Cipher, config.Password, udpTimeout, h) + sl.service, err = shadowaead_2022.NewServiceWithPassword(config.Cipher, config.Password, udpTimeout, h, time.Now) default: err = fmt.Errorf("shadowsocks: unsupported method: %s", config.Cipher) return embedSS.New(config, tcpIn, udpIn) diff --git a/transport/vless/conn.go b/transport/vless/conn.go index 5817083d..4ee34405 100644 --- a/transport/vless/conn.go +++ b/transport/vless/conn.go @@ -430,6 +430,15 @@ func (vc *Conn) Upstream() any { return vc.tlsConn } +func (vc *Conn) NeedHandshake() bool { + select { + case <-vc.handshake: + return true + default: + } + return false +} + func (vc *Conn) IsXTLSVisionEnabled() bool { return vc.addons != nil && vc.addons.Flow == XRV } diff --git a/transport/vmess/websocket.go b/transport/vmess/websocket.go index 71dabbdd..dfadb61a 100644 --- a/transport/vmess/websocket.go +++ b/transport/vmess/websocket.go @@ -301,15 +301,27 @@ func (wsedc *websocketWithEarlyDataConn) SetWriteDeadline(t time.Time) error { return wsedc.Conn.SetWriteDeadline(t) } -func (wsedc *websocketWithEarlyDataConn) LazyHeadroom() bool { - return wsedc.Conn == nil +func (wsedc *websocketWithEarlyDataConn) FrontHeadroom() int { + return 14 } func (wsedc *websocketWithEarlyDataConn) Upstream() any { - if wsedc.Conn == nil { // ensure return a nil interface not an interface with nil value - return nil - } - return wsedc.Conn + return wsedc.underlay +} + +//func (wsedc *websocketWithEarlyDataConn) LazyHeadroom() bool { +// return wsedc.Conn == nil +//} +// +//func (wsedc *websocketWithEarlyDataConn) Upstream() any { +// if wsedc.Conn == nil { // ensure return a nil interface not an interface with nil value +// return nil +// } +// return wsedc.Conn +//} + +func (wsedc *websocketWithEarlyDataConn) NeedHandshake() bool { + return wsedc.Conn == nil } func streamWebsocketWithEarlyDataConn(conn net.Conn, c *WebsocketConfig) (net.Conn, error) { diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index c52400e8..9e9db82b 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -13,6 +13,7 @@ import ( "github.com/jpillora/backoff" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/nat" P "github.com/Dreamacro/clash/component/process" "github.com/Dreamacro/clash/component/resolver" @@ -367,7 +368,7 @@ func handleTCPConn(connCtx C.ConnContext) { } conn := connCtx.Conn() - conn.ResetPeeked() + conn.ResetPeeked() // reset before sniffer if sniffer.Dispatcher.Enable() && sniffingEnable { sniffer.Dispatcher.TCPSniff(conn, metadata) } @@ -415,15 +416,17 @@ func handleTCPConn(connCtx C.ConnContext) { return remoteConn, nil } } - peekMutex.Lock() - defer peekMutex.Unlock() - peekBytes, _ = conn.Peek(conn.Buffered()) - _, err = remoteConn.Write(peekBytes) - if err != nil { - return nil, err - } - if peekLen = len(peekBytes); peekLen > 0 { - _, _ = conn.Discard(peekLen) + if N.NeedHandshake(remoteConn) { + peekMutex.Lock() + defer peekMutex.Unlock() + peekBytes, _ = conn.Peek(conn.Buffered()) + _, err = remoteConn.Write(peekBytes) + if err != nil { + return nil, err + } + if peekLen = len(peekBytes); peekLen > 0 { + _, _ = conn.Discard(peekLen) + } } return remoteConn, err }, func(err error) { @@ -469,8 +472,10 @@ func handleTCPConn(connCtx C.ConnContext) { ) } + _ = conn.SetReadDeadline(time.Now()) // stop unfinished peek peekMutex.Lock() defer peekMutex.Unlock() + _ = conn.SetReadDeadline(time.Time{}) // reset handleSocket(connCtx, remoteConn) }