chore: add early conn interface to decrease unneeded write

This commit is contained in:
wwqgtxx 2023-02-27 00:26:49 +08:00
parent c8c078e78a
commit c1199f1a8a
14 changed files with 144 additions and 62 deletions

View File

@ -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)
}

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)

View File

@ -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}

6
go.mod
View File

@ -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

12
go.sum
View File

@ -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=

View File

@ -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)

View File

@ -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
}

View File

@ -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) {

View File

@ -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)
}